]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/Player.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / Player.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code 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 Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34 /*
35 ===============================================================================
36
37         Player control of the Doom Marine.
38         This object handles all player movement and world interaction.
39
40 ===============================================================================
41 */
42
43 // distance between ladder rungs (actually is half that distance, but this sounds better)
44 const int LADDER_RUNG_DISTANCE = 32;
45
46 // amount of health per dose from the health station
47 const int HEALTH_PER_DOSE = 10;
48
49 // time before a weapon dropped to the floor disappears
50 const int WEAPON_DROP_TIME = 20 * 1000;
51
52 // time before a next or prev weapon switch happens
53 const int WEAPON_SWITCH_DELAY = 150;
54
55 // how many units to raise spectator above default view height so it's in the head of someone
56 const int SPECTATE_RAISE = 25;
57
58 const int HEALTHPULSE_TIME = 333;
59
60 // minimum speed to bob and play run/walk animations at
61 const float MIN_BOB_SPEED = 5.0f;
62
63 const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
64 const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
65 const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
66 const idEventDef EV_Player_StopFxFov( "stopFxFov" );
67 const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
68 const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
69 const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
70 const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
71 const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
72 const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
73 const idEventDef EV_Player_OpenPDA( "openPDA" );
74 const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
75 const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
76 const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
77 const idEventDef EV_Player_HideTip( "hideTip" );
78 const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
79 const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
80 #ifdef _D3XP
81 const idEventDef EV_Player_GiveInventoryItem( "giveInventoryItem", "s" );
82 const idEventDef EV_Player_RemoveInventoryItem( "removeInventoryItem", "s" );
83 const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
84 const idEventDef EV_Player_SetPowerupTime( "setPowerupTime", "dd" );
85 const idEventDef EV_Player_IsPowerupActive( "isPowerupActive", "d", 'd' );
86 const idEventDef EV_Player_WeaponAvailable( "weaponAvailable", "s", 'd');
87 const idEventDef EV_Player_StartWarp( "startWarp" );
88 const idEventDef EV_Player_StopHelltime( "stopHelltime", "d" );
89 const idEventDef EV_Player_ToggleBloom( "toggleBloom", "d" );
90 const idEventDef EV_Player_SetBloomParms( "setBloomParms", "ff" );
91 #endif
92
93 CLASS_DECLARATION( idActor, idPlayer )
94         EVENT( EV_Player_GetButtons,                    idPlayer::Event_GetButtons )
95         EVENT( EV_Player_GetMove,                               idPlayer::Event_GetMove )
96         EVENT( EV_Player_GetViewAngles,                 idPlayer::Event_GetViewAngles )
97         EVENT( EV_Player_StopFxFov,                             idPlayer::Event_StopFxFov )
98         EVENT( EV_Player_EnableWeapon,                  idPlayer::Event_EnableWeapon )
99         EVENT( EV_Player_DisableWeapon,                 idPlayer::Event_DisableWeapon )
100         EVENT( EV_Player_GetCurrentWeapon,              idPlayer::Event_GetCurrentWeapon )
101         EVENT( EV_Player_GetPreviousWeapon,             idPlayer::Event_GetPreviousWeapon )
102         EVENT( EV_Player_SelectWeapon,                  idPlayer::Event_SelectWeapon )
103         EVENT( EV_Player_GetWeaponEntity,               idPlayer::Event_GetWeaponEntity )
104         EVENT( EV_Player_OpenPDA,                               idPlayer::Event_OpenPDA )
105         EVENT( EV_Player_InPDA,                                 idPlayer::Event_InPDA )
106         EVENT( EV_Player_ExitTeleporter,                idPlayer::Event_ExitTeleporter )
107         EVENT( EV_Player_StopAudioLog,                  idPlayer::Event_StopAudioLog )
108         EVENT( EV_Player_HideTip,                               idPlayer::Event_HideTip )
109         EVENT( EV_Player_LevelTrigger,                  idPlayer::Event_LevelTrigger )
110         EVENT( EV_Gibbed,                                               idPlayer::Event_Gibbed )
111 #ifdef _D3XP
112         EVENT( EV_Player_GiveInventoryItem,             idPlayer::Event_GiveInventoryItem )
113         EVENT( EV_Player_RemoveInventoryItem,   idPlayer::Event_RemoveInventoryItem )
114         EVENT( EV_Player_GetIdealWeapon,                idPlayer::Event_GetIdealWeapon )
115         EVENT( EV_Player_WeaponAvailable,               idPlayer::Event_WeaponAvailable )
116         EVENT( EV_Player_SetPowerupTime,                idPlayer::Event_SetPowerupTime )
117         EVENT( EV_Player_IsPowerupActive,               idPlayer::Event_IsPowerupActive )
118         EVENT( EV_Player_StartWarp,                             idPlayer::Event_StartWarp )
119         EVENT( EV_Player_StopHelltime,                  idPlayer::Event_StopHelltime )
120         EVENT( EV_Player_ToggleBloom,                   idPlayer::Event_ToggleBloom )
121         EVENT( EV_Player_SetBloomParms,                 idPlayer::Event_SetBloomParms )
122 #endif
123 END_CLASS
124
125 const int MAX_RESPAWN_TIME = 10000;
126 const int RAGDOLL_DEATH_TIME = 3000;
127 const int MAX_PDAS = 64;
128 const int MAX_PDA_ITEMS = 128;
129 const int STEPUP_TIME = 200;
130 const int MAX_INVENTORY_ITEMS = 20;
131
132
133 #ifdef _D3XP
134 idVec3 idPlayer::colorBarTable[ 8 ] = { 
135 #else
136 idVec3 idPlayer::colorBarTable[ 5 ] = { 
137 #endif
138         idVec3( 0.25f, 0.25f, 0.25f ),
139         idVec3( 1.00f, 0.00f, 0.00f ),
140         idVec3( 0.00f, 0.80f, 0.10f ),
141         idVec3( 0.20f, 0.50f, 0.80f ),
142         idVec3( 1.00f, 0.80f, 0.10f )
143 #ifdef _D3XP
144         ,idVec3( 0.425f, 0.484f, 0.445f ),
145         idVec3( 0.39f, 0.199f, 0.3f ),
146         idVec3( 0.484f, 0.312f, 0.074f)
147 #endif
148 };
149
150 /*
151 ==============
152 idInventory::Clear
153 ==============
154 */
155 void idInventory::Clear( void ) {
156         maxHealth               = 0;
157         weapons                 = 0;
158         powerups                = 0;
159         armor                   = 0;
160         maxarmor                = 0;
161         deplete_armor   = 0;
162         deplete_rate    = 0.0f;
163         deplete_ammount = 0;
164         nextArmorDepleteTime = 0;
165
166         memset( ammo, 0, sizeof( ammo ) );
167
168         ClearPowerUps();
169
170         // set to -1 so that the gun knows to have a full clip the first time we get it and at the start of the level
171         memset( clip, -1, sizeof( clip ) );
172         
173         items.DeleteContents( true );
174         memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
175         pdas.Clear();
176         videos.Clear();
177         emails.Clear();
178         selVideo = 0;
179         selEMail = 0;
180         selPDA = 0;
181         selAudio = 0;
182         pdaOpened = false;
183         turkeyScore = false;
184
185         levelTriggers.Clear();
186
187         nextItemPickup = 0;
188         nextItemNum = 1;
189         onePickupTime = 0;
190         pickupItemNames.Clear();
191         objectiveNames.Clear();
192
193         ammoPredictTime = 0;
194
195         lastGiveTime = 0;
196
197         ammoPulse       = false;
198         weaponPulse     = false;
199         armorPulse      = false;
200 }
201
202 /*
203 ==============
204 idInventory::GivePowerUp
205 ==============
206 */
207 void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
208         if ( !msec ) {
209                 // get the duration from the .def files
210                 const idDeclEntityDef *def = NULL;
211                 switch ( powerup ) {
212                         case BERSERK:
213                                 def = gameLocal.FindEntityDef( "powerup_berserk", false );
214                                 break;
215                         case INVISIBILITY:
216                                 def = gameLocal.FindEntityDef( "powerup_invisibility", false );
217                                 break;
218                         case MEGAHEALTH:
219                                 def = gameLocal.FindEntityDef( "powerup_megahealth", false );
220                                 break;
221                         case ADRENALINE: 
222                                 def = gameLocal.FindEntityDef( "powerup_adrenaline", false );
223                                 break;
224 #ifdef _D3XP
225                         case INVULNERABILITY:
226                                 def = gameLocal.FindEntityDef( "powerup_invulnerability", false );
227                                 break;
228                         /*case HASTE:
229                                 def = gameLocal.FindEntityDef( "powerup_haste", false );
230                                 break;*/
231 #endif
232                 }
233                 assert( def );
234                 msec = def->dict.GetInt( "time" ) * 1000;
235         }
236         powerups |= 1 << powerup;
237         powerupEndTime[ powerup ] = gameLocal.time + msec;
238 }
239
240 /*
241 ==============
242 idInventory::ClearPowerUps
243 ==============
244 */
245 void idInventory::ClearPowerUps( void ) {
246         int i;
247         for ( i = 0; i < MAX_POWERUPS; i++ ) {
248                 powerupEndTime[ i ] = 0;
249         }
250         powerups = 0;
251 }
252
253 /*
254 ==============
255 idInventory::GetPersistantData
256 ==============
257 */
258 void idInventory::GetPersistantData( idDict &dict ) {
259         int             i;
260         int             num;
261         idDict  *item;
262         idStr   key;
263         const idKeyValue *kv;
264         const char *name;
265
266         // armor
267         dict.SetInt( "armor", armor );
268
269     // don't bother with powerups, maxhealth, maxarmor, or the clip
270
271         // ammo
272         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
273                 name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
274                 if ( name ) {
275                         dict.SetInt( name, ammo[ i ] );
276                 }
277         }
278
279 #ifdef _D3XP
280         //Save the clip data
281         for( i = 0; i < MAX_WEAPONS; i++ ) {
282                 dict.SetInt( va("clip%i", i), clip[ i ] );
283         }
284 #endif
285
286         // items
287         num = 0;
288         for( i = 0; i < items.Num(); i++ ) {
289                 item = items[ i ];
290
291                 // copy all keys with "inv_"
292                 kv = item->MatchPrefix( "inv_" );
293                 if ( kv ) {
294                         while( kv ) {
295                                 sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
296                                 dict.Set( key, kv->GetValue() );
297                                 kv = item->MatchPrefix( "inv_", kv );
298                         }
299                         num++;
300                 }
301         }
302         dict.SetInt( "items", num );
303
304         // pdas viewed
305         for ( i = 0; i < 4; i++ ) {
306                 dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
307         }
308
309         dict.SetInt( "selPDA", selPDA );
310         dict.SetInt( "selVideo", selVideo );
311         dict.SetInt( "selEmail", selEMail );
312         dict.SetInt( "selAudio", selAudio );
313         dict.SetInt( "pdaOpened", pdaOpened );
314         dict.SetInt( "turkeyScore", turkeyScore );
315
316         // pdas
317         for ( i = 0; i < pdas.Num(); i++ ) {
318                 sprintf( key, "pda_%i", i );
319                 dict.Set( key, pdas[ i ] );
320         }
321         dict.SetInt( "pdas", pdas.Num() );
322
323         // video cds
324         for ( i = 0; i < videos.Num(); i++ ) {
325                 sprintf( key, "video_%i", i );
326                 dict.Set( key, videos[ i ].c_str() );
327         }
328         dict.SetInt( "videos", videos.Num() );
329
330         // emails
331         for ( i = 0; i < emails.Num(); i++ ) {
332                 sprintf( key, "email_%i", i );
333                 dict.Set( key, emails[ i ].c_str() );
334         }
335         dict.SetInt( "emails", emails.Num() );
336
337         // weapons
338         dict.SetInt( "weapon_bits", weapons );
339
340         dict.SetInt( "levelTriggers", levelTriggers.Num() );
341         for ( i = 0; i < levelTriggers.Num(); i++ ) {
342                 sprintf( key, "levelTrigger_Level_%i", i );
343                 dict.Set( key, levelTriggers[i].levelName );
344                 sprintf( key, "levelTrigger_Trigger_%i", i );
345                 dict.Set( key, levelTriggers[i].triggerName );
346         }
347 }
348
349 /*
350 ==============
351 idInventory::RestoreInventory
352 ==============
353 */
354 void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
355         int                     i;
356         int                     num;
357         idDict          *item;
358         idStr           key;
359         idStr           itemname;
360         const idKeyValue *kv;
361         const char      *name;
362
363         Clear();
364
365         // health/armor
366         maxHealth               = dict.GetInt( "maxhealth", "100" );
367         armor                   = dict.GetInt( "armor", "50" );
368         maxarmor                = dict.GetInt( "maxarmor", "100" );
369         deplete_armor   = dict.GetInt( "deplete_armor", "0" );
370         deplete_rate    = dict.GetFloat( "deplete_rate", "2.0" );
371         deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
372
373         // the clip and powerups aren't restored
374
375         // ammo
376         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
377                 name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
378                 if ( name ) {
379                         ammo[ i ] = dict.GetInt( name );
380                 }
381         }
382
383 #ifdef _D3XP
384         //Restore the clip data
385         for( i = 0; i < MAX_WEAPONS; i++ ) {
386                 clip[i] = dict.GetInt(va("clip%i", i), "-1");
387         }
388 #endif
389
390         // items
391         num = dict.GetInt( "items" );
392         items.SetNum( num );
393         for( i = 0; i < num; i++ ) {
394                 item = new idDict();
395                 items[ i ] = item;
396                 sprintf( itemname, "item_%i ", i );
397                 kv = dict.MatchPrefix( itemname );
398                 while( kv ) {
399                         key = kv->GetKey();
400                         key.Strip( itemname );
401                         item->Set( key, kv->GetValue() );
402                         kv = dict.MatchPrefix( itemname, kv );
403                 }
404         }
405
406         // pdas viewed
407         for ( i = 0; i < 4; i++ ) {
408                 pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
409         }
410
411         selPDA = dict.GetInt( "selPDA" );
412         selEMail = dict.GetInt( "selEmail" );
413         selVideo = dict.GetInt( "selVideo" );
414         selAudio = dict.GetInt( "selAudio" );
415         pdaOpened = dict.GetBool( "pdaOpened" );
416         turkeyScore = dict.GetBool( "turkeyScore" );
417
418         // pdas
419         num = dict.GetInt( "pdas" );
420         pdas.SetNum( num );
421         for ( i = 0; i < num; i++ ) {
422                 sprintf( itemname, "pda_%i", i );
423                 pdas[i] = dict.GetString( itemname, "default" );
424         }
425
426         // videos
427         num = dict.GetInt( "videos" );
428         videos.SetNum( num );
429         for ( i = 0; i < num; i++ ) {
430                 sprintf( itemname, "video_%i", i );
431                 videos[i] = dict.GetString( itemname, "default" );
432         }
433
434         // emails
435         num = dict.GetInt( "emails" );
436         emails.SetNum( num );
437         for ( i = 0; i < num; i++ ) {
438                 sprintf( itemname, "email_%i", i );
439                 emails[i] = dict.GetString( itemname, "default" );
440         }
441
442         // weapons are stored as a number for persistant data, but as strings in the entityDef
443         weapons = dict.GetInt( "weapon_bits", "0" );
444
445 #ifdef ID_DEMO_BUILD
446                 Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
447 #else
448         if ( g_skill.GetInteger() >= 3 ) {
449                 Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false );
450         } else {
451                 Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
452         }
453 #endif
454
455         num = dict.GetInt( "levelTriggers" );
456         for ( i = 0; i < num; i++ ) {
457                 sprintf( itemname, "levelTrigger_Level_%i", i );
458                 idLevelTriggerInfo lti;
459                 lti.levelName = dict.GetString( itemname );
460                 sprintf( itemname, "levelTrigger_Trigger_%i", i );
461                 lti.triggerName = dict.GetString( itemname );
462                 levelTriggers.Append( lti );
463         }
464
465 }
466
467 /*
468 ==============
469 idInventory::Save
470 ==============
471 */
472 void idInventory::Save( idSaveGame *savefile ) const {
473         int i;
474
475         savefile->WriteInt( maxHealth );
476         savefile->WriteInt( weapons );
477         savefile->WriteInt( powerups );
478         savefile->WriteInt( armor );
479         savefile->WriteInt( maxarmor );
480         savefile->WriteInt( ammoPredictTime );
481         savefile->WriteInt( deplete_armor );
482         savefile->WriteFloat( deplete_rate );
483         savefile->WriteInt( deplete_ammount );
484         savefile->WriteInt( nextArmorDepleteTime );
485
486         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
487                 savefile->WriteInt( ammo[ i ] );
488         }
489         for( i = 0; i < MAX_WEAPONS; i++ ) {
490                 savefile->WriteInt( clip[ i ] );
491         }
492         for( i = 0; i < MAX_POWERUPS; i++ ) {
493                 savefile->WriteInt( powerupEndTime[ i ] );
494         }
495
496         savefile->WriteInt( items.Num() );
497         for( i = 0; i < items.Num(); i++ ) {
498                 savefile->WriteDict( items[ i ] );
499         }
500
501         savefile->WriteInt( pdasViewed[0] );
502         savefile->WriteInt( pdasViewed[1] );
503         savefile->WriteInt( pdasViewed[2] );
504         savefile->WriteInt( pdasViewed[3] );
505         
506         savefile->WriteInt( selPDA );
507         savefile->WriteInt( selVideo );
508         savefile->WriteInt( selEMail );
509         savefile->WriteInt( selAudio );
510         savefile->WriteBool( pdaOpened );
511         savefile->WriteBool( turkeyScore );
512
513         savefile->WriteInt( pdas.Num() );
514         for( i = 0; i < pdas.Num(); i++ ) {
515                 savefile->WriteString( pdas[ i ] );
516         }
517
518         savefile->WriteInt( pdaSecurity.Num() );
519         for( i=0; i < pdaSecurity.Num(); i++ ) {
520                 savefile->WriteString( pdaSecurity[ i ] );
521         }
522
523         savefile->WriteInt( videos.Num() );
524         for( i = 0; i < videos.Num(); i++ ) {
525                 savefile->WriteString( videos[ i ] );
526         }
527
528         savefile->WriteInt( emails.Num() );
529         for ( i = 0; i < emails.Num(); i++ ) {
530                 savefile->WriteString( emails[ i ] );
531         }
532
533         savefile->WriteInt( nextItemPickup );
534         savefile->WriteInt( nextItemNum );
535         savefile->WriteInt( onePickupTime );
536
537         savefile->WriteInt( pickupItemNames.Num() );
538         for( i = 0; i < pickupItemNames.Num(); i++ ) {
539                 savefile->WriteString( pickupItemNames[i].icon );
540                 savefile->WriteString( pickupItemNames[i].name );
541         }
542
543         savefile->WriteInt( objectiveNames.Num() );
544         for( i = 0; i < objectiveNames.Num(); i++ ) {
545                 savefile->WriteString( objectiveNames[i].screenshot );
546                 savefile->WriteString( objectiveNames[i].text );
547                 savefile->WriteString( objectiveNames[i].title );
548         }
549
550         savefile->WriteInt( levelTriggers.Num() );
551         for ( i = 0; i < levelTriggers.Num(); i++ ) {
552                 savefile->WriteString( levelTriggers[i].levelName );
553                 savefile->WriteString( levelTriggers[i].triggerName );
554         }
555
556         savefile->WriteBool( ammoPulse );
557         savefile->WriteBool( weaponPulse );
558         savefile->WriteBool( armorPulse );
559
560         savefile->WriteInt( lastGiveTime );
561
562 #ifdef _D3XP
563         for(i = 0; i < AMMO_NUMTYPES; i++) {
564                 savefile->WriteInt(rechargeAmmo[i].ammo);
565                 savefile->WriteInt(rechargeAmmo[i].rechargeTime);
566                 savefile->WriteString(rechargeAmmo[i].ammoName);
567         }
568 #endif
569 }
570
571 /*
572 ==============
573 idInventory::Restore
574 ==============
575 */
576 void idInventory::Restore( idRestoreGame *savefile ) {
577         int i, num;
578
579         savefile->ReadInt( maxHealth );
580         savefile->ReadInt( weapons );
581         savefile->ReadInt( powerups );
582         savefile->ReadInt( armor );
583         savefile->ReadInt( maxarmor );
584         savefile->ReadInt( ammoPredictTime );
585         savefile->ReadInt( deplete_armor );
586         savefile->ReadFloat( deplete_rate );
587         savefile->ReadInt( deplete_ammount );
588         savefile->ReadInt( nextArmorDepleteTime );
589
590         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
591                 savefile->ReadInt( ammo[ i ] );
592         }
593         for( i = 0; i < MAX_WEAPONS; i++ ) {
594                 savefile->ReadInt( clip[ i ] );
595         }
596         for( i = 0; i < MAX_POWERUPS; i++ ) {
597                 savefile->ReadInt( powerupEndTime[ i ] );
598         }
599
600         savefile->ReadInt( num );
601         for( i = 0; i < num; i++ ) {
602                 idDict *itemdict = new idDict;
603
604                 savefile->ReadDict( itemdict );
605                 items.Append( itemdict );
606         }
607
608         // pdas
609         savefile->ReadInt( pdasViewed[0] );
610         savefile->ReadInt( pdasViewed[1] );
611         savefile->ReadInt( pdasViewed[2] );
612         savefile->ReadInt( pdasViewed[3] );
613         
614         savefile->ReadInt( selPDA );
615         savefile->ReadInt( selVideo );
616         savefile->ReadInt( selEMail );
617         savefile->ReadInt( selAudio );
618         savefile->ReadBool( pdaOpened );
619         savefile->ReadBool( turkeyScore );
620
621         savefile->ReadInt( num );
622         for( i = 0; i < num; i++ ) {
623                 idStr strPda;
624                 savefile->ReadString( strPda );
625                 pdas.Append( strPda );
626         }
627
628         // pda security clearances
629         savefile->ReadInt( num );
630         for ( i = 0; i < num; i++ ) {
631                 idStr invName;
632                 savefile->ReadString( invName );
633                 pdaSecurity.Append( invName );
634         }
635
636         // videos
637         savefile->ReadInt( num );
638         for( i = 0; i < num; i++ ) {
639                 idStr strVideo;
640                 savefile->ReadString( strVideo );
641                 videos.Append( strVideo );
642         }
643
644         // email
645         savefile->ReadInt( num );
646         for( i = 0; i < num; i++ ) {
647                 idStr strEmail;
648                 savefile->ReadString( strEmail );
649                 emails.Append( strEmail );
650         }
651
652         savefile->ReadInt( nextItemPickup );
653         savefile->ReadInt( nextItemNum );
654         savefile->ReadInt( onePickupTime );
655         savefile->ReadInt( num );
656         for( i = 0; i < num; i++ ) {
657                 idItemInfo info;
658
659                 savefile->ReadString( info.icon );
660                 savefile->ReadString( info.name );
661
662                 pickupItemNames.Append( info );
663         }
664
665         savefile->ReadInt( num );
666         for( i = 0; i < num; i++ ) {
667                 idObjectiveInfo obj;
668
669                 savefile->ReadString( obj.screenshot );
670                 savefile->ReadString( obj.text );
671                 savefile->ReadString( obj.title );
672
673                 objectiveNames.Append( obj );
674         }
675
676         savefile->ReadInt( num );
677         for ( i = 0; i < num; i++ ) {
678                 idLevelTriggerInfo lti;
679                 savefile->ReadString( lti.levelName );
680                 savefile->ReadString( lti.triggerName );
681                 levelTriggers.Append( lti );
682         }
683
684         savefile->ReadBool( ammoPulse );
685         savefile->ReadBool( weaponPulse );
686         savefile->ReadBool( armorPulse );
687
688         savefile->ReadInt( lastGiveTime );
689
690 #ifdef _D3XP
691         for(i = 0; i < AMMO_NUMTYPES; i++) {
692                 savefile->ReadInt(rechargeAmmo[i].ammo);
693                 savefile->ReadInt(rechargeAmmo[i].rechargeTime);
694                 
695                 idStr name;
696                 savefile->ReadString(name);
697                 strcpy(rechargeAmmo[i].ammoName, name);
698         }
699 #endif
700 }
701
702 /*
703 ==============
704 idInventory::AmmoIndexForAmmoClass
705 ==============
706 */
707 ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
708         return idWeapon::GetAmmoNumForName( ammo_classname );
709 }
710
711 /*
712 ==============
713 idInventory::AmmoIndexForAmmoClass
714 ==============
715 */
716 int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
717         return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
718 }
719
720 /*
721 ==============
722 idInventory::AmmoPickupNameForIndex
723 ==============
724 */
725 const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
726         return idWeapon::GetAmmoPickupNameForNum( ammonum );
727 }
728
729 /*
730 ==============
731 idInventory::WeaponIndexForAmmoClass
732 mapping could be prepared in the constructor
733 ==============
734 */
735 int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
736         int i;
737         const char *weapon_classname;
738         for( i = 0; i < MAX_WEAPONS; i++ ) {
739                 weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
740                 if ( !weapon_classname ) {
741                         continue;
742                 }
743                 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
744                 if ( !decl ) {
745                         continue;
746                 }
747                 if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
748                         return i;
749                 }
750         }
751         return -1;
752 }
753
754 /*
755 ==============
756 idInventory::AmmoIndexForWeaponClass
757 ==============
758 */
759 ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
760         const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
761         if ( !decl ) {
762                 gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
763         }
764         if ( ammoRequired ) {
765                 *ammoRequired = decl->dict.GetInt( "ammoRequired" );
766         }
767         ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
768         return ammo_i;
769 }
770
771 /*
772 ==============
773 idInventory::AddPickupName
774 ==============
775 */
776 void idInventory::AddPickupName( const char *name, const char *icon, idPlayer* owner ) { //_D3XP
777         int num;
778
779         num = pickupItemNames.Num();
780         if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].name.Icmp( name ) != 0 ) ) {
781                 idItemInfo &info = pickupItemNames.Alloc();
782
783                 if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
784                         info.name = common->GetLanguageDict()->GetString( name );
785                 } else {
786                         info.name = name;
787                 }
788                 info.icon = icon;
789
790 #ifdef _D3XP
791                 if ( gameLocal.isServer ) {
792                         idBitMsg        msg;
793                         byte            msgBuf[MAX_EVENT_PARAM_SIZE];
794
795                         msg.Init( msgBuf, sizeof( msgBuf ) );
796                         msg.WriteString( name, MAX_EVENT_PARAM_SIZE );
797                         owner->ServerSendEvent( idPlayer::EVENT_PICKUPNAME, &msg, false, -1 );
798                 }
799         } 
800 #endif
801 }
802
803 /*
804 ==============
805 idInventory::Give
806 ==============
807 */
808 bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud ) {
809         int                                             i;
810         const char                              *pos;
811         const char                              *end;
812         int                                             len;
813         idStr                                   weaponString;
814         int                                             max;
815         const idDeclEntityDef   *weaponDecl;
816         bool                                    tookWeapon;
817         int                                             amount;
818         idItemInfo                              info;
819         const char                              *name;
820
821 #ifdef _D3XP
822         if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
823                 i = AmmoIndexForAmmoClass( statname );          
824                 max = MaxAmmoForAmmoClass( owner, statname );
825
826                 if(max <= 0) {
827                         //No Max
828                         ammo[ i ] += atoi( value );
829                 } else {
830                         //Already at or above the max so don't allow the give
831                         if(ammo[ i ] >= max) {
832                                 ammo[ i ] = max;
833                                 return false;
834                         }
835                         //We were below the max so accept the give but cap it at the max
836                         ammo[ i ] += atoi( value );
837                         if(ammo[ i ] > max) {
838                                 ammo[ i ] = max;
839                         }
840                 }
841         } else
842 #endif
843
844         if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
845                 i = AmmoIndexForAmmoClass( statname );
846                 max = MaxAmmoForAmmoClass( owner, statname );
847                 if ( ammo[ i ] >= max ) {
848                         return false;
849                 }
850                 amount = atoi( value );
851                 if ( amount ) {                 
852                         ammo[ i ] += amount;
853                         if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
854                                 ammo[ i ] = max;
855                         }
856                         ammoPulse = true;
857
858                         name = AmmoPickupNameForIndex( i );
859                         if ( idStr::Length( name ) ) {
860                                 AddPickupName( name, "", owner ); //_D3XP
861                         }
862                 }
863         } else if ( !idStr::Icmp( statname, "armor" ) ) {
864                 if ( armor >= maxarmor ) {
865                         return false;   // can't hold any more, so leave the item
866                 }
867                 amount = atoi( value );
868                 if ( amount ) {
869                         armor += amount;
870                         if ( armor > maxarmor ) {
871                                 armor = maxarmor;
872                         }
873                         nextArmorDepleteTime = 0;
874                         armorPulse = true;
875                 }
876         } else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
877 #ifdef _D3XP
878                 idStr temp = statname;
879                 i = atoi(temp.Mid(7, 2));
880 #else
881                 i = WeaponIndexForAmmoClass( spawnArgs, statname + 7 );
882 #endif
883                 if ( i != -1 ) {
884                         // set, don't add. not going over the clip size limit.
885 #ifndef _D3XP
886                         clip[ i ] = atoi( value );
887 #endif
888                 }
889 #ifdef _D3XP
890         } else if ( !idStr::Icmp( statname, "invulnerability" ) ) {
891                 owner->GivePowerUp( INVULNERABILITY, SEC2MS( atof( value ) ) );
892         } else if ( !idStr::Icmp( statname, "helltime" ) ) {
893                 owner->GivePowerUp( HELLTIME, SEC2MS( atof( value ) ) );
894         } else if ( !idStr::Icmp( statname, "envirosuit" ) ) {
895                 owner->GivePowerUp( ENVIROSUIT, SEC2MS( atof( value ) ) );
896                 owner->GivePowerUp( ENVIROTIME, SEC2MS( atof( value ) ) );
897         } else if ( !idStr::Icmp( statname, "berserk" ) ) {
898                 owner->GivePowerUp( BERSERK, SEC2MS( atof( value ) ) );
899         //} else if ( !idStr::Icmp( statname, "haste" ) ) {
900         //      owner->GivePowerUp( HASTE, SEC2MS( atof( value ) ) );
901 #else
902         } else if ( !idStr::Icmp( statname, "berserk" ) ) {
903                 GivePowerUp( owner, BERSERK, SEC2MS( atof( value ) ) );
904 #endif
905         } else if ( !idStr::Icmp( statname, "mega" ) ) {
906                 GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
907         } else if ( !idStr::Icmp( statname, "weapon" ) ) {
908                 tookWeapon = false;
909                 for( pos = value; pos != NULL; pos = end ) {
910                         end = strchr( pos, ',' );
911                         if ( end ) {
912                                 len = end - pos;
913                                 end++;
914                         } else {
915                                 len = strlen( pos );
916                         }
917
918                         idStr weaponName( pos, 0, len );
919
920                         // find the number of the matching weapon name
921                         for( i = 0; i < MAX_WEAPONS; i++ ) {
922                                 if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
923                                         break;
924                                 }
925                         }
926
927                         if ( i >= MAX_WEAPONS ) {
928 #ifdef _D3XP
929                                 gameLocal.Warning( "Unknown weapon '%s'", weaponName.c_str() );
930                                 continue;
931 #else
932                                 gameLocal.Error( "Unknown weapon '%s'", weaponName.c_str() );
933 #endif
934                         }
935
936                         // cache the media for this weapon
937                         weaponDecl = gameLocal.FindEntityDef( weaponName, false );
938
939                         // don't pickup "no ammo" weapon types twice
940                         // not for D3 SP .. there is only one case in the game where you can get a no ammo
941                         // weapon when you might already have it, in that case it is more conistent to pick it up
942                         if ( gameLocal.isMultiplayer && weaponDecl && ( weapons & ( 1 << i ) ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
943                                 continue;
944                         }
945
946                         if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
947                                 if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
948                                         if ( owner->GetUserInfo()->GetBool( "ui_autoSwitch" ) && idealWeapon && i != owner->weapon_bloodstone_active1 && i != owner->weapon_bloodstone_active2 && i != owner->weapon_bloodstone_active3) {
949                                                 assert( !gameLocal.isClient );
950                                                 *idealWeapon = i;
951                                         } 
952                                         if ( owner->hud && updateHud && lastGiveTime + 1000 < gameLocal.time ) {
953                                                 owner->hud->SetStateInt( "newWeapon", i );
954                                                 owner->hud->HandleNamedEvent( "newWeapon" );
955                                                 lastGiveTime = gameLocal.time;
956                                         }
957                                         weaponPulse = true;
958                                         weapons |= ( 1 << i );
959                                         tookWeapon = true;
960                                 }
961                         }
962                 }
963                 return tookWeapon;
964         } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
965                 // ignore these as they're handled elsewhere
966                 return false;
967         } else {
968                 // unknown item
969                 gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
970                 return false;
971         }
972
973         return true;
974 }
975
976 /*
977 ===============
978 idInventoy::Drop
979 ===============
980 */
981 void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
982         // remove the weapon bit
983         // also remove the ammo associated with the weapon as we pushed it in the item
984         assert( weapon_index != -1 || weapon_classname );
985         if ( weapon_index == -1 ) {
986                 for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
987                         if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
988                                 break;
989                         }
990                 }
991                 if ( weapon_index >= MAX_WEAPONS ) {
992                         gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
993                 }
994         } else if ( !weapon_classname ) {
995                 weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
996         }
997         weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
998         ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
999         if ( ammo_i ) {
1000                 clip[ weapon_index ] = -1;
1001                 ammo[ ammo_i ] = 0;
1002         }
1003 }
1004
1005 /*
1006 ===============
1007 idInventory::HasAmmo
1008 ===============
1009 */
1010 int idInventory::HasAmmo( ammo_t type, int amount ) {
1011         if ( ( type == 0 ) || !amount ) {
1012                 // always allow weapons that don't use ammo to fire
1013                 return -1;
1014         }
1015
1016         // check if we have infinite ammo
1017         if ( ammo[ type ] < 0 ) {
1018                 return -1;
1019         }
1020
1021         // return how many shots we can fire
1022         return ammo[ type ] / amount;
1023
1024 }
1025
1026 /*
1027 ===============
1028 idInventory::HasAmmo
1029 ===============
1030 */
1031 int idInventory::HasAmmo( const char *weapon_classname, bool includeClip, idPlayer* owner ) {           //_D3XP
1032         int ammoRequired;
1033         ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
1034
1035 #ifdef _D3XP
1036         int ammoCount = HasAmmo( ammo_i, ammoRequired );
1037         if(includeClip && owner) {
1038                 ammoCount += clip[owner->SlotForWeapon(weapon_classname)];
1039         }
1040         return ammoCount;
1041 #else
1042         return HasAmmo( ammo_i, ammoRequired );
1043 #endif
1044
1045 }
1046         
1047 #ifdef _D3XP
1048 /*
1049 ===============
1050 idInventory::HasEmptyClipCannotRefill
1051 ===============
1052 */
1053 bool idInventory::HasEmptyClipCannotRefill(const char *weapon_classname, idPlayer* owner) {
1054         
1055         int clipSize = clip[owner->SlotForWeapon(weapon_classname)];
1056         if(clipSize) {
1057                 return false;
1058         }
1059
1060         const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
1061         if ( !decl ) {
1062                 gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
1063         }
1064         int minclip = decl->dict.GetInt("minclipsize");
1065         if(!minclip) {
1066                 return false;
1067         }
1068
1069         ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
1070         int ammoRequired = decl->dict.GetInt( "ammoRequired" );
1071         int ammoCount = HasAmmo( ammo_i, ammoRequired );
1072         if(ammoCount < minclip) {
1073                 return true;
1074         }
1075         return false;
1076 }
1077 #endif
1078
1079 /*
1080 ===============
1081 idInventory::UseAmmo
1082 ===============
1083 */
1084 bool idInventory::UseAmmo( ammo_t type, int amount ) {
1085         if ( !HasAmmo( type, amount ) ) {
1086                 return false;
1087         }
1088
1089         // take an ammo away if not infinite
1090         if ( ammo[ type ] >= 0 ) {
1091                 ammo[ type ] -= amount;
1092                 ammoPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots
1093         }
1094
1095         return true;
1096 }
1097
1098 /*
1099 ===============
1100 idInventory::UpdateArmor
1101 ===============
1102 */
1103 void idInventory::UpdateArmor( void ) {
1104         if ( deplete_armor != 0.0f && deplete_armor < armor ) {
1105                 if ( !nextArmorDepleteTime ) {
1106                         nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
1107                 } else if ( gameLocal.time > nextArmorDepleteTime ) {
1108                         armor -= deplete_ammount;
1109                         if ( armor < deplete_armor ) {
1110                                 armor = deplete_armor;
1111                         }
1112                         nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
1113                 }
1114         }
1115 }
1116
1117 #ifdef _D3XP
1118 /*
1119 ===============
1120 idInventory::InitRechargeAmmo
1121 ===============
1122 * Loads any recharge ammo definitions from the ammo_types entity definitions.
1123 */
1124 void idInventory::InitRechargeAmmo(idPlayer *owner) {
1125
1126         memset (rechargeAmmo, 0, sizeof(rechargeAmmo));
1127
1128         const idKeyValue *kv = owner->spawnArgs.MatchPrefix( "ammorecharge_" );
1129         while( kv ) {
1130                 idStr key = kv->GetKey();
1131                 idStr ammoname = key.Right(key.Length()- strlen("ammorecharge_"));
1132                 int ammoType = AmmoIndexForAmmoClass(ammoname);
1133                 rechargeAmmo[ammoType].ammo = (atof(kv->GetValue().c_str())*1000);
1134                 strcpy(rechargeAmmo[ammoType].ammoName, ammoname);
1135                 kv = owner->spawnArgs.MatchPrefix( "ammorecharge_", kv );
1136         }
1137 }
1138
1139 /*
1140 ===============
1141 idInventory::RechargeAmmo
1142 ===============
1143 * Called once per frame to update any ammo amount for ammo types that recharge.
1144 */
1145 void idInventory::RechargeAmmo(idPlayer *owner) {
1146
1147         for(int i = 0; i < AMMO_NUMTYPES; i++) {
1148                 if(rechargeAmmo[i].ammo > 0) {
1149                         if(!rechargeAmmo[i].rechargeTime)  {
1150                                 //Initialize the recharge timer.
1151                                 rechargeAmmo[i].rechargeTime = gameLocal.time;
1152                         }
1153                         int elapsed = gameLocal.time - rechargeAmmo[i].rechargeTime;
1154                         if(elapsed >= rechargeAmmo[i].ammo) {
1155                                 int intervals = (gameLocal.time - rechargeAmmo[i].rechargeTime)/rechargeAmmo[i].ammo;
1156                                 ammo[i] += intervals;
1157
1158                                 int max = MaxAmmoForAmmoClass(owner, rechargeAmmo[i].ammoName);
1159                                 if(max > 0) {
1160                                         if(ammo[i] > max) {
1161                                                 ammo[i] = max;
1162                                         }
1163                                 }
1164                                 rechargeAmmo[i].rechargeTime += intervals*rechargeAmmo[i].ammo;
1165                         }
1166                 }
1167         }
1168 }
1169
1170 /*
1171 ===============
1172 idInventory::CanGive
1173 ===============
1174 */
1175 bool idInventory::CanGive( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon ) {
1176
1177         if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
1178                 int max = MaxAmmoForAmmoClass(owner, statname);
1179                 int i = AmmoIndexForAmmoClass(statname);
1180
1181                 if(max <= 0) {
1182                         //No Max
1183                         return true;
1184                 } else {
1185                         //Already at or above the max so don't allow the give
1186                         if(ammo[ i ] >= max) {
1187                                 ammo[ i ] = max;
1188                                 return false;
1189                         }
1190                         return true;
1191                 }
1192         } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
1193                 // ignore these as they're handled elsewhere
1194                 //These items should not be considered as succesful gives because it messes up the max ammo items
1195                 return false;
1196         }
1197         return true;
1198 }
1199 #endif
1200
1201 /*
1202 ==============
1203 idPlayer::idPlayer
1204 ==============
1205 */
1206 idPlayer::idPlayer() {
1207         memset( &usercmd, 0, sizeof( usercmd ) );
1208
1209         noclip                                  = false;
1210         godmode                                 = false;
1211
1212         spawnAnglesSet                  = false;
1213         spawnAngles                             = ang_zero;
1214         viewAngles                              = ang_zero;
1215         cmdAngles                               = ang_zero;
1216
1217         oldButtons                              = 0;
1218         buttonMask                              = 0;
1219         oldFlags                                = 0;
1220
1221         lastHitTime                             = 0;
1222         lastSndHitTime                  = 0;
1223         lastSavingThrowTime             = 0;
1224
1225         weapon                                  = NULL;
1226
1227         hud                                             = NULL;
1228         objectiveSystem                 = NULL;
1229         objectiveSystemOpen             = false;
1230
1231 #ifdef _D3XP
1232         mountedObject                   = NULL;
1233         enviroSuitLight                 = NULL;
1234 #endif
1235
1236         heartRate                               = BASE_HEARTRATE;
1237         heartInfo.Init( 0, 0, 0, 0 );
1238         lastHeartAdjust                 = 0;
1239         lastHeartBeat                   = 0;
1240         lastDmgTime                             = 0;
1241         deathClearContentsTime  = 0;
1242         lastArmorPulse                  = -10000;
1243         stamina                                 = 0.0f;
1244         healthPool                              = 0.0f;
1245         nextHealthPulse                 = 0;
1246         healthPulse                             = false;
1247         nextHealthTake                  = 0;
1248         healthTake                              = false;
1249
1250         scoreBoardOpen                  = false;
1251         forceScoreBoard                 = false;
1252         forceRespawn                    = false;
1253         spectating                              = false;
1254         spectator                               = 0;
1255         colorBar                                = vec3_zero;
1256         colorBarIndex                   = 0;
1257         forcedReady                             = false;
1258         wantSpectate                    = false;
1259
1260 #ifdef CTF    
1261         carryingFlag                    = false;
1262 #endif    
1263
1264         lastHitToggle                   = false;
1265
1266         minRespawnTime                  = 0;
1267         maxRespawnTime                  = 0;
1268
1269         firstPersonViewOrigin   = vec3_zero;
1270         firstPersonViewAxis             = mat3_identity;
1271
1272         hipJoint                                = INVALID_JOINT;
1273         chestJoint                              = INVALID_JOINT;
1274         headJoint                               = INVALID_JOINT;
1275
1276         bobFoot                                 = 0;
1277         bobFrac                                 = 0.0f;
1278         bobfracsin                              = 0.0f;
1279         bobCycle                                = 0;
1280         xyspeed                                 = 0.0f;
1281         stepUpTime                              = 0;
1282         stepUpDelta                             = 0.0f;
1283         idealLegsYaw                    = 0.0f;
1284         legsYaw                                 = 0.0f;
1285         legsForward                             = true;
1286         oldViewYaw                              = 0.0f;
1287         viewBobAngles                   = ang_zero;
1288         viewBob                                 = vec3_zero;
1289         landChange                              = 0;
1290         landTime                                = 0;
1291
1292         currentWeapon                   = -1;
1293         idealWeapon                             = -1;
1294         previousWeapon                  = -1;
1295         weaponSwitchTime                =  0;
1296         weaponEnabled                   = true;
1297         weapon_soulcube                 = -1;
1298         weapon_pda                              = -1;
1299         weapon_fists                    = -1;
1300 #ifdef _D3XP
1301         weapon_bloodstone               = -1;
1302         weapon_bloodstone_active1 = -1;
1303         weapon_bloodstone_active2 = -1;
1304         weapon_bloodstone_active3 = -1;
1305         harvest_lock                    = false;
1306
1307         hudPowerup                              = -1;
1308         lastHudPowerup                  = -1;
1309         hudPowerupDuration              = 0;
1310 #endif
1311         showWeaponViewModel             = true;
1312
1313         skin                                    = NULL;
1314         powerUpSkin                             = NULL;
1315         baseSkinName                    = "";
1316
1317         numProjectilesFired             = 0;
1318         numProjectileHits               = 0;
1319
1320         airless                                 = false;
1321         airTics                                 = 0;
1322         lastAirDamage                   = 0;
1323
1324         gibDeath                                = false;
1325         gibsLaunched                    = false;
1326         gibsDir                                 = vec3_zero;
1327
1328         zoomFov.Init( 0, 0, 0, 0 );
1329         centerView.Init( 0, 0, 0, 0 );
1330         fxFov                                   = false;
1331
1332         influenceFov                    = 0;
1333         influenceActive                 = 0;
1334         influenceRadius                 = 0.0f;
1335         influenceEntity                 = NULL;
1336         influenceMaterial               = NULL;
1337         influenceSkin                   = NULL;
1338
1339         privateCameraView               = NULL;
1340
1341         memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
1342         memset( loggedAccel, 0, sizeof( loggedAccel ) );
1343         currentLoggedAccel      = 0;
1344
1345         focusTime                               = 0;
1346         focusGUIent                             = NULL;
1347         focusUI                                 = NULL;
1348         focusCharacter                  = NULL;
1349         talkCursor                              = 0;
1350         focusVehicle                    = NULL;
1351         cursor                                  = NULL;
1352         
1353         oldMouseX                               = 0;
1354         oldMouseY                               = 0;
1355
1356         pdaAudio                                = "";
1357         pdaVideo                                = "";
1358         pdaVideoWave                    = "";
1359
1360         lastDamageDef                   = 0;
1361         lastDamageDir                   = vec3_zero;
1362         lastDamageLocation              = 0;
1363         smoothedFrame                   = 0;
1364         smoothedOriginUpdated   = false;
1365         smoothedOrigin                  = vec3_zero;
1366         smoothedAngles                  = ang_zero;
1367
1368         fl.networkSync                  = true;
1369
1370         latchedTeam                             = -1;
1371         doingDeathSkin                  = false;
1372         weaponGone                              = false;
1373         useInitialSpawns                = false;
1374         tourneyRank                             = 0;
1375         lastSpectateTeleport    = 0;
1376         tourneyLine                             = 0;
1377         hiddenWeapon                    = false;
1378         tipUp                                   = false;
1379         objectiveUp                             = false;
1380         teleportEntity                  = NULL;
1381         teleportKiller                  = -1;
1382         respawning                              = false;
1383         ready                                   = false;
1384         leader                                  = false;
1385         lastSpectateChange              = 0;
1386         lastTeleFX                              = -9999;
1387         weaponCatchup                   = false;
1388         lastSnapshotSequence    = 0;
1389
1390         MPAim                                   = -1;
1391         lastMPAim                               = -1;
1392         lastMPAimTime                   = 0;
1393         MPAimFadeTime                   = 0;
1394         MPAimHighlight                  = false;
1395
1396         spawnedTime                             = 0;
1397         lastManOver                             = false;
1398         lastManPlayAgain                = false;
1399         lastManPresent                  = false;
1400
1401         isTelefragged                   = false;
1402
1403         isLagged                                = false;
1404         isChatting                              = false;
1405
1406         selfSmooth                              = false;
1407 }
1408
1409 /*
1410 ==============
1411 idPlayer::LinkScriptVariables
1412
1413 set up conditions for animation
1414 ==============
1415 */
1416 void idPlayer::LinkScriptVariables( void ) {
1417         AI_FORWARD.LinkTo(                      scriptObject, "AI_FORWARD" );
1418         AI_BACKWARD.LinkTo(                     scriptObject, "AI_BACKWARD" );
1419         AI_STRAFE_LEFT.LinkTo(          scriptObject, "AI_STRAFE_LEFT" );
1420         AI_STRAFE_RIGHT.LinkTo(         scriptObject, "AI_STRAFE_RIGHT" );
1421         AI_ATTACK_HELD.LinkTo(          scriptObject, "AI_ATTACK_HELD" );
1422         AI_WEAPON_FIRED.LinkTo(         scriptObject, "AI_WEAPON_FIRED" );
1423         AI_JUMP.LinkTo(                         scriptObject, "AI_JUMP" );
1424         AI_DEAD.LinkTo(                         scriptObject, "AI_DEAD" );
1425         AI_CROUCH.LinkTo(                       scriptObject, "AI_CROUCH" );
1426         AI_ONGROUND.LinkTo(                     scriptObject, "AI_ONGROUND" );
1427         AI_ONLADDER.LinkTo(                     scriptObject, "AI_ONLADDER" );
1428         AI_HARDLANDING.LinkTo(          scriptObject, "AI_HARDLANDING" );
1429         AI_SOFTLANDING.LinkTo(          scriptObject, "AI_SOFTLANDING" );
1430         AI_RUN.LinkTo(                          scriptObject, "AI_RUN" );
1431         AI_PAIN.LinkTo(                         scriptObject, "AI_PAIN" );
1432         AI_RELOAD.LinkTo(                       scriptObject, "AI_RELOAD" );
1433         AI_TELEPORT.LinkTo(                     scriptObject, "AI_TELEPORT" );
1434         AI_TURN_LEFT.LinkTo(            scriptObject, "AI_TURN_LEFT" );
1435         AI_TURN_RIGHT.LinkTo(           scriptObject, "AI_TURN_RIGHT" );
1436 }
1437
1438 /*
1439 ==============
1440 idPlayer::SetupWeaponEntity
1441 ==============
1442 */
1443 void idPlayer::SetupWeaponEntity( void ) {
1444         int w;
1445         const char *weap;
1446
1447         if ( weapon.GetEntity() ) {
1448                 // get rid of old weapon
1449                 weapon.GetEntity()->Clear();
1450                 currentWeapon = -1;
1451         } else if ( !gameLocal.isClient ) {
1452                 weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
1453                 weapon.GetEntity()->SetOwner( this );
1454                 currentWeapon = -1;
1455         }
1456
1457         for( w = 0; w < MAX_WEAPONS; w++ ) {
1458                 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
1459                 if ( weap && *weap ) {
1460                         idWeapon::CacheWeapon( weap );
1461                 }
1462         }
1463 }
1464
1465 /*
1466 ==============
1467 idPlayer::Init
1468 ==============
1469 */
1470 void idPlayer::Init( void ) {
1471         const char                      *value;
1472         const idKeyValue        *kv;
1473
1474         noclip                                  = false;
1475         godmode                                 = false;
1476
1477         oldButtons                              = 0;
1478         oldFlags                                = 0;
1479
1480         currentWeapon                   = -1;
1481         idealWeapon                             = -1;
1482         previousWeapon                  = -1;
1483         weaponSwitchTime                = 0;
1484         weaponEnabled                   = true;
1485         weapon_soulcube                 = SlotForWeapon( "weapon_soulcube" );
1486         weapon_pda                              = SlotForWeapon( "weapon_pda" );
1487         weapon_fists                    = SlotForWeapon( "weapon_fists" );
1488 #ifdef _D3XP
1489         weapon_bloodstone               = SlotForWeapon( "weapon_bloodstone_passive" );
1490         weapon_bloodstone_active1 = SlotForWeapon( "weapon_bloodstone_active1" );
1491         weapon_bloodstone_active2 = SlotForWeapon( "weapon_bloodstone_active2" );
1492         weapon_bloodstone_active3 = SlotForWeapon( "weapon_bloodstone_active3" );
1493         harvest_lock                    = false;
1494 #endif
1495         showWeaponViewModel             = GetUserInfo()->GetBool( "ui_showGun" );
1496
1497
1498         lastDmgTime                             = 0;
1499         lastArmorPulse                  = -10000;
1500         lastHeartAdjust                 = 0;
1501         lastHeartBeat                   = 0;
1502         heartInfo.Init( 0, 0, 0, 0 );
1503
1504         bobCycle                                = 0;
1505         bobFrac                                 = 0.0f;
1506         landChange                              = 0;
1507         landTime                                = 0;
1508         zoomFov.Init( 0, 0, 0, 0 );
1509         centerView.Init( 0, 0, 0, 0 );
1510         fxFov                                   = false;
1511
1512         influenceFov                    = 0;
1513         influenceActive                 = 0;
1514         influenceRadius                 = 0.0f;
1515         influenceEntity                 = NULL;
1516         influenceMaterial               = NULL;
1517         influenceSkin                   = NULL;
1518
1519 #ifdef _D3XP
1520         mountedObject                   = NULL;
1521         if( enviroSuitLight.IsValid() ) {
1522                 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
1523         }
1524         enviroSuitLight                 = NULL;
1525         healthRecharge                  = false;
1526         lastHealthRechargeTime  = 0;
1527         rechargeSpeed                   = 500;
1528         new_g_damageScale               = 1.f;
1529         bloomEnabled                    = false;
1530         bloomSpeed                              = 1.f;
1531         bloomIntensity                  = -0.01f;
1532         inventory.InitRechargeAmmo(this);
1533         hudPowerup                              = -1;
1534         lastHudPowerup                  = -1;
1535         hudPowerupDuration              = 0;
1536 #endif
1537
1538         currentLoggedAccel              = 0;
1539
1540         focusTime                               = 0;
1541         focusGUIent                             = NULL;
1542         focusUI                                 = NULL;
1543         focusCharacter                  = NULL;
1544         talkCursor                              = 0;
1545         focusVehicle                    = NULL;
1546
1547         // remove any damage effects
1548         playerView.ClearEffects();
1549
1550         // damage values
1551         fl.takedamage                   = true;
1552         ClearPain();
1553
1554         // restore persistent data
1555         RestorePersistantInfo();
1556
1557         bobCycle                = 0;
1558         stamina                 = 0.0f;
1559         healthPool              = 0.0f;
1560         nextHealthPulse = 0;
1561         healthPulse             = false;
1562         nextHealthTake  = 0;
1563         healthTake              = false;
1564
1565         SetupWeaponEntity();
1566         currentWeapon = -1;
1567         previousWeapon = -1;
1568
1569         heartRate = BASE_HEARTRATE;
1570         AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
1571
1572         idealLegsYaw = 0.0f;
1573         legsYaw = 0.0f;
1574         legsForward     = true;
1575         oldViewYaw = 0.0f;
1576
1577         // set the pm_ cvars
1578         if ( !gameLocal.isMultiplayer || gameLocal.isServer ) {
1579                 kv = spawnArgs.MatchPrefix( "pm_", NULL );
1580                 while( kv ) {
1581                         cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
1582                         kv = spawnArgs.MatchPrefix( "pm_", kv );
1583                 }
1584         }
1585
1586         // disable stamina on hell levels
1587         if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
1588                 pm_stamina.SetFloat( 0.0f );
1589         }
1590
1591         // stamina always initialized to maximum
1592         stamina = pm_stamina.GetFloat();
1593
1594         // air always initialized to maximum too
1595         airTics = pm_airTics.GetFloat();
1596         airless = false;
1597
1598         gibDeath = false;
1599         gibsLaunched = false;
1600         gibsDir.Zero();
1601
1602         // set the gravity
1603         physicsObj.SetGravity( gameLocal.GetGravity() );
1604
1605         // start out standing
1606         SetEyeHeight( pm_normalviewheight.GetFloat() );
1607
1608         stepUpTime = 0;
1609         stepUpDelta = 0.0f;
1610         viewBobAngles.Zero();
1611         viewBob.Zero();
1612
1613         value = spawnArgs.GetString( "model" );
1614         if ( value && ( *value != 0 ) ) {
1615                 SetModel( value );
1616         }
1617
1618         if ( cursor ) {
1619                 cursor->SetStateInt( "talkcursor", 0 );
1620                 cursor->SetStateString( "combatcursor", "1" );
1621                 cursor->SetStateString( "itemcursor", "0" );
1622                 cursor->SetStateString( "guicursor", "0" );
1623 #ifdef _D3XP
1624                 cursor->SetStateString( "grabbercursor", "0" );
1625 #endif
1626         }
1627
1628         if ( ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) && skin ) {
1629                 SetSkin( skin );
1630                 renderEntity.shaderParms[6] = 0.0f;
1631         } else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
1632                 skin = declManager->FindSkin( value );
1633                 SetSkin( skin );
1634                 renderEntity.shaderParms[6] = 0.0f;
1635         }
1636
1637         value = spawnArgs.GetString( "bone_hips", "" );
1638         hipJoint = animator.GetJointHandle( value );
1639         if ( hipJoint == INVALID_JOINT ) {
1640                 gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
1641         }
1642
1643         value = spawnArgs.GetString( "bone_chest", "" );
1644         chestJoint = animator.GetJointHandle( value );
1645         if ( chestJoint == INVALID_JOINT ) {
1646                 gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
1647         }
1648
1649         value = spawnArgs.GetString( "bone_head", "" );
1650         headJoint = animator.GetJointHandle( value );
1651         if ( headJoint == INVALID_JOINT ) {
1652                 gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
1653         }
1654
1655         // initialize the script variables
1656         AI_FORWARD              = false;
1657         AI_BACKWARD             = false;
1658         AI_STRAFE_LEFT  = false;
1659         AI_STRAFE_RIGHT = false;
1660         AI_ATTACK_HELD  = false;
1661         AI_WEAPON_FIRED = false;
1662         AI_JUMP                 = false;
1663         AI_DEAD                 = false;
1664         AI_CROUCH               = false;
1665         AI_ONGROUND             = true;
1666         AI_ONLADDER             = false;
1667         AI_HARDLANDING  = false;
1668         AI_SOFTLANDING  = false;
1669         AI_RUN                  = false;
1670         AI_PAIN                 = false;
1671         AI_RELOAD               = false;
1672         AI_TELEPORT             = false;
1673         AI_TURN_LEFT    = false;
1674         AI_TURN_RIGHT   = false;
1675
1676         // reset the script object
1677         ConstructScriptObject();
1678
1679         // execute the script so the script object's constructor takes effect immediately
1680         scriptThread->Execute();
1681         
1682         forceScoreBoard         = false;
1683         forcedReady                     = false;
1684
1685         privateCameraView       = NULL;
1686
1687         lastSpectateChange      = 0;
1688         lastTeleFX                      = -9999;
1689
1690         hiddenWeapon            = false;
1691         tipUp                           = false;
1692         objectiveUp                     = false;
1693         teleportEntity          = NULL;
1694         teleportKiller          = -1;
1695         leader                          = false;
1696
1697         SetPrivateCameraView( NULL );
1698
1699         lastSnapshotSequence    = 0;
1700
1701         MPAim                           = -1;
1702         lastMPAim                       = -1;
1703         lastMPAimTime           = 0;
1704         MPAimFadeTime           = 0;
1705         MPAimHighlight          = false;
1706
1707         if ( hud ) {
1708                 hud->HandleNamedEvent( "aim_clear" );
1709         }
1710
1711         //isChatting = false;
1712         cvarSystem->SetCVarBool("ui_chat", false);
1713 }
1714
1715 /*
1716 ==============
1717 idPlayer::Spawn
1718
1719 Prepare any resources used by the player.
1720 ==============
1721 */
1722 void idPlayer::Spawn( void ) {
1723         idStr           temp;
1724         idBounds        bounds;
1725
1726         if ( entityNumber >= MAX_CLIENTS ) {
1727                 gameLocal.Error( "entityNum > MAX_CLIENTS for player.  Player may only be spawned with a client." );
1728         }
1729
1730         // allow thinking during cinematics
1731         cinematic = true;
1732
1733         if ( gameLocal.isMultiplayer ) {
1734                 // always start in spectating state waiting to be spawned in
1735                 // do this before SetClipModel to get the right bounding box
1736                 spectating = true;
1737         }
1738
1739         // set our collision model
1740         physicsObj.SetSelf( this );
1741         SetClipModel();
1742         physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
1743         physicsObj.SetContents( CONTENTS_BODY );
1744         physicsObj.SetClipMask( MASK_PLAYERSOLID );
1745         SetPhysics( &physicsObj );
1746         InitAASLocation();
1747
1748         skin = renderEntity.customSkin;
1749
1750         // only the local player needs guis
1751         if ( !gameLocal.isMultiplayer || entityNumber == gameLocal.localClientNum ) {
1752
1753                 // load HUD
1754                 if ( gameLocal.isMultiplayer ) {
1755                         hud = uiManager->FindGui( "guis/mphud.gui", true, false, true );
1756                 } else if ( spawnArgs.GetString( "hud", "", temp ) ) {
1757                         hud = uiManager->FindGui( temp, true, false, true );
1758                 }
1759                 if ( hud ) {
1760                         hud->Activate( true, gameLocal.time );
1761 #ifdef CTF
1762             if ( gameLocal.mpGame.IsGametypeFlagBased() ) {
1763                 hud->SetStateInt( "red_team_score", gameLocal.mpGame.GetFlagPoints(0) );
1764                 hud->SetStateInt( "blue_team_score", gameLocal.mpGame.GetFlagPoints(1) );
1765             }
1766 #endif            
1767                 }
1768
1769                 // load cursor
1770                 if ( spawnArgs.GetString( "cursor", "", temp ) ) {
1771                         cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer );
1772                 }
1773                 if ( cursor ) {
1774                         cursor->Activate( true, gameLocal.time );
1775                 }
1776
1777                 objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true );
1778                 objectiveSystemOpen = false;
1779         }
1780
1781         SetLastHitTime( 0 );
1782
1783         // load the armor sound feedback
1784         declManager->FindSound( "player_sounds_hitArmor" );
1785
1786         // set up conditions for animation
1787         LinkScriptVariables();
1788
1789         animator.RemoveOriginOffset( true );
1790
1791         // initialize user info related settings
1792         // on server, we wait for the userinfo broadcast, as this controls when the player is initially spawned in game
1793         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
1794                 UserInfoChanged( false );
1795         }
1796
1797         // create combat collision hull for exact collision detection
1798         SetCombatModel();
1799
1800         // init the damage effects
1801         playerView.SetPlayerEntity( this );
1802
1803         // supress model in non-player views, but allow it in mirrors and remote views
1804         renderEntity.suppressSurfaceInViewID = entityNumber+1;
1805
1806         // don't project shadow on self or weapon
1807         renderEntity.noSelfShadow = true;
1808
1809         idAFAttachment *headEnt = head.GetEntity();
1810         if ( headEnt ) {
1811                 headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
1812                 headEnt->GetRenderEntity()->noSelfShadow = true;
1813         }
1814
1815         if ( gameLocal.isMultiplayer ) {
1816                 Init();
1817                 Hide(); // properly hidden if starting as a spectator
1818                 if ( !gameLocal.isClient ) {
1819                         // set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
1820                         SetupWeaponEntity();
1821                         SpawnFromSpawnSpot();
1822                         forceRespawn = true;
1823                         assert( spectating );
1824                 }
1825         } else {
1826                 SetupWeaponEntity();
1827                 SpawnFromSpawnSpot();
1828         }
1829
1830         // trigger playtesting item gives, if we didn't get here from a previous level
1831         // the devmap key will be set on the first devmap, but cleared on any level
1832         // transitions
1833         if ( !gameLocal.isMultiplayer && gameLocal.serverInfo.FindKey( "devmap" ) ) {
1834                 // fire a trigger with the name "devmap"
1835                 idEntity *ent = gameLocal.FindEntity( "devmap" );
1836                 if ( ent ) {
1837                         ent->ActivateTargets( this );
1838                 }
1839         }
1840         if ( hud ) {
1841                 // We can spawn with a full soul cube, so we need to make sure the hud knows this
1842 #ifndef _D3XP
1843                 if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
1844                         int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
1845                         if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_souls" ) ] >= max_souls ) {
1846                                 hud->HandleNamedEvent( "soulCubeReady" );
1847                         }
1848                 }
1849 #endif
1850 #ifdef _D3XP
1851                 //We can spawn with a full bloodstone, so make sure the hud knows
1852                 if ( weapon_bloodstone > 0 && ( inventory.weapons & ( 1 << weapon_bloodstone ) ) ) {
1853                         //int max_blood = inventory.MaxAmmoForAmmoClass( this, "ammo_bloodstone" );
1854                         //if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_bloodstone" ) ] >= max_blood ) {
1855                                 hud->HandleNamedEvent( "bloodstoneReady" );
1856                         //}
1857                 }
1858 #endif
1859                 hud->HandleNamedEvent( "itemPickup" );
1860         }
1861
1862         if ( GetPDA() ) {
1863                 // Add any emails from the inventory
1864                 for ( int i = 0; i < inventory.emails.Num(); i++ ) {
1865                         GetPDA()->AddEmail( inventory.emails[i] );
1866                 }
1867                 GetPDA()->SetSecurity( common->GetLanguageDict()->GetString( "#str_00066" ) );
1868         }
1869
1870         if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
1871                 hiddenWeapon = true;
1872                 if ( weapon.GetEntity() ) {
1873                         weapon.GetEntity()->LowerWeapon();
1874                 }
1875                 idealWeapon = 0;
1876         } else {
1877                 hiddenWeapon = false;
1878         }
1879         
1880         if ( hud ) {
1881                 UpdateHudWeapon();
1882                 hud->StateChanged( gameLocal.time );
1883         }
1884
1885         tipUp = false;
1886         objectiveUp = false;
1887
1888         if ( inventory.levelTriggers.Num() ) {
1889                 PostEventMS( &EV_Player_LevelTrigger, 0 );
1890         }
1891
1892         inventory.pdaOpened = false;
1893         inventory.selPDA = 0;
1894
1895         if ( !gameLocal.isMultiplayer ) {
1896                 if ( g_skill.GetInteger() < 2 ) {
1897                         if ( health < 25 ) {
1898                                 health = 25;
1899                         }
1900                         if ( g_useDynamicProtection.GetBool() ) {
1901 #ifdef _D3XP
1902                                 new_g_damageScale = 1.0f;
1903 #else
1904                                 g_damageScale.SetFloat( 1.0f );
1905 #endif
1906                         }
1907                 } else {
1908 #ifdef _D3XP
1909                         new_g_damageScale = 1.0f;
1910 #else
1911                         g_damageScale.SetFloat( 1.0f );
1912 #endif
1913                         g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
1914 #ifndef ID_DEMO_BUILD
1915                         if ( g_skill.GetInteger() == 3 ) {
1916                                 healthTake = true;
1917                                 nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
1918                         }
1919 #endif
1920                 }
1921         }
1922
1923 #ifdef _D3XP
1924         //Setup the weapon toggle lists
1925         const idKeyValue *kv;
1926         kv = spawnArgs.MatchPrefix( "weapontoggle", NULL );
1927         while( kv ) {
1928                 WeaponToggle_t newToggle;
1929                 strcpy(newToggle.name, kv->GetKey().c_str());
1930
1931                 idStr toggleData = kv->GetValue();
1932
1933                 idLexer src;
1934                 idToken token;
1935                 src.LoadMemory(toggleData, toggleData.Length(), "toggleData");
1936                 while(1) {
1937                         if(!src.ReadToken(&token)) {
1938                                 break;
1939                         }
1940                         int index = atoi(token.c_str());
1941                         newToggle.toggleList.Append(index);
1942
1943                         //Skip the ,
1944                         src.ReadToken(&token);
1945                 }
1946                 weaponToggles.Set(newToggle.name, newToggle);
1947
1948                 kv = spawnArgs.MatchPrefix( "weapontoggle", kv );
1949         }
1950 #endif
1951
1952 #ifdef _D3XP
1953         if(g_skill.GetInteger() >= 3) {
1954                 if(!WeaponAvailable("weapon_bloodstone_passive")) {
1955                         GiveInventoryItem("weapon_bloodstone_passive");
1956                 }
1957                 if(!WeaponAvailable("weapon_bloodstone_active1")) {
1958                         GiveInventoryItem("weapon_bloodstone_active1");
1959                 }
1960                 if(!WeaponAvailable("weapon_bloodstone_active2")) {
1961                         GiveInventoryItem("weapon_bloodstone_active2");
1962                 }
1963                 if(!WeaponAvailable("weapon_bloodstone_active3")) {
1964                         GiveInventoryItem("weapon_bloodstone_active3");
1965                 }
1966         }
1967
1968         bloomEnabled                    = false;
1969         bloomSpeed                              = 1;
1970         bloomIntensity                  = -0.01f;
1971 #endif
1972 }
1973
1974 /*
1975 ==============
1976 idPlayer::~idPlayer()
1977
1978 Release any resources used by the player.
1979 ==============
1980 */
1981 idPlayer::~idPlayer() {
1982         delete weapon.GetEntity();
1983         weapon = NULL;
1984 #ifdef CTF
1985         if ( enviroSuitLight.IsValid() ) {
1986                 enviroSuitLight.GetEntity()->ProcessEvent( &EV_Remove );
1987         }
1988         // have to do this here, idMultiplayerGame::DisconnectClient() is too late
1989         if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() ) { 
1990                 ReturnFlag();
1991         }
1992 #endif
1993 }
1994
1995 /*
1996 ===========
1997 idPlayer::Save
1998 ===========
1999 */
2000 void idPlayer::Save( idSaveGame *savefile ) const {
2001         int i;
2002
2003         savefile->WriteUsercmd( usercmd );
2004         playerView.Save( savefile );
2005
2006         savefile->WriteBool( noclip );
2007         savefile->WriteBool( godmode );
2008
2009         // don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
2010         savefile->WriteAngles( spawnAngles );
2011         savefile->WriteAngles( viewAngles );
2012         savefile->WriteAngles( cmdAngles );
2013
2014         savefile->WriteInt( buttonMask );
2015         savefile->WriteInt( oldButtons );
2016         savefile->WriteInt( oldFlags );
2017
2018         savefile->WriteInt( lastHitTime );
2019         savefile->WriteInt( lastSndHitTime );
2020         savefile->WriteInt( lastSavingThrowTime );
2021
2022         // idBoolFields don't need to be saved, just re-linked in Restore
2023
2024         inventory.Save( savefile );
2025         weapon.Save( savefile );
2026
2027         savefile->WriteUserInterface( hud, false );
2028         savefile->WriteUserInterface( objectiveSystem, false );
2029         savefile->WriteBool( objectiveSystemOpen );
2030
2031         savefile->WriteInt( weapon_soulcube );
2032         savefile->WriteInt( weapon_pda );
2033         savefile->WriteInt( weapon_fists );
2034 #ifdef _D3XP
2035         savefile->WriteInt( weapon_bloodstone );
2036         savefile->WriteInt( weapon_bloodstone_active1 );
2037         savefile->WriteInt( weapon_bloodstone_active2 );
2038         savefile->WriteInt( weapon_bloodstone_active3 );
2039         savefile->WriteBool( harvest_lock );
2040         savefile->WriteInt( hudPowerup );
2041         savefile->WriteInt( lastHudPowerup );
2042         savefile->WriteInt( hudPowerupDuration );
2043         
2044         
2045 #endif
2046
2047         savefile->WriteInt( heartRate );
2048
2049         savefile->WriteFloat( heartInfo.GetStartTime() );
2050         savefile->WriteFloat( heartInfo.GetDuration() );
2051         savefile->WriteFloat( heartInfo.GetStartValue() );
2052         savefile->WriteFloat( heartInfo.GetEndValue() );
2053
2054         savefile->WriteInt( lastHeartAdjust );
2055         savefile->WriteInt( lastHeartBeat );
2056         savefile->WriteInt( lastDmgTime );
2057         savefile->WriteInt( deathClearContentsTime );
2058         savefile->WriteBool( doingDeathSkin );
2059         savefile->WriteInt( lastArmorPulse );
2060         savefile->WriteFloat( stamina );
2061         savefile->WriteFloat( healthPool );
2062         savefile->WriteInt( nextHealthPulse );
2063         savefile->WriteBool( healthPulse );
2064         savefile->WriteInt( nextHealthTake );
2065         savefile->WriteBool( healthTake );
2066
2067         savefile->WriteBool( hiddenWeapon );
2068         soulCubeProjectile.Save( savefile );
2069
2070         savefile->WriteInt( spectator );
2071         savefile->WriteVec3( colorBar );
2072         savefile->WriteInt( colorBarIndex );
2073         savefile->WriteBool( scoreBoardOpen );
2074         savefile->WriteBool( forceScoreBoard );
2075         savefile->WriteBool( forceRespawn );
2076         savefile->WriteBool( spectating );
2077         savefile->WriteInt( lastSpectateTeleport );
2078         savefile->WriteBool( lastHitToggle );
2079         savefile->WriteBool( forcedReady );
2080         savefile->WriteBool( wantSpectate );
2081         savefile->WriteBool( weaponGone );
2082         savefile->WriteBool( useInitialSpawns );
2083         savefile->WriteInt( latchedTeam );
2084         savefile->WriteInt( tourneyRank );
2085         savefile->WriteInt( tourneyLine );
2086
2087         teleportEntity.Save( savefile );
2088         savefile->WriteInt( teleportKiller );
2089
2090         savefile->WriteInt( minRespawnTime );
2091         savefile->WriteInt( maxRespawnTime );
2092
2093         savefile->WriteVec3( firstPersonViewOrigin );
2094         savefile->WriteMat3( firstPersonViewAxis );
2095
2096         // don't bother saving dragEntity since it's a dev tool
2097
2098         savefile->WriteJoint( hipJoint );
2099         savefile->WriteJoint( chestJoint );
2100         savefile->WriteJoint( headJoint );
2101
2102         savefile->WriteStaticObject( physicsObj );
2103
2104         savefile->WriteInt( aasLocation.Num() );
2105         for( i = 0; i < aasLocation.Num(); i++ ) {
2106                 savefile->WriteInt( aasLocation[ i ].areaNum );
2107                 savefile->WriteVec3( aasLocation[ i ].pos );
2108         }
2109
2110         savefile->WriteInt( bobFoot );
2111         savefile->WriteFloat( bobFrac );
2112         savefile->WriteFloat( bobfracsin );
2113         savefile->WriteInt( bobCycle );
2114         savefile->WriteFloat( xyspeed );
2115         savefile->WriteInt( stepUpTime );
2116         savefile->WriteFloat( stepUpDelta );
2117         savefile->WriteFloat( idealLegsYaw );
2118         savefile->WriteFloat( legsYaw );
2119         savefile->WriteBool( legsForward );
2120         savefile->WriteFloat( oldViewYaw );
2121         savefile->WriteAngles( viewBobAngles );
2122         savefile->WriteVec3( viewBob );
2123         savefile->WriteInt( landChange );
2124         savefile->WriteInt( landTime );
2125
2126         savefile->WriteInt( currentWeapon );
2127         savefile->WriteInt( idealWeapon );
2128         savefile->WriteInt( previousWeapon );
2129         savefile->WriteInt( weaponSwitchTime );
2130         savefile->WriteBool( weaponEnabled );
2131         savefile->WriteBool( showWeaponViewModel );
2132
2133         savefile->WriteSkin( skin );
2134         savefile->WriteSkin( powerUpSkin );
2135         savefile->WriteString( baseSkinName );
2136
2137         savefile->WriteInt( numProjectilesFired );
2138         savefile->WriteInt( numProjectileHits );
2139
2140         savefile->WriteBool( airless );
2141         savefile->WriteInt( airTics );
2142         savefile->WriteInt( lastAirDamage );
2143
2144         savefile->WriteBool( gibDeath );
2145         savefile->WriteBool( gibsLaunched );
2146         savefile->WriteVec3( gibsDir );
2147
2148         savefile->WriteFloat( zoomFov.GetStartTime() );
2149         savefile->WriteFloat( zoomFov.GetDuration() );
2150         savefile->WriteFloat( zoomFov.GetStartValue() );
2151         savefile->WriteFloat( zoomFov.GetEndValue() );
2152
2153         savefile->WriteFloat( centerView.GetStartTime() );
2154         savefile->WriteFloat( centerView.GetDuration() );
2155         savefile->WriteFloat( centerView.GetStartValue() );
2156         savefile->WriteFloat( centerView.GetEndValue() );
2157
2158         savefile->WriteBool( fxFov );
2159
2160         savefile->WriteFloat( influenceFov );
2161         savefile->WriteInt( influenceActive );
2162         savefile->WriteFloat( influenceRadius );
2163         savefile->WriteObject( influenceEntity );
2164         savefile->WriteMaterial( influenceMaterial );
2165         savefile->WriteSkin( influenceSkin );
2166
2167         savefile->WriteObject( privateCameraView );
2168
2169         for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
2170                 savefile->WriteAngles( loggedViewAngles[ i ] );
2171         }
2172         for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
2173                 savefile->WriteInt( loggedAccel[ i ].time );
2174                 savefile->WriteVec3( loggedAccel[ i ].dir );
2175         }
2176         savefile->WriteInt( currentLoggedAccel );
2177
2178         savefile->WriteObject( focusGUIent );
2179         // can't save focusUI
2180         savefile->WriteObject( focusCharacter );
2181         savefile->WriteInt( talkCursor );
2182         savefile->WriteInt( focusTime );
2183         savefile->WriteObject( focusVehicle );
2184         savefile->WriteUserInterface( cursor, false );
2185
2186         savefile->WriteInt( oldMouseX );
2187         savefile->WriteInt( oldMouseY );
2188
2189         savefile->WriteString( pdaAudio );
2190         savefile->WriteString( pdaVideo );
2191         savefile->WriteString( pdaVideoWave );
2192
2193         savefile->WriteBool( tipUp );
2194         savefile->WriteBool( objectiveUp );
2195
2196         savefile->WriteInt( lastDamageDef );
2197         savefile->WriteVec3( lastDamageDir );
2198         savefile->WriteInt( lastDamageLocation );
2199         savefile->WriteInt( smoothedFrame );
2200         savefile->WriteBool( smoothedOriginUpdated );
2201         savefile->WriteVec3( smoothedOrigin );
2202         savefile->WriteAngles( smoothedAngles );
2203
2204         savefile->WriteBool( ready );
2205         savefile->WriteBool( respawning );
2206         savefile->WriteBool( leader );
2207         savefile->WriteInt( lastSpectateChange );
2208         savefile->WriteInt( lastTeleFX );
2209
2210         savefile->WriteFloat( pm_stamina.GetFloat() );
2211
2212         if ( hud ) {
2213                 hud->SetStateString( "message", common->GetLanguageDict()->GetString( "#str_02916" ) );
2214                 hud->HandleNamedEvent( "Message" );
2215         }
2216
2217 #ifdef _D3XP
2218         savefile->WriteInt(weaponToggles.Num());
2219         for(i = 0; i < weaponToggles.Num(); i++) {
2220                 WeaponToggle_t* weaponToggle = weaponToggles.GetIndex(i);
2221                 savefile->WriteString(weaponToggle->name);
2222                 savefile->WriteInt(weaponToggle->toggleList.Num());
2223                 for(int j = 0; j < weaponToggle->toggleList.Num(); j++) {
2224                         savefile->WriteInt(weaponToggle->toggleList[j]);
2225                 }
2226         }
2227         savefile->WriteObject( mountedObject );
2228         enviroSuitLight.Save( savefile );
2229         savefile->WriteBool( healthRecharge );
2230         savefile->WriteInt( lastHealthRechargeTime );
2231         savefile->WriteInt( rechargeSpeed );
2232         savefile->WriteFloat( new_g_damageScale );
2233
2234         savefile->WriteBool( bloomEnabled );
2235         savefile->WriteFloat( bloomSpeed );
2236         savefile->WriteFloat( bloomIntensity );
2237
2238 #endif
2239 }
2240
2241 /*
2242 ===========
2243 idPlayer::Restore
2244 ===========
2245 */
2246 void idPlayer::Restore( idRestoreGame *savefile ) {
2247         int       i;
2248         int       num;
2249         float set;
2250
2251         savefile->ReadUsercmd( usercmd );
2252         playerView.Restore( savefile );
2253
2254         savefile->ReadBool( noclip );
2255         savefile->ReadBool( godmode );
2256
2257         savefile->ReadAngles( spawnAngles );
2258         savefile->ReadAngles( viewAngles );
2259         savefile->ReadAngles( cmdAngles );
2260
2261         memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
2262         SetViewAngles( viewAngles );
2263         spawnAnglesSet = true;
2264
2265         savefile->ReadInt( buttonMask );
2266         savefile->ReadInt( oldButtons );
2267         savefile->ReadInt( oldFlags );
2268
2269         usercmd.flags = 0;
2270         oldFlags = 0;
2271
2272         savefile->ReadInt( lastHitTime );
2273         savefile->ReadInt( lastSndHitTime );
2274         savefile->ReadInt( lastSavingThrowTime );
2275
2276         // Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
2277         LinkScriptVariables();
2278
2279         inventory.Restore( savefile );
2280         weapon.Restore( savefile );
2281
2282         for ( i = 0; i < inventory.emails.Num(); i++ ) {
2283                 GetPDA()->AddEmail( inventory.emails[i] );
2284         }
2285
2286         savefile->ReadUserInterface( hud );
2287         savefile->ReadUserInterface( objectiveSystem );
2288         savefile->ReadBool( objectiveSystemOpen );
2289
2290         savefile->ReadInt( weapon_soulcube );
2291         savefile->ReadInt( weapon_pda );
2292         savefile->ReadInt( weapon_fists );
2293 #ifdef _D3XP
2294         savefile->ReadInt( weapon_bloodstone );
2295         savefile->ReadInt( weapon_bloodstone_active1 );
2296         savefile->ReadInt( weapon_bloodstone_active2 );
2297         savefile->ReadInt( weapon_bloodstone_active3 );
2298
2299         savefile->ReadBool( harvest_lock );
2300         savefile->ReadInt( hudPowerup );
2301         savefile->ReadInt( lastHudPowerup );
2302         savefile->ReadInt( hudPowerupDuration );
2303         
2304         
2305 #endif
2306
2307         savefile->ReadInt( heartRate );
2308
2309         savefile->ReadFloat( set );
2310         heartInfo.SetStartTime( set );
2311         savefile->ReadFloat( set );
2312         heartInfo.SetDuration( set );
2313         savefile->ReadFloat( set );
2314         heartInfo.SetStartValue( set );
2315         savefile->ReadFloat( set );
2316         heartInfo.SetEndValue( set );
2317
2318         savefile->ReadInt( lastHeartAdjust );
2319         savefile->ReadInt( lastHeartBeat );
2320         savefile->ReadInt( lastDmgTime );
2321         savefile->ReadInt( deathClearContentsTime );
2322         savefile->ReadBool( doingDeathSkin );
2323         savefile->ReadInt( lastArmorPulse );
2324         savefile->ReadFloat( stamina );
2325         savefile->ReadFloat( healthPool );
2326         savefile->ReadInt( nextHealthPulse );
2327         savefile->ReadBool( healthPulse );
2328         savefile->ReadInt( nextHealthTake );
2329         savefile->ReadBool( healthTake );
2330
2331         savefile->ReadBool( hiddenWeapon );
2332         soulCubeProjectile.Restore( savefile );
2333
2334         savefile->ReadInt( spectator );
2335         savefile->ReadVec3( colorBar );
2336         savefile->ReadInt( colorBarIndex );
2337         savefile->ReadBool( scoreBoardOpen );
2338         savefile->ReadBool( forceScoreBoard );
2339         savefile->ReadBool( forceRespawn );
2340         savefile->ReadBool( spectating );
2341         savefile->ReadInt( lastSpectateTeleport );
2342         savefile->ReadBool( lastHitToggle );
2343         savefile->ReadBool( forcedReady );
2344         savefile->ReadBool( wantSpectate );
2345         savefile->ReadBool( weaponGone );
2346         savefile->ReadBool( useInitialSpawns );
2347         savefile->ReadInt( latchedTeam );
2348         savefile->ReadInt( tourneyRank );
2349         savefile->ReadInt( tourneyLine );
2350
2351         teleportEntity.Restore( savefile );
2352         savefile->ReadInt( teleportKiller );
2353
2354         savefile->ReadInt( minRespawnTime );
2355         savefile->ReadInt( maxRespawnTime );
2356
2357         savefile->ReadVec3( firstPersonViewOrigin );
2358         savefile->ReadMat3( firstPersonViewAxis );
2359
2360         // don't bother saving dragEntity since it's a dev tool
2361         dragEntity.Clear();
2362
2363         savefile->ReadJoint( hipJoint );
2364         savefile->ReadJoint( chestJoint );
2365         savefile->ReadJoint( headJoint );
2366
2367         savefile->ReadStaticObject( physicsObj );
2368         RestorePhysics( &physicsObj );
2369
2370         savefile->ReadInt( num );
2371         aasLocation.SetGranularity( 1 );
2372         aasLocation.SetNum( num );
2373         for( i = 0; i < num; i++ ) {
2374                 savefile->ReadInt( aasLocation[ i ].areaNum );
2375                 savefile->ReadVec3( aasLocation[ i ].pos );
2376         }
2377
2378         savefile->ReadInt( bobFoot );
2379         savefile->ReadFloat( bobFrac );
2380         savefile->ReadFloat( bobfracsin );
2381         savefile->ReadInt( bobCycle );
2382         savefile->ReadFloat( xyspeed );
2383         savefile->ReadInt( stepUpTime );
2384         savefile->ReadFloat( stepUpDelta );
2385         savefile->ReadFloat( idealLegsYaw );
2386         savefile->ReadFloat( legsYaw );
2387         savefile->ReadBool( legsForward );
2388         savefile->ReadFloat( oldViewYaw );
2389         savefile->ReadAngles( viewBobAngles );
2390         savefile->ReadVec3( viewBob );
2391         savefile->ReadInt( landChange );
2392         savefile->ReadInt( landTime );
2393
2394         savefile->ReadInt( currentWeapon );
2395         savefile->ReadInt( idealWeapon );
2396         savefile->ReadInt( previousWeapon );
2397         savefile->ReadInt( weaponSwitchTime );
2398         savefile->ReadBool( weaponEnabled );
2399         savefile->ReadBool( showWeaponViewModel );
2400
2401         savefile->ReadSkin( skin );
2402         savefile->ReadSkin( powerUpSkin );
2403         savefile->ReadString( baseSkinName );
2404
2405         savefile->ReadInt( numProjectilesFired );
2406         savefile->ReadInt( numProjectileHits );
2407
2408         savefile->ReadBool( airless );
2409         savefile->ReadInt( airTics );
2410         savefile->ReadInt( lastAirDamage );
2411
2412         savefile->ReadBool( gibDeath );
2413         savefile->ReadBool( gibsLaunched );
2414         savefile->ReadVec3( gibsDir );
2415
2416         savefile->ReadFloat( set );
2417         zoomFov.SetStartTime( set );
2418         savefile->ReadFloat( set );
2419         zoomFov.SetDuration( set );
2420         savefile->ReadFloat( set );
2421         zoomFov.SetStartValue( set );
2422         savefile->ReadFloat( set );
2423         zoomFov.SetEndValue( set );
2424
2425         savefile->ReadFloat( set );
2426         centerView.SetStartTime( set );
2427         savefile->ReadFloat( set );
2428         centerView.SetDuration( set );
2429         savefile->ReadFloat( set );
2430         centerView.SetStartValue( set );
2431         savefile->ReadFloat( set );
2432         centerView.SetEndValue( set );
2433
2434         savefile->ReadBool( fxFov );
2435
2436         savefile->ReadFloat( influenceFov );
2437         savefile->ReadInt( influenceActive );
2438         savefile->ReadFloat( influenceRadius );
2439         savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
2440         savefile->ReadMaterial( influenceMaterial );
2441         savefile->ReadSkin( influenceSkin );
2442
2443         savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
2444
2445         for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
2446                 savefile->ReadAngles( loggedViewAngles[ i ] );
2447         }
2448         for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
2449                 savefile->ReadInt( loggedAccel[ i ].time );
2450                 savefile->ReadVec3( loggedAccel[ i ].dir );
2451         }
2452         savefile->ReadInt( currentLoggedAccel );
2453
2454         savefile->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
2455         // can't save focusUI
2456         focusUI = NULL;
2457         savefile->ReadObject( reinterpret_cast<idClass *&>( focusCharacter ) );
2458         savefile->ReadInt( talkCursor );
2459         savefile->ReadInt( focusTime );
2460         savefile->ReadObject( reinterpret_cast<idClass *&>( focusVehicle ) );
2461         savefile->ReadUserInterface( cursor );
2462
2463         savefile->ReadInt( oldMouseX );
2464         savefile->ReadInt( oldMouseY );
2465
2466         savefile->ReadString( pdaAudio );
2467         savefile->ReadString( pdaVideo );
2468         savefile->ReadString( pdaVideoWave );
2469
2470         savefile->ReadBool( tipUp );
2471         savefile->ReadBool( objectiveUp );
2472
2473         savefile->ReadInt( lastDamageDef );
2474         savefile->ReadVec3( lastDamageDir );
2475         savefile->ReadInt( lastDamageLocation );
2476         savefile->ReadInt( smoothedFrame );
2477         savefile->ReadBool( smoothedOriginUpdated );
2478         savefile->ReadVec3( smoothedOrigin );
2479         savefile->ReadAngles( smoothedAngles );
2480
2481         savefile->ReadBool( ready );
2482         savefile->ReadBool( respawning );
2483         savefile->ReadBool( leader );
2484         savefile->ReadInt( lastSpectateChange );
2485         savefile->ReadInt( lastTeleFX );
2486
2487         // set the pm_ cvars
2488         const idKeyValue        *kv;
2489         kv = spawnArgs.MatchPrefix( "pm_", NULL );
2490         while( kv ) {
2491                 cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
2492                 kv = spawnArgs.MatchPrefix( "pm_", kv );
2493         }
2494
2495         savefile->ReadFloat( set );
2496         pm_stamina.SetFloat( set );
2497
2498         // create combat collision hull for exact collision detection
2499         SetCombatModel();
2500
2501 #ifdef _D3XP
2502         int weaponToggleCount;
2503         savefile->ReadInt(weaponToggleCount);
2504         for(i = 0; i < weaponToggleCount; i++) {
2505                 WeaponToggle_t newToggle;
2506                 memset(&newToggle, 0, sizeof(newToggle));
2507
2508                 idStr name;
2509                 savefile->ReadString(name);
2510                 strcpy(newToggle.name, name.c_str());
2511
2512                 int indexCount;
2513                 savefile->ReadInt(indexCount);
2514                 for(int j = 0; j < indexCount; j++) {
2515                         int temp;
2516                         savefile->ReadInt(temp);
2517                         newToggle.toggleList.Append(temp);
2518                 }
2519                 weaponToggles.Set(newToggle.name, newToggle);
2520         }
2521         savefile->ReadObject(reinterpret_cast<idClass *&>(mountedObject));
2522         enviroSuitLight.Restore( savefile );
2523         savefile->ReadBool( healthRecharge );
2524         savefile->ReadInt( lastHealthRechargeTime );
2525         savefile->ReadInt( rechargeSpeed );
2526         savefile->ReadFloat( new_g_damageScale );
2527
2528         savefile->ReadBool( bloomEnabled );
2529         savefile->ReadFloat( bloomSpeed );
2530         savefile->ReadFloat( bloomIntensity );
2531 #endif
2532 }
2533
2534 /*
2535 ===============
2536 idPlayer::PrepareForRestart
2537 ================
2538 */
2539 void idPlayer::PrepareForRestart( void ) {
2540         ClearPowerUps();
2541         Spectate( true );
2542         forceRespawn = true;
2543         
2544 #ifdef CTF
2545         // Confirm reset hud states
2546     DropFlag();
2547
2548         if ( hud ) {
2549                 hud->SetStateInt( "red_flagstatus", 0 );
2550                 hud->SetStateInt( "blue_flagstatus", 0 );
2551         }
2552 #endif
2553         
2554         // we will be restarting program, clear the client entities from program-related things first
2555         ShutdownThreads();
2556
2557         // the sound world is going to be cleared, don't keep references to emitters
2558         FreeSoundEmitter( false );
2559 }
2560
2561 /*
2562 ===============
2563 idPlayer::Restart
2564 ================
2565 */
2566 void idPlayer::Restart( void ) {
2567         idActor::Restart();
2568         
2569         // client needs to setup the animation script object again
2570         if ( gameLocal.isClient ) {
2571                 Init();
2572         } else {
2573                 // choose a random spot and prepare the point of view in case player is left spectating
2574                 assert( spectating );
2575                 SpawnFromSpawnSpot();
2576         }
2577
2578         useInitialSpawns = true;
2579         UpdateSkinSetup( true );
2580 }
2581
2582 /*
2583 ===============
2584 idPlayer::ServerSpectate
2585 ================
2586 */
2587 void idPlayer::ServerSpectate( bool spectate ) {
2588         assert( !gameLocal.isClient );
2589
2590         if ( spectating != spectate ) {
2591                 Spectate( spectate );
2592                 if ( spectate ) {
2593                         SetSpectateOrigin();
2594                 } else {
2595                         if ( gameLocal.gameType == GAME_DM ) {
2596                                 // make sure the scores are reset so you can't exploit by spectating and entering the game back
2597                                 // other game types don't matter, as you either can't join back, or it's team scores
2598                                 gameLocal.mpGame.ClearFrags( entityNumber );
2599                         }
2600                 }
2601         }
2602         if ( !spectate ) {
2603                 SpawnFromSpawnSpot();
2604         }
2605 #ifdef CTF
2606         // drop the flag if player was carrying it
2607         if ( spectate && gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() && 
2608                  carryingFlag )
2609         {
2610                 DropFlag();
2611         }
2612 #endif
2613 }
2614
2615 /*
2616 ===========
2617 idPlayer::SelectInitialSpawnPoint
2618
2619 Try to find a spawn point marked 'initial', otherwise
2620 use normal spawn selection.
2621 ============
2622 */
2623 void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
2624         idEntity *spot;
2625         idStr skin;
2626
2627         spot = gameLocal.SelectInitialSpawnPoint( this );
2628
2629         // set the player skin from the spawn location
2630         if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
2631                 spawnArgs.Set( "spawn_skin", skin );
2632         }
2633
2634         // activate the spawn locations targets
2635         spot->PostEventMS( &EV_ActivateTargets, 0, this );
2636
2637         origin = spot->GetPhysics()->GetOrigin();
2638         origin[2] += 4.0f + CM_BOX_EPSILON;             // move up to make sure the player is at least an epsilon above the floor
2639         angles = spot->GetPhysics()->GetAxis().ToAngles();
2640 }
2641
2642 /*
2643 ===========
2644 idPlayer::SpawnFromSpawnSpot
2645
2646 Chooses a spawn location and spawns the player
2647 ============
2648 */
2649 void idPlayer::SpawnFromSpawnSpot( void ) {
2650         idVec3          spawn_origin;
2651         idAngles        spawn_angles;
2652         
2653         SelectInitialSpawnPoint( spawn_origin, spawn_angles );
2654         SpawnToPoint( spawn_origin, spawn_angles );
2655 }
2656
2657 /*
2658 ===========
2659 idPlayer::SpawnToPoint
2660
2661 Called every time a client is placed fresh in the world:
2662 after the first ClientBegin, and after each respawn
2663 Initializes all non-persistant parts of playerState
2664
2665 when called here with spectating set to true, just place yourself and init
2666 ============
2667 */
2668 void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
2669         idVec3 spec_origin;
2670
2671         assert( !gameLocal.isClient );
2672
2673         respawning = true;
2674
2675         Init();
2676
2677         fl.noknockback = false;
2678
2679         // stop any ragdolls being used
2680         StopRagdoll();
2681
2682         // set back the player physics
2683         SetPhysics( &physicsObj );
2684
2685         physicsObj.SetClipModelAxis();
2686         physicsObj.EnableClip();
2687
2688         if ( !spectating ) {
2689                 SetCombatContents( true );
2690         }
2691
2692         physicsObj.SetLinearVelocity( vec3_origin );
2693
2694         // setup our initial view
2695         if ( !spectating ) {
2696                 SetOrigin( spawn_origin );
2697         } else {
2698                 spec_origin = spawn_origin;
2699                 spec_origin[ 2 ] += pm_normalheight.GetFloat();
2700                 spec_origin[ 2 ] += SPECTATE_RAISE;
2701                 SetOrigin( spec_origin );
2702         }
2703
2704         // if this is the first spawn of the map, we don't have a usercmd yet,
2705         // so the delta angles won't be correct.  This will be fixed on the first think.
2706         viewAngles = ang_zero;
2707         SetDeltaViewAngles( ang_zero );
2708         SetViewAngles( spawn_angles );
2709         spawnAngles = spawn_angles;
2710         spawnAnglesSet = false;
2711
2712         legsForward = true;
2713         legsYaw = 0.0f;
2714         idealLegsYaw = 0.0f;
2715         oldViewYaw = viewAngles.yaw;
2716
2717         if ( spectating ) {
2718                 Hide();
2719         } else {
2720                 Show();
2721         }
2722
2723         if ( gameLocal.isMultiplayer ) {
2724                 if ( !spectating ) {
2725                         // we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
2726                         if ( lastTeleFX < gameLocal.time - 1000 ) {
2727                                 idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
2728                                 lastTeleFX = gameLocal.time;
2729                         }
2730                 }
2731                 AI_TELEPORT = true;
2732         } else {
2733                 AI_TELEPORT = false;
2734         }
2735
2736         // kill anything at the new position
2737         if ( !spectating ) {
2738                 physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
2739                 gameLocal.KillBox( this );
2740         }
2741
2742         // don't allow full run speed for a bit
2743         physicsObj.SetKnockBack( 100 );
2744
2745         // set our respawn time and buttons so that if we're killed we don't respawn immediately
2746         minRespawnTime = gameLocal.time;
2747         maxRespawnTime = gameLocal.time;
2748         if ( !spectating ) {
2749                 forceRespawn = false;
2750         }
2751
2752         privateCameraView = NULL;
2753
2754         BecomeActive( TH_THINK );
2755
2756         // run a client frame to drop exactly to the floor,
2757         // initialize animations and other things
2758         Think();
2759
2760         respawning                      = false;
2761         lastManOver                     = false;
2762         lastManPlayAgain        = false;
2763         isTelefragged           = false;
2764 }
2765
2766 /*
2767 ===============
2768 idPlayer::SavePersistantInfo
2769
2770 Saves any inventory and player stats when changing levels.
2771 ===============
2772 */
2773 void idPlayer::SavePersistantInfo( void ) {
2774         idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
2775
2776         playerInfo.Clear();
2777         inventory.GetPersistantData( playerInfo );
2778         playerInfo.SetInt( "health", health );
2779         playerInfo.SetInt( "current_weapon", currentWeapon );
2780 }
2781
2782 /*
2783 ===============
2784 idPlayer::RestorePersistantInfo
2785
2786 Restores any inventory and player stats when changing levels.
2787 ===============
2788 */
2789 void idPlayer::RestorePersistantInfo( void ) {
2790         if ( gameLocal.isMultiplayer ) {
2791                 gameLocal.persistentPlayerInfo[entityNumber].Clear();
2792         }
2793
2794         spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
2795
2796         inventory.RestoreInventory( this, spawnArgs );
2797         health = spawnArgs.GetInt( "health", "100" );
2798         if ( !gameLocal.isClient ) {
2799                 idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
2800         }
2801 }
2802
2803 /*
2804 ================
2805 idPlayer::GetUserInfo
2806 ================
2807 */
2808 idDict *idPlayer::GetUserInfo( void ) {
2809         return &gameLocal.userInfo[ entityNumber ];
2810 }
2811
2812 /*
2813 ==============
2814 idPlayer::UpdateSkinSetup
2815 ==============
2816 */
2817 void idPlayer::UpdateSkinSetup( bool restart ) {
2818         if ( restart ) {
2819                 team = ( idStr::Icmp( GetUserInfo()->GetString( "ui_team" ), "Blue" ) == 0 );
2820         }
2821         if ( gameLocal.mpGame.IsGametypeTeamBased() ) { /* CTF */
2822                 if ( team ) {
2823                         baseSkinName = "skins/characters/player/marine_mp_blue";
2824                 } else {
2825                         baseSkinName = "skins/characters/player/marine_mp_red";
2826                 }
2827                 if ( !gameLocal.isClient && team != latchedTeam ) {
2828                         gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
2829                 }
2830                 latchedTeam = team;
2831         } else {
2832                 baseSkinName = GetUserInfo()->GetString( "ui_skin" );
2833         }
2834         if ( !baseSkinName.Length() ) {
2835                 baseSkinName = "skins/characters/player/marine_mp";
2836         }
2837         skin = declManager->FindSkin( baseSkinName, false );
2838         assert( skin );
2839         // match the skin to a color band for scoreboard
2840         if ( baseSkinName.Find( "red" ) != -1 ) {
2841                 colorBarIndex = 1;
2842         } else if ( baseSkinName.Find( "green" ) != -1 ) {
2843                 colorBarIndex = 2;
2844         } else if ( baseSkinName.Find( "blue" ) != -1 ) {
2845                 colorBarIndex = 3;
2846         } else if ( baseSkinName.Find( "yellow" ) != -1 ) {
2847                 colorBarIndex = 4;
2848         } else if ( baseSkinName.Find( "grey" ) != -1 ) {
2849                 colorBarIndex = 5;
2850         } else if ( baseSkinName.Find( "purple" ) != -1 ) {
2851                 colorBarIndex = 6;
2852         } else if ( baseSkinName.Find( "orange" ) != -1 ) {
2853                 colorBarIndex = 7;
2854         } else {
2855                 colorBarIndex = 0;
2856         }
2857         colorBar = colorBarTable[ colorBarIndex ];
2858         if ( PowerUpActive( BERSERK ) ) {
2859                 powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
2860         }
2861 #ifdef _D3XP
2862         else if ( PowerUpActive( INVULNERABILITY ) ) {
2863                 powerUpSkin = declManager->FindSkin( baseSkinName + "_invuln" );
2864         //} else if ( PowerUpActive( HASTE ) ) {
2865         //      powerUpSkin = declManager->FindSkin( baseSkinName + "_haste" );
2866         }
2867 #endif
2868 }
2869
2870 /*
2871 ==============
2872 idPlayer::BalanceTDM
2873 ==============
2874 */
2875 bool idPlayer::BalanceTDM( void ) {
2876         int                     i, balanceTeam, teamCount[2];
2877         idEntity        *ent;
2878
2879         teamCount[ 0 ] = teamCount[ 1 ] = 0;
2880         for( i = 0; i < gameLocal.numClients; i++ ) {
2881                 ent = gameLocal.entities[ i ];
2882                 if ( ent && ent->IsType( idPlayer::Type ) ) {
2883                         teamCount[ static_cast< idPlayer * >( ent )->team ]++;
2884                 }
2885         }
2886         balanceTeam = -1;
2887         if ( teamCount[ 0 ] < teamCount[ 1 ] ) {
2888                 balanceTeam = 0;
2889         } else if ( teamCount[ 0 ] > teamCount[ 1 ] ) {
2890                 balanceTeam = 1;
2891         }
2892         if ( balanceTeam != -1 && team != balanceTeam ) {
2893                 common->DPrintf( "team balance: forcing player %d to %s team\n", entityNumber, balanceTeam ? "blue" : "red" );
2894                 team = balanceTeam;
2895                 GetUserInfo()->Set( "ui_team", team ? "Blue" : "Red" );
2896                 return true;
2897         }
2898         return false;
2899 }
2900
2901 /*
2902 ==============
2903 idPlayer::UserInfoChanged
2904 ==============
2905 */
2906 bool idPlayer::UserInfoChanged( bool canModify ) {
2907         idDict  *userInfo;
2908         bool    modifiedInfo;
2909         bool    spec;
2910         bool    newready;
2911
2912         userInfo = GetUserInfo();
2913         showWeaponViewModel = userInfo->GetBool( "ui_showGun" );
2914
2915         if ( !gameLocal.isMultiplayer ) {
2916                 return false;
2917         }
2918
2919         modifiedInfo = false;
2920
2921         spec = ( idStr::Icmp( userInfo->GetString( "ui_spectate" ), "Spectate" ) == 0 );
2922         if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
2923                 // never let spectators go back to game while sudden death is on
2924                 if ( canModify && gameLocal.mpGame.GetGameState() == idMultiplayerGame::SUDDENDEATH && !spec && wantSpectate == true ) {
2925                         userInfo->Set( "ui_spectate", "Spectate" );
2926                         modifiedInfo |= true;
2927                 } else {
2928                         if ( spec != wantSpectate && !spec ) {
2929                                 // returning from spectate, set forceRespawn so we don't get stuck in spectate forever
2930                                 forceRespawn = true;
2931                         }
2932                         wantSpectate = spec;
2933                 }
2934         } else {
2935                 if ( canModify && spec ) {
2936                         userInfo->Set( "ui_spectate", "Play" );
2937                         modifiedInfo |= true;
2938                 } else if ( spectating ) {  
2939                         // allow player to leaving spectator mode if they were in it when si_spectators got turned off
2940                         forceRespawn = true;
2941                 }
2942                 wantSpectate = false;
2943         }
2944
2945         newready = ( idStr::Icmp( userInfo->GetString( "ui_ready" ), "Ready" ) == 0 );
2946         if ( ready != newready && gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP && !wantSpectate ) {
2947                 gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_07180" ), userInfo->GetString( "ui_name" ), newready ? common->GetLanguageDict()->GetString( "#str_04300" ) : common->GetLanguageDict()->GetString( "#str_04301" ) );
2948         }
2949         ready = newready;
2950         team = ( idStr::Icmp( userInfo->GetString( "ui_team" ), "Blue" ) == 0 );
2951         // server maintains TDM balance
2952         if ( canModify && gameLocal.mpGame.IsGametypeTeamBased() && !gameLocal.mpGame.IsInGame( entityNumber ) && g_balanceTDM.GetBool() ) { /* CTF */
2953                 modifiedInfo |= BalanceTDM( );
2954         }
2955         UpdateSkinSetup( false );
2956
2957         isChatting = userInfo->GetBool( "ui_chat", "0" );
2958         if ( canModify && isChatting && AI_DEAD ) {
2959                 // if dead, always force chat icon off.
2960                 isChatting = false;
2961                 userInfo->SetBool( "ui_chat", false );
2962                 modifiedInfo |= true;
2963         }
2964
2965         return modifiedInfo;
2966 }
2967
2968 /*
2969 ===============
2970 idPlayer::UpdateHudAmmo
2971 ===============
2972 */
2973 void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
2974         int inclip;
2975         int ammoamount;
2976
2977         assert( weapon.GetEntity() );
2978         assert( _hud );
2979
2980         inclip          = weapon.GetEntity()->AmmoInClip();
2981         ammoamount      = weapon.GetEntity()->AmmoAvailable();
2982
2983 #ifdef _D3XP
2984         //Hack to stop the bloodstone ammo to display when it is being activated
2985         if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() || currentWeapon == weapon_bloodstone) {
2986 #else
2987         if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() ) {
2988 #endif
2989                 // show infinite ammo
2990                 _hud->SetStateString( "player_ammo", "" );
2991                 _hud->SetStateString( "player_totalammo", "" );
2992         } else { 
2993                 // show remaining ammo
2994 #ifdef _D3XP
2995                 _hud->SetStateString( "player_totalammo", va( "%i", ammoamount ) );
2996 #else
2997                 _hud->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip ) );
2998 #endif
2999                 _hud->SetStateString( "player_ammo", weapon.GetEntity()->ClipSize() ? va( "%i", inclip ) : "--" );              // how much in the current clip
3000                 _hud->SetStateString( "player_clips", weapon.GetEntity()->ClipSize() ? va( "%i", ammoamount / weapon.GetEntity()->ClipSize() ) : "--" );
3001
3002 #ifdef _D3XP
3003                 _hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount ) );
3004 #else
3005                 _hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
3006 #endif
3007         } 
3008
3009         _hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
3010         _hud->SetStateBool( "player_clip_empty", ( weapon.GetEntity()->ClipSize() ? inclip == 0 : false ) );
3011         _hud->SetStateBool( "player_clip_low", ( weapon.GetEntity()->ClipSize() ? inclip <= weapon.GetEntity()->LowAmmo() : false ) );
3012
3013 #ifdef _D3XP
3014         //Hack to stop the bloodstone ammo to display when it is being activated
3015         if(currentWeapon == weapon_bloodstone) {
3016                 _hud->SetStateBool( "player_ammo_empty", false );
3017                 _hud->SetStateBool( "player_clip_empty", false );
3018                 _hud->SetStateBool( "player_clip_low", false );
3019         }
3020 #endif
3021
3022 #ifdef _D3XP
3023         //Let the HUD know the total amount of ammo regardless of the ammo required value
3024         _hud->SetStateString( "player_ammo_count", va("%i", weapon.GetEntity()->AmmoCount()));
3025 #endif
3026
3027 #ifdef _D3XP
3028         //Make sure the hud always knows how many bloodstone charges there are
3029         int ammoRequired;
3030         ammo_t ammo_i = inventory.AmmoIndexForWeaponClass( "weapon_bloodstone_passive", &ammoRequired );
3031         int bloodstoneAmmo = inventory.HasAmmo( ammo_i, ammoRequired );
3032         _hud->SetStateString("player_bloodstone_ammo", va("%i", bloodstoneAmmo));
3033         _hud->HandleNamedEvent( "bloodstoneAmmoUpdate" );
3034 #endif
3035
3036         _hud->HandleNamedEvent( "updateAmmo" );
3037 }
3038
3039 /*
3040 ===============
3041 idPlayer::UpdateHudStats
3042 ===============
3043 */
3044 void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
3045         int staminapercentage;
3046         float max_stamina;
3047
3048         assert( _hud );
3049
3050         max_stamina = pm_stamina.GetFloat();
3051         if ( !max_stamina ) {
3052                 // stamina disabled, so show full stamina bar
3053                 staminapercentage = 100.0f;
3054         } else {
3055                 staminapercentage = idMath::FtoiFast( 100.0f * stamina / max_stamina );
3056         }
3057
3058         _hud->SetStateInt( "player_health", health );
3059         _hud->SetStateInt( "player_stamina", staminapercentage );
3060         _hud->SetStateInt( "player_armor", inventory.armor );
3061         _hud->SetStateInt( "player_hr", heartRate );
3062         
3063         _hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 );
3064
3065         _hud->HandleNamedEvent( "updateArmorHealthAir" );
3066
3067 #ifdef _D3XP
3068         _hud->HandleNamedEvent( "updatePowerup" );
3069 #endif
3070
3071         if ( healthPulse ) {
3072                 _hud->HandleNamedEvent( "healthPulse" );
3073                 StartSound( "snd_healthpulse", SND_CHANNEL_ITEM, 0, false, NULL );
3074                 healthPulse = false;
3075         }
3076
3077         if ( healthTake ) {
3078                 _hud->HandleNamedEvent( "healthPulse" );
3079                 StartSound( "snd_healthtake", SND_CHANNEL_ITEM, 0, false, NULL );
3080                 healthTake = false;
3081         }
3082
3083         if ( inventory.ammoPulse ) { 
3084                 _hud->HandleNamedEvent( "ammoPulse" );
3085                 inventory.ammoPulse = false;
3086         }
3087         if ( inventory.weaponPulse ) {
3088                 // We need to update the weapon hud manually, but not
3089                 // the armor/ammo/health because they are updated every
3090                 // frame no matter what
3091                 UpdateHudWeapon();
3092                 _hud->HandleNamedEvent( "weaponPulse" );
3093                 inventory.weaponPulse = false;
3094         }
3095         if ( inventory.armorPulse ) { 
3096                 _hud->HandleNamedEvent( "armorPulse" );
3097                 inventory.armorPulse = false;
3098         }
3099
3100 #ifdef CTF
3101     if ( gameLocal.mpGame.IsGametypeFlagBased() && _hud )
3102     {
3103                 _hud->SetStateInt( "red_flagstatus", gameLocal.mpGame.GetFlagStatus( 0 ) );
3104                 _hud->SetStateInt( "blue_flagstatus", gameLocal.mpGame.GetFlagStatus( 1 ) );
3105
3106                 _hud->SetStateInt( "red_team_score",  gameLocal.mpGame.GetFlagPoints( 0 ) );
3107                 _hud->SetStateInt( "blue_team_score", gameLocal.mpGame.GetFlagPoints( 1 ) );
3108
3109         _hud->HandleNamedEvent( "RedFlagStatusChange" );
3110         _hud->HandleNamedEvent( "BlueFlagStatusChange" );
3111     }
3112     
3113     _hud->HandleNamedEvent( "selfTeam" );
3114     
3115 #endif
3116     
3117
3118         UpdateHudAmmo( _hud );
3119 }
3120
3121 /*
3122 ===============
3123 idPlayer::UpdateHudWeapon
3124 ===============
3125 */
3126 void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
3127         idUserInterface *hud = idPlayer::hud;
3128
3129         // if updating the hud of a followed client
3130         if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
3131                 idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
3132                 if ( p->spectating && p->spectator == entityNumber ) {
3133                         assert( p->hud );
3134                         hud = p->hud;
3135                 }
3136         }
3137
3138         if ( !hud ) {
3139                 return;
3140         }
3141
3142         for ( int i = 0; i < MAX_WEAPONS; i++ ) {
3143                 const char *weapnum = va( "def_weapon%d", i );
3144                 const char *hudWeap = va( "weapon%d", i );
3145                 int weapstate = 0;
3146                 if ( inventory.weapons & ( 1 << i ) ) {
3147                         const char *weap = spawnArgs.GetString( weapnum );
3148                         if ( weap && *weap ) {
3149                                 weapstate++;
3150                         }
3151                         if ( idealWeapon == i ) {
3152                                 weapstate++;
3153                         }
3154                 }
3155                 hud->SetStateInt( hudWeap, weapstate );
3156         }
3157         if ( flashWeapon ) {
3158
3159 /*#ifdef _D3XP
3160                 //Clear all hud weapon varaibles for the weapon change
3161                 hud->SetStateString( "player_ammo", "" );
3162                 hud->SetStateString( "player_totalammo", "" );
3163                 hud->SetStateString( "player_clips", "" );
3164                 hud->SetStateString( "player_allammo", "" );
3165                 hud->SetStateBool( "player_ammo_empty", false );
3166                 hud->SetStateBool( "player_clip_empty", false );
3167                 hud->SetStateBool( "player_clip_low", false );
3168                 hud->SetStateString( "player_ammo_count", "");
3169 #endif*/
3170
3171                 hud->HandleNamedEvent( "weaponChange" );
3172         }
3173 }
3174
3175 /*
3176 ===============
3177 idPlayer::DrawHUD
3178 ===============
3179 */
3180 void idPlayer::DrawHUD( idUserInterface *_hud ) {
3181
3182         if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !_hud || !g_showHud.GetBool() ) {
3183                 return;
3184         }
3185
3186         UpdateHudStats( _hud );
3187
3188         _hud->SetStateString( "weapicon", weapon.GetEntity()->Icon() );
3189
3190         // FIXME: this is temp to allow the sound meter to show up in the hud
3191         // it should be commented out before shipping but the code can remain
3192         // for mod developers to enable for the same functionality
3193         _hud->SetStateInt( "s_debug", cvarSystem->GetCVarInteger( "s_showLevelMeter" ) );
3194
3195         weapon.GetEntity()->UpdateGUI();
3196
3197         _hud->Redraw( gameLocal.realClientTime );
3198
3199         // weapon targeting crosshair
3200         if ( !GuiActive() ) {
3201                 if ( cursor && weapon.GetEntity()->ShowCrosshair() ) {
3202
3203 #ifdef _D3XP
3204                         if ( weapon.GetEntity()->GetGrabberState() == 1 || weapon.GetEntity()->GetGrabberState() == 2 ) {
3205                                 cursor->SetStateString( "grabbercursor", "1" );
3206                                 cursor->SetStateString( "combatcursor", "0" );
3207                         } else {
3208                                 cursor->SetStateString( "grabbercursor", "0" );
3209                                 cursor->SetStateString( "combatcursor", "1" );
3210                         }
3211 #endif
3212
3213                         cursor->Redraw( gameLocal.realClientTime );
3214                 }
3215         }
3216 }
3217
3218 /*
3219 ===============
3220 idPlayer::EnterCinematic
3221 ===============
3222 */
3223 void idPlayer::EnterCinematic( void ) {
3224 #ifdef _D3XP
3225         if ( PowerUpActive( HELLTIME ) ) {
3226                 StopHelltime();
3227         }
3228 #endif
3229
3230         Hide();
3231         StopAudioLog();
3232         StopSound( SND_CHANNEL_PDA, false );
3233         if ( hud ) {
3234                 hud->HandleNamedEvent( "radioChatterDown" );
3235         }
3236         
3237         physicsObj.SetLinearVelocity( vec3_origin );
3238         
3239         SetState( "EnterCinematic" );
3240         UpdateScript();
3241
3242         if ( weaponEnabled && weapon.GetEntity() ) {
3243                 weapon.GetEntity()->EnterCinematic();
3244         }
3245
3246         AI_FORWARD              = false;
3247         AI_BACKWARD             = false;
3248         AI_STRAFE_LEFT  = false;
3249         AI_STRAFE_RIGHT = false;
3250         AI_RUN                  = false;
3251         AI_ATTACK_HELD  = false;
3252         AI_WEAPON_FIRED = false;
3253         AI_JUMP                 = false;
3254         AI_CROUCH               = false;
3255         AI_ONGROUND             = true;
3256         AI_ONLADDER             = false;
3257         AI_DEAD                 = ( health <= 0 );
3258         AI_RUN                  = false;
3259         AI_PAIN                 = false;
3260         AI_HARDLANDING  = false;
3261         AI_SOFTLANDING  = false;
3262         AI_RELOAD               = false;
3263         AI_TELEPORT             = false;
3264         AI_TURN_LEFT    = false;
3265         AI_TURN_RIGHT   = false;
3266 }
3267
3268 /*
3269 ===============
3270 idPlayer::ExitCinematic
3271 ===============
3272 */
3273 void idPlayer::ExitCinematic( void ) {
3274         Show();
3275
3276         if ( weaponEnabled && weapon.GetEntity() ) {
3277                 weapon.GetEntity()->ExitCinematic();
3278         }
3279
3280         SetState( "ExitCinematic" );
3281         UpdateScript();
3282 }
3283
3284 /*
3285 =====================
3286 idPlayer::UpdateConditions
3287 =====================
3288 */
3289 void idPlayer::UpdateConditions( void ) {
3290         idVec3  velocity;
3291         float   fallspeed;
3292         float   forwardspeed;
3293         float   sidespeed;
3294
3295         // minus the push velocity to avoid playing the walking animation and sounds when riding a mover
3296         velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
3297         fallspeed = velocity * physicsObj.GetGravityNormal();
3298
3299         if ( influenceActive ) {
3300                 AI_FORWARD              = false;
3301                 AI_BACKWARD             = false;
3302                 AI_STRAFE_LEFT  = false;
3303                 AI_STRAFE_RIGHT = false;
3304         } else if ( gameLocal.time - lastDmgTime < 500 ) {
3305                 forwardspeed = velocity * viewAxis[ 0 ];
3306                 sidespeed = velocity * viewAxis[ 1 ];
3307                 AI_FORWARD              = AI_ONGROUND && ( forwardspeed > 20.01f );
3308                 AI_BACKWARD             = AI_ONGROUND && ( forwardspeed < -20.01f );
3309                 AI_STRAFE_LEFT  = AI_ONGROUND && ( sidespeed > 20.01f );
3310                 AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
3311         } else if ( xyspeed > MIN_BOB_SPEED ) {
3312                 AI_FORWARD              = AI_ONGROUND && ( usercmd.forwardmove > 0 );
3313                 AI_BACKWARD             = AI_ONGROUND && ( usercmd.forwardmove < 0 );
3314                 AI_STRAFE_LEFT  = AI_ONGROUND && ( usercmd.rightmove < 0 );
3315                 AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
3316         } else {
3317                 AI_FORWARD              = false;
3318                 AI_BACKWARD             = false;
3319                 AI_STRAFE_LEFT  = false;
3320                 AI_STRAFE_RIGHT = false;
3321         }
3322
3323         AI_RUN                  = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
3324         AI_DEAD                 = ( health <= 0 );
3325 }
3326
3327 /*
3328 ==================
3329 WeaponFireFeedback
3330
3331 Called when a weapon fires, generates head twitches, etc
3332 ==================
3333 */
3334 void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
3335         // force a blink
3336         blink_time = 0;
3337
3338         // play the fire animation
3339         AI_WEAPON_FIRED = true;
3340
3341         // update view feedback
3342         playerView.WeaponFireFeedback( weaponDef );
3343 }
3344
3345 /*
3346 ===============
3347 idPlayer::StopFiring
3348 ===============
3349 */
3350 void idPlayer::StopFiring( void ) {
3351         AI_ATTACK_HELD  = false;
3352         AI_WEAPON_FIRED = false;
3353         AI_RELOAD               = false;
3354         if ( weapon.GetEntity() ) {
3355                 weapon.GetEntity()->EndAttack();
3356         }
3357 }
3358
3359 /*
3360 ===============
3361 idPlayer::FireWeapon
3362 ===============
3363 */
3364 void idPlayer::FireWeapon( void ) {
3365         idMat3 axis;
3366         idVec3 muzzle;
3367
3368         if ( privateCameraView ) {
3369                 return;
3370         }
3371
3372         if ( g_editEntityMode.GetInteger() ) {
3373                 GetViewPos( muzzle, axis );
3374                 if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
3375                         return;
3376                 }
3377         }
3378
3379         if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
3380                 if ( weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
3381                         AI_ATTACK_HELD = true;
3382                         weapon.GetEntity()->BeginAttack();
3383                         if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
3384                                 if ( hud ) {
3385                                         hud->HandleNamedEvent( "soulCubeNotReady" );
3386                                 }
3387                                 SelectWeapon( previousWeapon, false );
3388                         }
3389 #ifdef _D3XP
3390                         if( (weapon_bloodstone >= 0) && (currentWeapon == weapon_bloodstone) && inventory.weapons & ( 1 << weapon_bloodstone_active1 ) && weapon.GetEntity()->GetStatus() == WP_READY) {
3391                                 // tell it to switch to the previous weapon. Only do this once to prevent
3392                                 // weapon toggling messing up the previous weapon
3393                                 if(idealWeapon == weapon_bloodstone) {
3394                                         if(previousWeapon == weapon_bloodstone || previousWeapon == -1) {
3395                                                 NextBestWeapon();
3396                                         } else {
3397                                                 //Since this is a toggle weapon just select itself and it will toggle to the last weapon
3398                                                 SelectWeapon( weapon_bloodstone, false );
3399                                         }
3400                                 }
3401                         }
3402 #endif
3403                 } else {
3404                         NextBestWeapon();
3405                 }
3406         }
3407
3408         if ( hud ) {
3409                 if ( tipUp ) {
3410                         HideTip();
3411                 }
3412                 // may want to track with with a bool as well
3413                 // keep from looking up named events so often
3414                 if ( objectiveUp ) {
3415                         HideObjective();
3416                 }
3417         }
3418 }
3419
3420 /*
3421 ===============
3422 idPlayer::CacheWeapons
3423 ===============
3424 */
3425 void idPlayer::CacheWeapons( void ) {
3426         idStr   weap;
3427         int             w;
3428
3429         // check if we have any weapons
3430         if ( !inventory.weapons ) {
3431                 return;
3432         }
3433         
3434         for( w = 0; w < MAX_WEAPONS; w++ ) {
3435                 if ( inventory.weapons & ( 1 << w ) ) {
3436                         weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
3437                         if ( weap != "" ) {
3438                                 idWeapon::CacheWeapon( weap );
3439                         } else {
3440                                 inventory.weapons &= ~( 1 << w );
3441                         }
3442                 }
3443         }
3444 }
3445
3446 /*
3447 ===============
3448 idPlayer::Give
3449 ===============
3450 */
3451 bool idPlayer::Give( const char *statname, const char *value ) {
3452         int amount;
3453
3454         if ( AI_DEAD ) {
3455                 return false;
3456         }
3457
3458         if ( !idStr::Icmp( statname, "health" ) ) {
3459                 if ( health >= inventory.maxHealth ) {
3460                         return false;
3461                 }
3462                 amount = atoi( value );
3463                 if ( amount ) {
3464                         health += amount;
3465                         if ( health > inventory.maxHealth ) {
3466                                 health = inventory.maxHealth;
3467                         }
3468                         if ( hud ) {
3469                                 hud->HandleNamedEvent( "healthPulse" );
3470                         }
3471                 }
3472
3473         } else if ( !idStr::Icmp( statname, "stamina" ) ) {
3474                 if ( stamina >= 100 ) {
3475                         return false;
3476                 }
3477                 stamina += atof( value );
3478                 if ( stamina > 100 ) {
3479                         stamina = 100;
3480                 }
3481
3482         } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
3483                 heartRate += atoi( value );
3484                 if ( heartRate > MAX_HEARTRATE ) {
3485                         heartRate = MAX_HEARTRATE;
3486                 }
3487
3488         } else if ( !idStr::Icmp( statname, "air" ) ) {
3489                 if ( airTics >= pm_airTics.GetInteger() ) {
3490                         return false;
3491                 }
3492                 airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger();
3493                 if ( airTics > pm_airTics.GetInteger() ) {
3494                         airTics = pm_airTics.GetInteger();
3495                 }
3496 #ifdef _D3XP
3497         } else if ( !idStr::Icmp( statname, "enviroTime" ) ) {
3498                 if ( PowerUpActive( ENVIROTIME ) ) {
3499                         inventory.powerupEndTime[ ENVIROTIME ] += (atof(value) * 1000);
3500                 } else {
3501                         GivePowerUp( ENVIROTIME, atoi(value)*1000 );
3502                 }
3503         } else {
3504                 bool ret = inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
3505                 if(!idStr::Icmp( statname, "ammo_bloodstone" ) ) {
3506                         //int i = inventory.AmmoIndexForAmmoClass( statname );          
3507                         //int max = inventory.MaxAmmoForAmmoClass( this, statname );
3508                         //if(hud && inventory.ammo[ i ] >= max) {
3509                         if(hud) {
3510                                 
3511                                 //Force an update of the bloodstone ammount
3512                                 int ammoRequired;
3513                                 ammo_t ammo_i = inventory.AmmoIndexForWeaponClass( "weapon_bloodstone_passive", &ammoRequired );
3514                                 int bloodstoneAmmo = inventory.HasAmmo( ammo_i, ammoRequired );
3515                                 hud->SetStateString("player_bloodstone_ammo", va("%i", bloodstoneAmmo));
3516
3517                                 hud->HandleNamedEvent("bloodstoneReady");
3518                                 //Make sure we unlock the ability to harvest
3519                                 harvest_lock = false;
3520                         }
3521                 }
3522                 return ret;
3523 #else
3524                 return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
3525 #endif
3526         }
3527         return true;
3528 }
3529
3530
3531 /*
3532 ===============
3533 idPlayer::GiveHealthPool
3534
3535 adds health to the player health pool
3536 ===============
3537 */
3538 void idPlayer::GiveHealthPool( float amt ) {
3539         
3540         if ( AI_DEAD ) {
3541                 return;
3542         }
3543
3544         if ( health > 0 ) {
3545                 healthPool += amt;
3546                 if ( healthPool > inventory.maxHealth - health ) {
3547                         healthPool = inventory.maxHealth - health;
3548                 }
3549                 nextHealthPulse = gameLocal.time;
3550         }
3551 }
3552
3553 /*
3554 ===============
3555 idPlayer::GiveItem
3556
3557 Returns false if the item shouldn't be picked up
3558 ===============
3559 */
3560 bool idPlayer::GiveItem( idItem *item ) {
3561         int                                     i;
3562         const idKeyValue        *arg;
3563         idDict                          attr;
3564         bool                            gave;
3565         int                                     numPickup;
3566
3567         if ( gameLocal.isMultiplayer && spectating ) {
3568                 return false;
3569         }
3570
3571         item->GetAttributes( attr );
3572         
3573         gave = false;
3574         numPickup = inventory.pickupItemNames.Num();
3575         for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
3576                 arg = attr.GetKeyVal( i );
3577                 if ( Give( arg->GetKey(), arg->GetValue() ) ) {
3578                         gave = true;
3579                 }
3580         }
3581
3582         arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
3583         if ( arg && hud ) {
3584                 // We need to update the weapon hud manually, but not
3585                 // the armor/ammo/health because they are updated every
3586                 // frame no matter what
3587                 UpdateHudWeapon( false );
3588                 hud->HandleNamedEvent( "weaponPulse" );
3589         }
3590
3591         // display the pickup feedback on the hud
3592         if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
3593                 inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ), this ); //_D3XP
3594         }
3595
3596         return gave;
3597 }
3598
3599 /*
3600 ===============
3601 idPlayer::PowerUpModifier
3602 ===============
3603 */
3604 float idPlayer::PowerUpModifier( int type ) {
3605         float mod = 1.0f;
3606
3607         if ( PowerUpActive( BERSERK ) ) {
3608                 switch( type ) {
3609                         case SPEED: {
3610                                 mod *= 1.7f;
3611                                 break;
3612                         }
3613                         case PROJECTILE_DAMAGE: {
3614                                 mod *= 2.0f;
3615                                 break;
3616                         }
3617                         case MELEE_DAMAGE: {
3618                                 mod *= 30.0f;
3619                                 break;
3620                         }
3621                         case MELEE_DISTANCE: {
3622                                 mod *= 2.0f;
3623                                 break;
3624                         }
3625                 }
3626         }
3627
3628         if ( gameLocal.isMultiplayer && !gameLocal.isClient ) {
3629                 if ( PowerUpActive( MEGAHEALTH ) ) {
3630                         if ( healthPool <= 0 ) {
3631                                 GiveHealthPool( 100 );
3632                         }
3633                 } else {
3634                         healthPool = 0;
3635                 }
3636
3637 #ifdef _D3XP
3638                 /*if( PowerUpActive( HASTE ) ) {
3639                         switch( type ) {
3640                         case SPEED: {
3641                                 mod = 1.7f;
3642                                 break;
3643                                                 }
3644                         }
3645                 }*/
3646 #endif 
3647         }
3648
3649         return mod;
3650 }
3651
3652 /*
3653 ===============
3654 idPlayer::PowerUpActive
3655 ===============
3656 */
3657 bool idPlayer::PowerUpActive( int powerup ) const {
3658         return ( inventory.powerups & ( 1 << powerup ) ) != 0;
3659 }
3660
3661 /*
3662 ===============
3663 idPlayer::GivePowerUp
3664 ===============
3665 */
3666 bool idPlayer::GivePowerUp( int powerup, int time ) {
3667         const char *sound;
3668         const char *skin;
3669
3670         if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
3671
3672                 if ( gameLocal.isServer ) {
3673                         idBitMsg        msg;
3674                         byte            msgBuf[MAX_EVENT_PARAM_SIZE];
3675
3676                         msg.Init( msgBuf, sizeof( msgBuf ) );
3677                         msg.WriteShort( powerup );
3678                         msg.WriteBits( 1, 1 );
3679                         ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
3680                 }
3681
3682                 if ( powerup != MEGAHEALTH ) {
3683                         inventory.GivePowerUp( this, powerup, time );
3684                 }
3685
3686                 const idDeclEntityDef *def = NULL;
3687
3688                 switch( powerup ) {
3689                         case BERSERK: {
3690                                 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3691                                         inventory.AddPickupName("#str_00100627", "", this);
3692                                 }
3693
3694                                 if(gameLocal.isMultiplayer) {
3695                                         if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) ) {
3696                                                 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
3697                                         }
3698                                 }
3699                         
3700
3701                                 if ( baseSkinName.Length() ) {
3702                                         powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
3703                                 }
3704                                 if ( !gameLocal.isClient ) {
3705 #ifdef _D3XP
3706                                         if( !gameLocal.isMultiplayer ) {
3707                                                 // Trying it out without the health boost (1/3/05)
3708                                                 // Give the player full health in single-player
3709                                                 // health = 100;
3710                                         } else {
3711                                                 // Switch to fists in multiplayer
3712                                                 idealWeapon = 1;
3713                                         }
3714 #else
3715                                         idealWeapon = 0;
3716 #endif
3717                                 }
3718                                 break;
3719                         }
3720                         case INVISIBILITY: {
3721                                 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3722                                         inventory.AddPickupName("#str_00100628", "", this);
3723                                 }
3724                                 spawnArgs.GetString( "skin_invisibility", "", &skin );
3725                                 powerUpSkin = declManager->FindSkin( skin );
3726                                 // remove any decals from the model
3727                                 if ( modelDefHandle != -1 ) {
3728                                         gameRenderWorld->RemoveDecals( modelDefHandle );
3729                                 }
3730                                 if ( weapon.GetEntity() ) {
3731                                         weapon.GetEntity()->UpdateSkin();
3732                                 }
3733 /*                              if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
3734                                         StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
3735                                 } */
3736                                 break;
3737                         }
3738                         case ADRENALINE: {
3739 #ifdef _D3XP
3740                                 inventory.AddPickupName("#str_00100799", "", this);
3741 #endif
3742                                 stamina = 100.0f;
3743                                 break;
3744                          }
3745                         case MEGAHEALTH: {
3746                                 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3747                                         inventory.AddPickupName("#str_00100629", "", this);
3748                                 }
3749                                 if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
3750                                         StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
3751                                 }
3752                                 def = gameLocal.FindEntityDef( "powerup_megahealth", false );
3753                                 if ( def ) {
3754                                         health = def->dict.GetInt( "inv_health" );
3755                                 }
3756                                 break;
3757                          }
3758 #ifdef _D3XP
3759                         case HELLTIME: {
3760                                 if ( spawnArgs.GetString( "snd_helltime_start", "", &sound ) ) {
3761                                         PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
3762                                 }
3763                                 if ( spawnArgs.GetString( "snd_helltime_loop", "", &sound ) ) {
3764                                         PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_DEMONIC );
3765                                 }
3766                                 break;
3767                         }
3768                         case ENVIROSUIT: {
3769                                 // Turn on the envirosuit sound
3770                                 if ( gameSoundWorld ) {
3771                                         gameSoundWorld->SetEnviroSuit( true );
3772                                 }
3773
3774                                 // Put the helmet and lights on the player
3775                                 idDict  args;
3776
3777                                 // Light
3778                                 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
3779                                 if ( lightDef ) {
3780                                         idEntity *temp;
3781                                         gameLocal.SpawnEntityDef( *lightDef, &temp, false );
3782
3783                                         idLight *eLight = static_cast<idLight *>(temp);
3784                                         eLight->GetPhysics()->SetOrigin( firstPersonViewOrigin );
3785                                         eLight->UpdateVisuals();
3786                                         eLight->Present();
3787
3788                                         enviroSuitLight = eLight;
3789                                 }
3790                                 break;
3791                         }
3792                         case ENVIROTIME: {
3793                                 hudPowerup = ENVIROTIME;
3794                                 // The HUD display bar is fixed at 60 seconds
3795                                 hudPowerupDuration = 60000;
3796                                 break;
3797                         }
3798                         case INVULNERABILITY: {
3799                                 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3800                                         inventory.AddPickupName("#str_00100630", "", this);
3801                                 }
3802                                 if(gameLocal.isMultiplayer) {
3803                                         /*if ( spawnArgs.GetString( "snd_invulnerable", "", &sound ) ) {
3804                                                 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
3805                                         }*/
3806                                         if ( baseSkinName.Length() ) {
3807                                                 powerUpSkin = declManager->FindSkin( baseSkinName + "_invuln" );
3808                                         }
3809                                 }
3810                                 break;
3811                         }
3812                         /*case HASTE: {
3813                                 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3814                                         inventory.AddPickupName("#str_00100631", "", this);
3815                                 }
3816                                 
3817                                 if ( baseSkinName.Length() ) {
3818                                         powerUpSkin = declManager->FindSkin( baseSkinName + "_haste" );
3819                                 }
3820                                 break;
3821                         }*/
3822 #endif
3823                 }
3824
3825                 if ( hud ) {
3826                         hud->HandleNamedEvent( "itemPickup" );
3827                 }
3828
3829                 return true;
3830         } else {
3831                 gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
3832         }
3833         return false;
3834 }
3835
3836 /*
3837 ==============
3838 idPlayer::ClearPowerup
3839 ==============
3840 */
3841 void idPlayer::ClearPowerup( int i ) {
3842
3843         if ( gameLocal.isServer ) {
3844                 idBitMsg        msg;
3845                 byte            msgBuf[MAX_EVENT_PARAM_SIZE];
3846
3847                 msg.Init( msgBuf, sizeof( msgBuf ) );
3848                 msg.WriteShort( i );
3849                 msg.WriteBits( 0, 1 );
3850                 ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
3851         }
3852
3853         powerUpSkin = NULL;
3854         inventory.powerups &= ~( 1 << i );
3855         inventory.powerupEndTime[ i ] = 0;
3856         switch( i ) {
3857                 case BERSERK: {
3858                         if(gameLocal.isMultiplayer) {
3859                                 StopSound( SND_CHANNEL_DEMONIC, false );
3860                         }
3861 #ifdef _D3XP
3862                         if(!gameLocal.isMultiplayer) {
3863                                 StopHealthRecharge();
3864                         }
3865 #endif
3866                         break;
3867                 }
3868                 case INVISIBILITY: {
3869                         if ( weapon.GetEntity() ) {
3870                                 weapon.GetEntity()->UpdateSkin();
3871                         }
3872                         break;
3873                 }
3874 #ifdef _D3XP
3875                 case HELLTIME: {
3876                         StopSound( SND_CHANNEL_DEMONIC, false );
3877                         break;
3878                 }
3879                 case ENVIROSUIT: {
3880                         
3881                         hudPowerup = -1;
3882
3883                         // Turn off the envirosuit sound
3884                         if ( gameSoundWorld ) {
3885                                 gameSoundWorld->SetEnviroSuit( false );
3886                         }
3887
3888                         // Take off the helmet and lights
3889                         if ( enviroSuitLight.IsValid() ) {
3890                                 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
3891                         }
3892                         enviroSuitLight = NULL;
3893                         break;
3894                 }
3895                 case INVULNERABILITY: {
3896                         if(gameLocal.isMultiplayer) {
3897                                 StopSound( SND_CHANNEL_DEMONIC, false );
3898                         }
3899                 }
3900                 /*case HASTE: {
3901                         if(gameLocal.isMultiplayer) {
3902                                 StopSound( SND_CHANNEL_DEMONIC, false );
3903                         }
3904                 }*/
3905 #endif
3906         }
3907 }
3908
3909 /*
3910 ==============
3911 idPlayer::UpdatePowerUps
3912 ==============
3913 */
3914 void idPlayer::UpdatePowerUps( void ) {
3915         int i;
3916
3917         if ( !gameLocal.isClient ) {
3918                 for ( i = 0; i < MAX_POWERUPS; i++ ) {
3919 #ifdef _D3XP
3920                         if ( ( inventory.powerups & ( 1 << i ) ) && inventory.powerupEndTime[i] > gameLocal.time ) {
3921                                 switch( i ) {
3922                                         case ENVIROSUIT: {
3923                                                 if ( enviroSuitLight.IsValid() ) {
3924                                                         idAngles lightAng = firstPersonViewAxis.ToAngles();
3925                                                         idVec3 lightOrg = firstPersonViewOrigin;
3926                                                         const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
3927
3928                                                         idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
3929                                                         idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
3930
3931                                                         lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
3932                                                         lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
3933                                                         lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
3934                                                         lightAng.pitch += enviroAngleOffset.x;
3935                                                         lightAng.yaw += enviroAngleOffset.y;
3936                                                         lightAng.roll += enviroAngleOffset.z;
3937
3938                                                         enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
3939                                                         enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
3940                                                         enviroSuitLight.GetEntity()->UpdateVisuals();
3941                                                         enviroSuitLight.GetEntity()->Present();
3942                                                 }
3943                                                 break;
3944                                         }
3945                                         default: {
3946                                                 break;
3947                                         }
3948                                 }
3949                         }
3950 #endif
3951                         if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
3952                                 ClearPowerup( i );
3953                         }
3954                 }
3955         }
3956
3957         if ( health > 0 ) {
3958                 if ( powerUpSkin ) {
3959                         renderEntity.customSkin = powerUpSkin;
3960                 } else {
3961                         renderEntity.customSkin = skin;
3962                 }
3963         }
3964
3965         if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
3966                 assert( !gameLocal.isClient );  // healthPool never be set on client
3967                 int amt = ( healthPool > 5 ) ? 5 : healthPool;
3968                 health += amt;
3969                 if ( health > inventory.maxHealth ) {
3970                         health = inventory.maxHealth;
3971                         healthPool = 0;
3972                 } else {
3973                         healthPool -= amt;
3974                 }
3975                 nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
3976                 healthPulse = true;
3977         }
3978 #ifndef ID_DEMO_BUILD
3979         if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
3980                 assert( !gameLocal.isClient );  // healthPool never be set on client
3981                 
3982 #ifdef _D3XP
3983                 if(!PowerUpActive(INVULNERABILITY)) {
3984 #endif
3985                 health -= g_healthTakeAmt.GetInteger();
3986                 if ( health < g_healthTakeLimit.GetInteger() ) {
3987                         health = g_healthTakeLimit.GetInteger();
3988                 }
3989 #ifdef _D3XP
3990                 }
3991 #endif
3992                 nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
3993                 healthTake = true;
3994         }
3995 #endif
3996 }
3997
3998 /*
3999 ===============
4000 idPlayer::ClearPowerUps
4001 ===============
4002 */
4003 void idPlayer::ClearPowerUps( void ) {
4004         int i;
4005         for ( i = 0; i < MAX_POWERUPS; i++ ) {
4006                 if ( PowerUpActive( i ) ) {
4007                         ClearPowerup( i );
4008                 }
4009         }
4010         inventory.ClearPowerUps();
4011
4012 #ifdef _D3XP
4013         if ( gameLocal.isMultiplayer ) {
4014                 if ( enviroSuitLight.IsValid() ) {
4015                         enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
4016                 }
4017         }
4018 #endif
4019 }
4020
4021 /*
4022 ===============
4023 idPlayer::GiveInventoryItem
4024 ===============
4025 */
4026 bool idPlayer::GiveInventoryItem( idDict *item ) {
4027         if ( gameLocal.isMultiplayer && spectating ) {
4028                 return false;
4029         }
4030         inventory.items.Append( new idDict( *item ) );
4031         idItemInfo info;
4032         const char* itemName = item->GetString( "inv_name" );
4033         if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
4034                 info.name = common->GetLanguageDict()->GetString( itemName );
4035         } else {
4036                 info.name = itemName;
4037         }
4038         info.icon = item->GetString( "inv_icon" );
4039         inventory.pickupItemNames.Append( info );
4040         if ( hud ) {
4041                 hud->SetStateString( "itemicon", info.icon );
4042                 hud->HandleNamedEvent( "invPickup" );
4043         }
4044
4045 #ifdef _D3XP //Added to support powercells
4046         if(item->GetInt("inv_powercell") && focusUI) {
4047                 //Reset the powercell count
4048                 int powerCellCount = 0;
4049                 for ( int j = 0; j < inventory.items.Num(); j++ ) {
4050                         idDict *item = inventory.items[ j ];
4051                         if(item->GetInt("inv_powercell")) {
4052                                 powerCellCount++;
4053                         }
4054                 }
4055                 focusUI->SetStateInt( "powercell_count", powerCellCount );
4056         }
4057 #endif
4058
4059         return true;
4060 }
4061
4062 #ifdef _D3XP //BSM: Implementing this defined function for scripted give inventory items
4063 /*
4064 ==============
4065 idPlayer::GiveInventoryItem
4066 ==============
4067 */
4068 bool idPlayer::GiveInventoryItem( const char *name ) {
4069         idDict args;
4070
4071         args.Set( "classname", name );
4072         args.Set( "owner", this->name.c_str() );
4073         gameLocal.SpawnEntityDef( args);
4074         return true;
4075 }
4076 #endif
4077
4078 /*
4079 ==============
4080 idPlayer::UpdateObjectiveInfo
4081 ==============
4082  */
4083 void idPlayer::UpdateObjectiveInfo( void ) {
4084         if ( objectiveSystem == NULL ) {
4085                 return;
4086         }
4087         objectiveSystem->SetStateString( "objective1", "" );
4088         objectiveSystem->SetStateString( "objective2", "" );
4089         objectiveSystem->SetStateString( "objective3", "" );
4090         for ( int i = 0; i < inventory.objectiveNames.Num(); i++ ) {
4091                 objectiveSystem->SetStateString( va( "objective%i", i+1 ), "1" );
4092                 objectiveSystem->SetStateString( va( "objectivetitle%i", i+1 ), inventory.objectiveNames[i].title.c_str() );
4093                 objectiveSystem->SetStateString( va( "objectivetext%i", i+1 ), inventory.objectiveNames[i].text.c_str() );
4094                 objectiveSystem->SetStateString( va( "objectiveshot%i", i+1 ), inventory.objectiveNames[i].screenshot.c_str() );
4095         }
4096         objectiveSystem->StateChanged( gameLocal.time );
4097 }
4098
4099 /*
4100 ===============
4101 idPlayer::GiveObjective
4102 ===============
4103 */
4104 void idPlayer::GiveObjective( const char *title, const char *text, const char *screenshot ) {
4105         idObjectiveInfo info;
4106         info.title = title;
4107         info.text = text;
4108         info.screenshot = screenshot;
4109         inventory.objectiveNames.Append( info );
4110         ShowObjective( "newObjective" );
4111         if ( hud ) {
4112                 hud->HandleNamedEvent( "newObjective" );
4113         }
4114 }
4115
4116 /*
4117 ===============
4118 idPlayer::CompleteObjective
4119 ===============
4120 */
4121 void idPlayer::CompleteObjective( const char *title ) {
4122         int c = inventory.objectiveNames.Num();
4123         for ( int i = 0;  i < c; i++ ) {
4124                 if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
4125                         inventory.objectiveNames.RemoveIndex( i );
4126                         break;
4127                 }
4128         }
4129         ShowObjective( "newObjectiveComplete" );
4130
4131         if ( hud ) {
4132                 hud->HandleNamedEvent( "newObjectiveComplete" );
4133         }
4134 }
4135
4136 /*
4137 ===============
4138 idPlayer::GiveVideo
4139 ===============
4140 */
4141 void idPlayer::GiveVideo( const char *videoName, idDict *item ) {
4142
4143         if ( videoName == NULL || *videoName == NULL ) {
4144                 return;
4145         }
4146
4147         inventory.videos.AddUnique( videoName );
4148
4149         if ( item ) {
4150                 idItemInfo info;
4151                 info.name = item->GetString( "inv_name" );
4152                 info.icon = item->GetString( "inv_icon" );
4153                 inventory.pickupItemNames.Append( info );
4154         }
4155         if ( hud ) {
4156                 hud->HandleNamedEvent( "videoPickup" );
4157         }
4158 }
4159
4160 /*
4161 ===============
4162 idPlayer::GiveSecurity
4163 ===============
4164 */
4165 void idPlayer::GiveSecurity( const char *security ) {
4166         GetPDA()->SetSecurity( security );
4167         if ( hud ) {
4168                 hud->SetStateString( "pda_security", "1" );
4169                 hud->HandleNamedEvent( "securityPickup" );
4170         }
4171 }
4172
4173 /*
4174 ===============
4175 idPlayer::GiveEmail
4176 ===============
4177 */
4178 void idPlayer::GiveEmail( const char *emailName ) {
4179
4180         if ( emailName == NULL || *emailName == NULL ) {
4181                 return;
4182         }
4183
4184         inventory.emails.AddUnique( emailName );
4185         GetPDA()->AddEmail( emailName );
4186
4187         if ( hud ) {
4188                 hud->HandleNamedEvent( "emailPickup" );
4189         }
4190 }
4191
4192 /*
4193 ===============
4194 idPlayer::GivePDA
4195 ===============
4196 */
4197 void idPlayer::GivePDA( const char *pdaName, idDict *item )
4198 {
4199         if ( gameLocal.isMultiplayer && spectating ) {
4200                 return;
4201         }
4202
4203         if ( item ) {
4204                 inventory.pdaSecurity.AddUnique( item->GetString( "inv_name" ) );
4205         }
4206
4207         if ( pdaName == NULL || *pdaName == NULL ) {
4208                 pdaName = "personal";
4209         }
4210
4211         const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, pdaName ) );
4212
4213         inventory.pdas.AddUnique( pdaName );
4214
4215         // Copy any videos over
4216         for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
4217                 const idDeclVideo *video = pda->GetVideoByIndex( i );
4218                 if ( video ) {
4219                         inventory.videos.AddUnique( video->GetName() );
4220                 }
4221         }
4222
4223         // This is kind of a hack, but it works nicely
4224         // We don't want to display the 'you got a new pda' message during a map load
4225         if ( gameLocal.GetFrameNum() > 10 ) {
4226                 if ( pda && hud ) {
4227                         idStr pdaName = pda->GetPdaName();
4228                         pdaName.RemoveColors();
4229                         hud->SetStateString( "pda", "1" );
4230                         hud->SetStateString( "pda_text", pdaName );
4231                         const char *sec = pda->GetSecurity();
4232                         hud->SetStateString( "pda_security", ( sec && *sec ) ? "1" : "0" );
4233                         hud->HandleNamedEvent( "pdaPickup" );
4234                 }
4235
4236                 if ( inventory.pdas.Num() == 1 ) {
4237                         GetPDA()->RemoveAddedEmailsAndVideos();
4238                         if ( !objectiveSystemOpen ) {
4239                                 TogglePDA();
4240                         }
4241                         objectiveSystem->HandleNamedEvent( "showPDATip" );
4242                         //ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
4243                 }
4244
4245                 if ( inventory.pdas.Num() > 1 && pda->GetNumVideos() > 0 && hud ) {
4246                         hud->HandleNamedEvent( "videoPickup" );
4247                 }
4248         }
4249 }
4250
4251 /*
4252 ===============
4253 idPlayer::FindInventoryItem
4254 ===============
4255 */
4256 idDict *idPlayer::FindInventoryItem( const char *name ) {
4257         for ( int i = 0; i < inventory.items.Num(); i++ ) {
4258                 const char *iname = inventory.items[i]->GetString( "inv_name" );
4259                 if ( iname && *iname ) {
4260                         if ( idStr::Icmp( name, iname ) == 0 ) {
4261                                 return inventory.items[i];
4262                         }
4263                 }
4264         }
4265         return NULL;
4266 }
4267
4268 /*
4269 ===============
4270 idPlayer::RemoveInventoryItem
4271 ===============
4272 */
4273 void idPlayer::RemoveInventoryItem( const char *name ) {
4274         //Hack for localization
4275         if(!idStr::Icmp(name, "Pwr Cell")) {
4276                 name = common->GetLanguageDict()->GetString( "#str_00101056" );
4277         }
4278         idDict *item = FindInventoryItem(name);
4279         if ( item ) {
4280                 RemoveInventoryItem( item );
4281         }
4282 }
4283
4284 /*
4285 ===============
4286 idPlayer::RemoveInventoryItem
4287 ===============
4288 */
4289 void idPlayer::RemoveInventoryItem( idDict *item ) {
4290         inventory.items.Remove( item );
4291
4292 #ifdef _D3XP //Added to support powercells
4293         if(item->GetInt("inv_powercell") && focusUI) {
4294                 //Reset the powercell count
4295                 int powerCellCount = 0;
4296                 for ( int j = 0; j < inventory.items.Num(); j++ ) {
4297                         idDict *item = inventory.items[ j ];
4298                         if(item->GetInt("inv_powercell")) {
4299                                 powerCellCount++;
4300                         }
4301                 }
4302                 focusUI->SetStateInt( "powercell_count", powerCellCount );
4303         }
4304 #endif
4305
4306         delete item;
4307 }
4308
4309 /*
4310 ===============
4311 idPlayer::GiveItem
4312 ===============
4313 */
4314 void idPlayer::GiveItem( const char *itemname ) {
4315         idDict args;
4316
4317         args.Set( "classname", itemname );
4318         args.Set( "owner", name.c_str() );
4319         gameLocal.SpawnEntityDef( args );
4320         if ( hud ) {
4321                 hud->HandleNamedEvent( "itemPickup" );
4322         }
4323 }
4324
4325 /*
4326 ==================
4327 idPlayer::SlotForWeapon
4328 ==================
4329 */
4330 int idPlayer::SlotForWeapon( const char *weaponName ) {
4331         int i;
4332
4333         for( i = 0; i < MAX_WEAPONS; i++ ) {
4334                 const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
4335                 if ( !idStr::Cmp( weap, weaponName ) ) {
4336                         return i;
4337                 }
4338         }
4339
4340         // not found
4341         return -1;
4342 }
4343
4344 /*
4345 ===============
4346 idPlayer::Reload
4347 ===============
4348 */
4349 void idPlayer::Reload( void ) {
4350         if ( gameLocal.isClient ) {
4351                 return;
4352         }
4353
4354         if ( spectating || gameLocal.inCinematic || influenceActive ) {
4355                 return;
4356         }
4357
4358         if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
4359                 weapon.GetEntity()->Reload();
4360         }
4361 }
4362
4363 /*
4364 ===============
4365 idPlayer::NextBestWeapon
4366 ===============
4367 */
4368 void idPlayer::NextBestWeapon( void ) {
4369         const char *weap;
4370         int w = MAX_WEAPONS;
4371
4372         if ( gameLocal.isClient || !weaponEnabled ) {
4373                 return;
4374         }
4375
4376         while ( w > 0 ) {
4377                 w--;
4378                 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4379 #ifdef _D3XP
4380                 if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) ) {
4381 #else
4382                 if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !(inventory.HasAmmo( weap )) ) ) {
4383 #endif
4384                         continue;
4385                 }
4386                 if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
4387                         continue;
4388                 }
4389
4390 #ifdef _D3XP
4391                 //Some weapons will report having ammo but the clip is empty and 
4392                 //will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
4393                 //We need to skip these weapons because they cannot be used
4394                 if(inventory.HasEmptyClipCannotRefill(weap, this)) {
4395                         continue;
4396                 }
4397 #endif 
4398
4399                 break;
4400         }
4401         idealWeapon = w;
4402         weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4403         UpdateHudWeapon();
4404 }
4405
4406 /*
4407 ===============
4408 idPlayer::NextWeapon
4409 ===============
4410 */
4411 void idPlayer::NextWeapon( void ) {
4412
4413         const char *weap;
4414         int w;
4415
4416         if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
4417                 return;
4418         }
4419
4420         if ( gameLocal.isClient ) {
4421                 return;
4422         }
4423
4424         // check if we have any weapons
4425         if ( !inventory.weapons ) {
4426                 return;
4427         }
4428         
4429         w = idealWeapon;
4430         while( 1 ) {
4431                 w++;
4432                 if ( w >= MAX_WEAPONS ) {
4433                         w = 0;
4434                 } 
4435                 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4436                 if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
4437                         continue;
4438                 }
4439                 if ( !weap[ 0 ] ) {
4440                         continue;
4441                 }
4442                 if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
4443                         continue;
4444                 }
4445
4446 #ifdef _D3XP
4447                 if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
4448 #else
4449                 if ( inventory.HasAmmo( weap ) ) {
4450 #endif
4451                         break;
4452                 }
4453         }
4454
4455         if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
4456                 idealWeapon = w;
4457                 weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4458                 UpdateHudWeapon();
4459         }
4460 }
4461
4462 /*
4463 ===============
4464 idPlayer::PrevWeapon
4465 ===============
4466 */
4467 void idPlayer::PrevWeapon( void ) {
4468
4469         const char *weap;
4470         int w;
4471
4472         if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
4473                 return;
4474         }
4475
4476         if ( gameLocal.isClient ) {
4477                 return;
4478         }
4479
4480         // check if we have any weapons
4481         if ( !inventory.weapons ) {
4482                 return;
4483         }
4484
4485         w = idealWeapon;
4486         while( 1 ) {
4487                 w--;
4488                 if ( w < 0 ) {
4489                         w = MAX_WEAPONS - 1;
4490                 }
4491                 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4492                 if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
4493                         continue;
4494                 }
4495                 if ( !weap[ 0 ] ) {
4496                         continue;
4497                 }
4498                 if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
4499                         continue;
4500                 }
4501 #ifdef _D3XP
4502                 if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
4503 #else
4504                 if ( inventory.HasAmmo( weap ) ) {
4505 #endif
4506                         break;
4507                 }
4508         }
4509
4510         if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
4511                 idealWeapon = w;
4512                 weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4513                 UpdateHudWeapon();
4514         }
4515 }
4516
4517 /*
4518 ===============
4519 idPlayer::SelectWeapon
4520 ===============
4521 */
4522 void idPlayer::SelectWeapon( int num, bool force ) {
4523         const char *weap;
4524
4525         if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
4526                 return;
4527         }
4528
4529         if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
4530                 return;
4531         }
4532
4533         if ( gameLocal.isClient ) {
4534                 return;
4535         }
4536
4537         if ( ( num != weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
4538                 num = weapon_fists;
4539                 hiddenWeapon ^= 1;
4540                 if ( hiddenWeapon && weapon.GetEntity() ) {
4541                         weapon.GetEntity()->LowerWeapon();
4542                 } else {
4543                         weapon.GetEntity()->RaiseWeapon();
4544                 }
4545         }       
4546
4547         weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
4548         if ( !weap[ 0 ] ) {
4549                 gameLocal.Printf( "Invalid weapon\n" );
4550                 return;
4551         }
4552
4553 #ifdef _D3XP
4554         //Is the weapon a toggle weapon
4555         WeaponToggle_t* weaponToggle;
4556         if(weaponToggles.Get(va("weapontoggle%d", num), &weaponToggle)) {
4557
4558                 int weaponToggleIndex = 0;
4559
4560                 //Find the current Weapon in the list
4561                 int currentIndex = -1;
4562                 for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
4563                         if(weaponToggle->toggleList[i] == idealWeapon) {
4564                                 currentIndex = i;
4565                                 break;
4566                         }
4567                 }
4568                 if(currentIndex == -1) {
4569                         //Didn't find the current weapon so select the first item
4570                         weaponToggleIndex = 0;
4571                 } else {
4572                         //Roll to the next available item in the list
4573                         weaponToggleIndex = currentIndex;
4574                         weaponToggleIndex++;
4575                         if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
4576                                 weaponToggleIndex = 0;
4577                         }
4578                 }
4579
4580                 for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
4581                         
4582                         //Is it available
4583                         if(inventory.weapons & ( 1 << weaponToggle->toggleList[weaponToggleIndex])) {
4584                                 break;
4585                         }
4586
4587                         weaponToggleIndex++;
4588                         if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
4589                                 weaponToggleIndex = 0;
4590                         }
4591                 }
4592
4593                 num = weaponToggle->toggleList[weaponToggleIndex];
4594         }
4595 #endif
4596
4597         if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
4598 #ifdef _D3XP
4599                 if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
4600 #else
4601                 if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
4602 #endif
4603                         return;
4604                 }
4605                 if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
4606                         weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
4607 #ifdef _D3XP
4608                         if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
4609 #else
4610                         if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
4611 #endif
4612                                 return;
4613                         }
4614                         idealWeapon = previousWeapon;
4615                 } else if ( ( weapon_pda >= 0 ) && ( num == weapon_pda ) && ( inventory.pdas.Num() == 0 ) ) {
4616                         ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
4617                         return;
4618                 } else {
4619                         idealWeapon = num;
4620                 }
4621                 UpdateHudWeapon();
4622         }
4623 }
4624
4625 /*
4626 =================
4627 idPlayer::DropWeapon
4628 =================
4629 */
4630 void idPlayer::DropWeapon( bool died ) {
4631         idVec3 forward, up;
4632         int inclip, ammoavailable;
4633
4634         assert( !gameLocal.isClient );
4635         
4636         if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
4637                 return;
4638         }
4639         
4640         if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
4641                 return;
4642         }
4643         // ammoavailable is how many shots we can fire
4644         // inclip is which amount is in clip right now
4645         ammoavailable = weapon.GetEntity()->AmmoAvailable();
4646         inclip = weapon.GetEntity()->AmmoInClip();
4647         
4648         // don't drop a grenade if we have none left
4649         if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
4650                 return;
4651         }
4652
4653 #ifdef _D3XP
4654         ammoavailable += inclip;
4655 #endif
4656
4657         // expect an ammo setup that makes sense before doing any dropping
4658         // ammoavailable is -1 for infinite ammo, and weapons like chainsaw
4659         // a bad ammo config usually indicates a bad weapon state, so we should not drop
4660         // used to be an assertion check, but it still happens in edge cases
4661
4662         if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
4663                 common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
4664                 return;
4665         }
4666         idEntity *item = NULL;
4667         if ( died ) {
4668                 // ain't gonna throw you no weapon if I'm dead
4669                 item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
4670         } else {
4671                 viewAngles.ToVectors( &forward, NULL, &up );
4672                 item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
4673         }
4674         if ( !item ) {
4675                 return;
4676         }
4677         // set the appropriate ammo in the dropped object
4678         const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
4679         if ( keyval ) {
4680                 item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
4681                 idStr inclipKey = keyval->GetKey();
4682                 inclipKey.Insert( "inclip_", 4 );
4683 #ifdef _D3XP
4684                 inclipKey.Insert( va("%.2d", currentWeapon), 11);
4685 #endif
4686                 item->spawnArgs.SetInt( inclipKey, inclip );
4687         }
4688         if ( !died ) {
4689                 // remove from our local inventory completely
4690                 inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
4691                 weapon.GetEntity()->ResetAmmoClip();
4692                 NextWeapon();
4693                 weapon.GetEntity()->WeaponStolen();
4694                 weaponGone = true;
4695         }
4696 }
4697
4698 /*
4699 =================
4700 idPlayer::StealWeapon
4701 steal the target player's current weapon
4702 =================
4703 */
4704 void idPlayer::StealWeapon( idPlayer *player ) {
4705         assert( !gameLocal.isClient );
4706
4707         // make sure there's something to steal
4708         idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
4709         if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
4710                 return;
4711         }
4712         // steal - we need to effectively force the other player to abandon his weapon
4713         int newweap = player->currentWeapon;
4714         if ( newweap == -1 ) {
4715                 return;
4716         }
4717         // might be just dropped - check inventory
4718         if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
4719                 return;
4720         }
4721         const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
4722         assert( weapon_classname );
4723         int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
4724         int inclip = player->weapon.GetEntity()->AmmoInClip();
4725
4726 #ifdef _D3XP
4727         ammoavailable += inclip;
4728 #endif
4729
4730         if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
4731                 // see DropWeapon
4732                 common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
4733                 // we still steal the weapon, so let's use the default ammo levels
4734                 inclip = -1;
4735                 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
4736                 assert( decl );
4737                 const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
4738                 assert( keypair );
4739                 ammoavailable = atoi( keypair->GetValue() );
4740         }
4741
4742         player->weapon.GetEntity()->WeaponStolen();
4743         player->inventory.Drop( player->spawnArgs, NULL, newweap );
4744         player->SelectWeapon( weapon_fists, false );
4745         // in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
4746         // this will ensure the firing actually stops
4747         player->weaponGone = true;
4748
4749         // give weapon, setup the ammo count
4750         Give( "weapon", weapon_classname );
4751         ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
4752         idealWeapon = newweap;
4753         inventory.ammo[ ammo_i ] += ammoavailable;
4754
4755 #ifndef _D3XP
4756         inventory.clip[ newweap ] = inclip;
4757 #endif
4758 }
4759
4760 /*
4761 ===============
4762 idPlayer::ActiveGui
4763 ===============
4764 */
4765 idUserInterface *idPlayer::ActiveGui( void ) {
4766         if ( objectiveSystemOpen ) {
4767                 return objectiveSystem;
4768         }
4769
4770         return focusUI;
4771 }
4772
4773 /*
4774 ===============
4775 idPlayer::Weapon_Combat
4776 ===============
4777 */
4778 void idPlayer::Weapon_Combat( void ) {
4779         if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
4780                 return;
4781         }
4782
4783         weapon.GetEntity()->RaiseWeapon();
4784         if ( weapon.GetEntity()->IsReloading() ) {
4785                 if ( !AI_RELOAD ) {
4786                         AI_RELOAD = true;
4787                         SetState( "ReloadWeapon" );
4788                         UpdateScript();
4789                 }
4790         } else {
4791                 AI_RELOAD = false;
4792         }
4793
4794         if ( idealWeapon == weapon_soulcube && soulCubeProjectile.GetEntity() != NULL ) {
4795                 idealWeapon = currentWeapon;
4796         }
4797
4798         if ( idealWeapon != currentWeapon ) {
4799                 if ( weaponCatchup ) {
4800                         assert( gameLocal.isClient );
4801
4802                         currentWeapon = idealWeapon;
4803                         weaponGone = false;
4804                         animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
4805                         weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
4806                         animPrefix.Strip( "weapon_" );
4807
4808                         weapon.GetEntity()->NetCatchup();
4809                         const function_t *newstate = GetScriptFunction( "NetCatchup" );
4810                         if ( newstate ) {
4811                                 SetState( newstate );
4812                                 UpdateScript();
4813                         }
4814                         weaponCatchup = false;                  
4815                 } else {
4816                         if ( weapon.GetEntity()->IsReady() ) {
4817                                 weapon.GetEntity()->PutAway();
4818                         }
4819
4820                         if ( weapon.GetEntity()->IsHolstered() ) {
4821                                 assert( idealWeapon >= 0 );
4822                                 assert( idealWeapon < MAX_WEAPONS );
4823
4824                                 if ( currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", currentWeapon ) ) ) {
4825                                         previousWeapon = currentWeapon;
4826                                 }
4827                                 currentWeapon = idealWeapon;
4828                                 weaponGone = false;
4829                                 animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
4830                                 weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
4831                                 animPrefix.Strip( "weapon_" );
4832
4833                                 weapon.GetEntity()->Raise();
4834                         }
4835                 }
4836         } else {
4837                 weaponGone = false;     // if you drop and re-get weap, you may miss the = false above 
4838                 if ( weapon.GetEntity()->IsHolstered() ) {
4839                         if ( !weapon.GetEntity()->AmmoAvailable() ) {
4840                                 // weapons can switch automatically if they have no more ammo
4841                                 NextBestWeapon();
4842                         } else {
4843                                 weapon.GetEntity()->Raise();
4844                                 state = GetScriptFunction( "RaiseWeapon" );
4845                                 if ( state ) {
4846                                         SetState( state );
4847                                 }
4848                         }
4849                 }
4850         }
4851
4852         // check for attack
4853         AI_WEAPON_FIRED = false;
4854         if ( !influenceActive ) {
4855                 if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
4856                         FireWeapon();
4857                 } else if ( oldButtons & BUTTON_ATTACK ) {
4858                         AI_ATTACK_HELD = false;
4859                         weapon.GetEntity()->EndAttack();
4860                 }
4861         }
4862
4863         // update our ammo clip in our inventory
4864         if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
4865                 inventory.clip[ currentWeapon ] = weapon.GetEntity()->AmmoInClip();
4866                 if ( hud && ( currentWeapon == idealWeapon ) ) {
4867                         UpdateHudAmmo( hud );
4868                 }
4869         }
4870 }
4871
4872 /*
4873 ===============
4874 idPlayer::Weapon_NPC
4875 ===============
4876 */
4877 void idPlayer::Weapon_NPC( void ) {
4878         if ( idealWeapon != currentWeapon ) {
4879                 Weapon_Combat();
4880         }
4881         StopFiring();
4882         weapon.GetEntity()->LowerWeapon();
4883
4884         if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
4885                 buttonMask |= BUTTON_ATTACK;
4886                 focusCharacter->TalkTo( this );
4887         }
4888 }
4889
4890 /*
4891 ===============
4892 idPlayer::LowerWeapon
4893 ===============
4894 */
4895 void idPlayer::LowerWeapon( void ) {
4896         if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
4897                 weapon.GetEntity()->LowerWeapon();
4898         }
4899 }
4900
4901 /*
4902 ===============
4903 idPlayer::RaiseWeapon
4904 ===============
4905 */
4906 void idPlayer::RaiseWeapon( void ) {
4907         if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
4908                 weapon.GetEntity()->RaiseWeapon();
4909         }
4910 }
4911
4912 /*
4913 ===============
4914 idPlayer::WeaponLoweringCallback
4915 ===============
4916 */
4917 void idPlayer::WeaponLoweringCallback( void ) {
4918         SetState( "LowerWeapon" );
4919         UpdateScript();
4920 }
4921
4922 /*
4923 ===============
4924 idPlayer::WeaponRisingCallback
4925 ===============
4926 */
4927 void idPlayer::WeaponRisingCallback( void ) {
4928         SetState( "RaiseWeapon" );
4929         UpdateScript();
4930 }
4931
4932 /*
4933 ===============
4934 idPlayer::Weapon_GUI
4935 ===============
4936 */
4937 void idPlayer::Weapon_GUI( void ) {
4938
4939         if ( !objectiveSystemOpen ) {
4940                 if ( idealWeapon != currentWeapon ) {
4941                         Weapon_Combat();
4942                 }
4943                 StopFiring();
4944                 weapon.GetEntity()->LowerWeapon();
4945         }
4946
4947         // disable click prediction for the GUIs. handy to check the state sync does the right thing
4948         if ( gameLocal.isClient && !net_clientPredictGUI.GetBool() ) {
4949                 return;
4950         }
4951
4952         if ( ( oldButtons ^ usercmd.buttons ) & BUTTON_ATTACK ) {
4953                 sysEvent_t ev;
4954                 const char *command = NULL;
4955                 bool updateVisuals = false;
4956
4957                 idUserInterface *ui = ActiveGui();
4958                 if ( ui ) {
4959                         ev = sys->GenerateMouseButtonEvent( 1, ( usercmd.buttons & BUTTON_ATTACK ) != 0 );
4960                         command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
4961                         if ( updateVisuals && focusGUIent && ui == focusUI ) {
4962                                 focusGUIent->UpdateVisuals();
4963                         }
4964                 }
4965                 if ( gameLocal.isClient ) {
4966                         // we predict enough, but don't want to execute commands
4967                         return;
4968                 }
4969                 if ( focusGUIent ) {
4970                         HandleGuiCommands( focusGUIent, command );
4971                 } else {
4972                         HandleGuiCommands( this, command );
4973                 }
4974         }
4975 }
4976
4977 /*
4978 ===============
4979 idPlayer::UpdateWeapon
4980 ===============
4981 */
4982 void idPlayer::UpdateWeapon( void ) {
4983         if ( health <= 0 ) {
4984                 return;
4985         }
4986
4987         assert( !spectating );
4988
4989         if ( gameLocal.isClient ) {
4990                 // clients need to wait till the weapon and it's world model entity
4991                 // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
4992                 if ( !weapon.GetEntity()->IsWorldModelReady() ) {
4993                         return;
4994                 }
4995         }
4996
4997         // always make sure the weapon is correctly setup before accessing it
4998         if ( !weapon.GetEntity()->IsLinked() ) {
4999                 if ( idealWeapon != -1 ) {
5000                         animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
5001                         weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ idealWeapon ] );
5002                         assert( weapon.GetEntity()->IsLinked() );
5003                 } else {
5004                         return;
5005                 }
5006         }
5007
5008         if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
5009                 HideTip();
5010         }
5011         
5012         if ( g_dragEntity.GetBool() ) {
5013                 StopFiring();
5014                 weapon.GetEntity()->LowerWeapon();
5015                 dragEntity.Update( this );
5016         } else if ( ActiveGui() ) {
5017                 // gui handling overrides weapon use
5018                 Weapon_GUI();
5019         } else  if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
5020                 Weapon_NPC();
5021         } else {
5022                 Weapon_Combat();
5023         }
5024         
5025         if ( hiddenWeapon ) {
5026                 weapon.GetEntity()->LowerWeapon();
5027         }
5028
5029         // update weapon state, particles, dlights, etc
5030         weapon.GetEntity()->PresentWeapon( showWeaponViewModel );
5031 }
5032
5033 /*
5034 ===============
5035 idPlayer::SpectateFreeFly
5036 ===============
5037 */
5038 void idPlayer::SpectateFreeFly( bool force ) {
5039         idPlayer        *player;
5040         idVec3          newOrig;
5041         idVec3          spawn_origin;
5042         idAngles        spawn_angles;
5043
5044         player = gameLocal.GetClientByNum( spectator );
5045         if ( force || gameLocal.time > lastSpectateChange ) {
5046                 spectator = entityNumber;
5047                 if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
5048                         newOrig = player->GetPhysics()->GetOrigin();
5049                         if ( player->physicsObj.IsCrouching() ) {
5050                                 newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
5051                         } else {
5052                                 newOrig[ 2 ] += pm_normalviewheight.GetFloat();
5053                         }
5054                         newOrig[ 2 ] += SPECTATE_RAISE;
5055                         idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
5056                         idVec3 start = player->GetPhysics()->GetOrigin();
5057                         start[2] += pm_spectatebbox.GetFloat() * 0.5f;
5058                         trace_t t;
5059                         // assuming spectate bbox is inside stand or crouch box
5060                         gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
5061                         newOrig.Lerp( start, newOrig, t.fraction );
5062                         SetOrigin( newOrig );
5063                         idAngles angle = player->viewAngles;
5064                         angle[ 2 ] = 0;
5065                         SetViewAngles( angle );
5066                 } else {        
5067                         SelectInitialSpawnPoint( spawn_origin, spawn_angles );
5068                         spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
5069                         spawn_origin[ 2 ] += SPECTATE_RAISE;
5070                         SetOrigin( spawn_origin );
5071                         SetViewAngles( spawn_angles );
5072                 }
5073                 lastSpectateChange = gameLocal.time + 500;
5074         }
5075 }
5076
5077 /*
5078 ===============
5079 idPlayer::SpectateCycle
5080 ===============
5081 */
5082 void idPlayer::SpectateCycle( void ) {
5083         idPlayer *player;
5084
5085         if ( gameLocal.time > lastSpectateChange ) {
5086                 int latchedSpectator = spectator;
5087                 spectator = gameLocal.GetNextClientNum( spectator );
5088                 player = gameLocal.GetClientByNum( spectator );
5089                 assert( player ); // never call here when the current spectator is wrong
5090                 // ignore other spectators
5091                 while ( latchedSpectator != spectator && player->spectating ) {
5092                         spectator = gameLocal.GetNextClientNum( spectator );
5093                         player = gameLocal.GetClientByNum( spectator );
5094                 }
5095                 lastSpectateChange = gameLocal.time + 500;
5096         }
5097 }
5098
5099 /*
5100 ===============
5101 idPlayer::UpdateSpectating
5102 ===============
5103 */
5104 void idPlayer::UpdateSpectating( void ) {
5105         assert( spectating );
5106         assert( !gameLocal.isClient );
5107         assert( IsHidden() );
5108         idPlayer *player;
5109         if ( !gameLocal.isMultiplayer ) {
5110                 return;
5111         }
5112         player = gameLocal.GetClientByNum( spectator );
5113         if ( !player || ( player->spectating && player != this ) ) {
5114                 SpectateFreeFly( true );
5115         } else if ( usercmd.upmove > 0 ) {
5116                 SpectateFreeFly( false );
5117         } else if ( usercmd.buttons & BUTTON_ATTACK ) {
5118                 SpectateCycle();
5119         }
5120 }
5121
5122 /*
5123 ===============
5124 idPlayer::HandleSingleGuiCommand
5125 ===============
5126 */
5127 bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
5128         idToken token;
5129
5130         if ( !src->ReadToken( &token ) ) {
5131                 return false;
5132         }
5133
5134         if ( token == ";" ) {
5135                 return false;
5136         }
5137
5138         if ( token.Icmp( "addhealth" ) == 0 ) {
5139                 if ( entityGui && health < 100 ) {
5140                         int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
5141                         int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
5142                         _health -= amt;
5143                         entityGui->spawnArgs.SetInt( "gui_parm1", _health );
5144                         if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
5145                                 entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
5146                         }
5147                         health += amt;
5148                         if ( health > 100 ) {
5149                                 health = 100;
5150                         }
5151                 }
5152                 return true;
5153         }
5154
5155         if ( token.Icmp( "ready" ) == 0 ) {
5156                 PerformImpulse( IMPULSE_17 );
5157                 return true;
5158         }
5159
5160         if ( token.Icmp( "updatepda" ) == 0 ) {
5161                 UpdatePDAInfo( true );
5162                 return true;
5163         }
5164
5165         if ( token.Icmp( "updatepda2" ) == 0 ) {
5166                 UpdatePDAInfo( false );
5167                 return true;
5168         }
5169
5170         if ( token.Icmp( "stoppdavideo" ) == 0 ) {
5171                 if ( objectiveSystem && objectiveSystemOpen && pdaVideoWave.Length() > 0 ) {
5172                         StopSound( SND_CHANNEL_PDA, false );
5173                 }
5174                 return true;
5175         }
5176
5177         if ( token.Icmp( "close" ) == 0 ) {
5178                 if ( objectiveSystem && objectiveSystemOpen ) {
5179                         TogglePDA();
5180                 }
5181         }
5182
5183         if ( token.Icmp( "playpdavideo" ) == 0 ) {
5184                 if ( objectiveSystem && objectiveSystemOpen && pdaVideo.Length() > 0 ) {
5185                         const idMaterial *mat = declManager->FindMaterial( pdaVideo );
5186                         if ( mat ) {
5187                                 int c = mat->GetNumStages();
5188                                 for ( int i = 0; i < c; i++ ) {
5189                                         const shaderStage_t *stage = mat->GetStage(i);
5190                                         if ( stage && stage->texture.cinematic ) {
5191                                                 stage->texture.cinematic->ResetTime( gameLocal.time );
5192                                         }
5193                                 }
5194                                 if ( pdaVideoWave.Length() ) {
5195                                         const idSoundShader *shader = declManager->FindSound( pdaVideoWave );
5196                                         StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, NULL );
5197                                 }
5198                         }
5199                 }
5200         }
5201
5202         if ( token.Icmp( "playpdaaudio" ) == 0 ) {
5203                 if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
5204                         const idSoundShader *shader = declManager->FindSound( pdaAudio );
5205                         int ms;
5206                         StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, &ms );
5207                         StartAudioLog();
5208                         CancelEvents( &EV_Player_StopAudioLog );
5209                         PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
5210                 }
5211                 return true;
5212         }
5213
5214         if ( token.Icmp( "stoppdaaudio" ) == 0 ) {
5215                 if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
5216                         // idSoundShader *shader = declManager->FindSound( pdaAudio );
5217                         StopAudioLog();
5218                         StopSound( SND_CHANNEL_PDA, false );
5219                 }
5220                 return true;
5221         }
5222
5223         src->UnreadToken( &token );
5224         return false;
5225 }
5226
5227 /*
5228 ==============
5229 idPlayer::Collide
5230 ==============
5231 */
5232 bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
5233         idEntity *other;
5234
5235         if ( gameLocal.isClient ) {
5236                 return false;
5237         }
5238
5239         other = gameLocal.entities[ collision.c.entityNum ];
5240         if ( other ) {
5241                 other->Signal( SIG_TOUCH );
5242                 if ( !spectating ) {
5243                         if ( other->RespondsTo( EV_Touch ) ) {
5244                                 other->ProcessEvent( &EV_Touch, this, &collision );
5245                         }
5246                 } else {
5247                         if ( other->RespondsTo( EV_SpectatorTouch ) ) {
5248                                 other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
5249                         }
5250                 }
5251         }
5252         return false;
5253 }
5254
5255
5256 /*
5257 ================
5258 idPlayer::UpdateLocation
5259
5260 Searches nearby locations 
5261 ================
5262 */
5263 void idPlayer::UpdateLocation( void ) {
5264         if ( hud ) {
5265                 idLocationEntity *locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
5266                 if ( locationEntity ) {
5267                         hud->SetStateString( "location", locationEntity->GetLocation() );
5268                 } else {
5269                         hud->SetStateString( "location", common->GetLanguageDict()->GetString( "#str_02911" ) );
5270                 }
5271         }
5272 }
5273
5274 /*
5275 ================
5276 idPlayer::ClearFocus
5277
5278 Clears the focus cursor
5279 ================
5280 */
5281 void idPlayer::ClearFocus( void ) {
5282         focusCharacter  = NULL;
5283         focusGUIent             = NULL;
5284         focusUI                 = NULL;
5285         focusVehicle    = NULL;
5286         talkCursor              = 0;
5287 }
5288
5289 /*
5290 ================
5291 idPlayer::UpdateFocus
5292
5293 Searches nearby entities for interactive guis, possibly making one of them
5294 the focus and sending it a mouse move event
5295 ================
5296 */
5297 void idPlayer::UpdateFocus( void ) {
5298         idClipModel *clipModelList[ MAX_GENTITIES ];
5299         idClipModel *clip;
5300         int                     listedClipModels;
5301         idEntity        *oldFocus;
5302         idEntity        *ent;
5303         idUserInterface *oldUI;
5304         idAI            *oldChar;
5305         int                     oldTalkCursor;
5306         idAFEntity_Vehicle *oldVehicle;
5307         int                     i, j;
5308         idVec3          start, end;
5309         bool            allowFocus;
5310         const char *command;
5311         trace_t         trace;
5312         guiPoint_t      pt;
5313         const idKeyValue *kv;
5314         sysEvent_t      ev;
5315         idUserInterface *ui;
5316
5317         if ( gameLocal.inCinematic ) {
5318                 return;
5319         }
5320
5321         // only update the focus character when attack button isn't pressed so players
5322         // can still chainsaw NPC's
5323         if ( gameLocal.isMultiplayer || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
5324                 allowFocus = false;
5325         } else {
5326                 allowFocus = true;
5327         }
5328
5329         oldFocus                = focusGUIent;
5330         oldUI                   = focusUI;
5331         oldChar                 = focusCharacter;
5332         oldTalkCursor   = talkCursor;
5333         oldVehicle              = focusVehicle;
5334
5335         if ( focusTime <= gameLocal.time ) {
5336                 ClearFocus();
5337         }
5338
5339         // don't let spectators interact with GUIs
5340         if ( spectating ) {
5341                 return;
5342         }
5343
5344         start = GetEyePosition();
5345         end = start + viewAngles.ToForward() * 80.0f;
5346
5347         // player identification -> names to the hud
5348         if ( gameLocal.isMultiplayer && entityNumber == gameLocal.localClientNum ) {
5349                 idVec3 end = start + viewAngles.ToForward() * 768.0f;
5350                 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
5351                 int iclient = -1;
5352                 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
5353                         iclient = trace.c.entityNum;
5354                 }
5355                 if ( MPAim != iclient ) {
5356                         lastMPAim = MPAim;
5357                         MPAim = iclient;
5358                         lastMPAimTime = gameLocal.realClientTime;
5359                 }
5360         }
5361
5362         idBounds bounds( start );
5363         bounds.AddPoint( end );
5364
5365         listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
5366
5367         // no pretense at sorting here, just assume that there will only be one active
5368         // gui within range along the trace
5369         for ( i = 0; i < listedClipModels; i++ ) {
5370                 clip = clipModelList[ i ];
5371                 ent = clip->GetEntity();
5372
5373                 if ( ent->IsHidden() ) {
5374                         continue;
5375                 }
5376
5377                 if ( allowFocus ) {
5378                         if ( ent->IsType( idAFAttachment::Type ) ) {
5379                                 idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
5380                                 if ( body && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
5381                                         gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5382                                         if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5383                                                 ClearFocus();
5384                                                 focusCharacter = static_cast<idAI *>( body );
5385                                                 talkCursor = 1;
5386                                                 focusTime = gameLocal.time + FOCUS_TIME;
5387                                                 break;
5388                                         }
5389                                 }
5390                                 continue;
5391                         }
5392
5393                         if ( ent->IsType( idAI::Type ) ) {
5394                                 if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
5395                                         gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5396                                         if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5397                                                 ClearFocus();
5398                                                 focusCharacter = static_cast<idAI *>( ent );
5399                                                 talkCursor = 1;
5400                                                 focusTime = gameLocal.time + FOCUS_TIME;
5401                                                 break;
5402                                         }
5403                                 }
5404                                 continue;
5405                         }
5406
5407                         if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
5408                                 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5409                                 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5410                                         ClearFocus();
5411                                         focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
5412                                         focusTime = gameLocal.time + FOCUS_TIME;
5413                                         break;
5414                                 }
5415                                 continue;
5416                         }
5417                 }
5418
5419                 if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
5420                         continue;
5421                 }
5422
5423                 if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
5424                         // don't allow guis on pickup items focus
5425                         continue;
5426                 }
5427
5428                 pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
5429                 if ( pt.x != -1 ) {
5430                         // we have a hit
5431                         renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
5432                         if ( !focusGUIrenderEntity ) {
5433                                 continue;
5434                         }
5435
5436                         if ( pt.guiId == 1 ) {
5437                                 ui = focusGUIrenderEntity->gui[ 0 ];
5438                         } else if ( pt.guiId == 2 ) {
5439                                 ui = focusGUIrenderEntity->gui[ 1 ];
5440                         } else {
5441                                 ui = focusGUIrenderEntity->gui[ 2 ];
5442                         }
5443                         
5444                         if ( ui == NULL ) {
5445                                 continue;
5446                         }
5447
5448                         ClearFocus();
5449                         focusGUIent = ent;
5450                         focusUI = ui;
5451
5452                         if ( oldFocus != ent ) {
5453                                 // new activation
5454                                 // going to see if we have anything in inventory a gui might be interested in
5455                                 // need to enumerate inventory items
5456                                 focusUI->SetStateInt( "inv_count", inventory.items.Num() );
5457                                 for ( j = 0; j < inventory.items.Num(); j++ ) {
5458                                         idDict *item = inventory.items[ j ];
5459                                         const char *iname = item->GetString( "inv_name" );
5460                                         const char *iicon = item->GetString( "inv_icon" );
5461                                         const char *itext = item->GetString( "inv_text" );
5462
5463                                         focusUI->SetStateString( va( "inv_name_%i", j), iname );
5464                                         focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
5465                                         focusUI->SetStateString( va( "inv_text_%i", j), itext );
5466                                         kv = item->MatchPrefix("inv_id", NULL);
5467                                         if ( kv ) {
5468                                                 focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
5469                                         }
5470                                         focusUI->SetStateInt( iname, 1 );
5471                                 }
5472
5473
5474                                 for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
5475                                         const char *p = inventory.pdaSecurity[ j ];
5476                                         if ( p && *p ) {
5477                                                 focusUI->SetStateInt( p, 1 );
5478                                         }
5479                                 }
5480
5481 #ifdef _D3XP            //BSM: Added for powercells
5482                                 int powerCellCount = 0;
5483                                 for ( j = 0; j < inventory.items.Num(); j++ ) {
5484                                         idDict *item = inventory.items[ j ];
5485                                         if(item->GetInt("inv_powercell")) {
5486                                                 powerCellCount++;
5487                                         }
5488                                 }
5489                                 focusUI->SetStateInt( "powercell_count", powerCellCount );
5490 #endif
5491
5492                                 int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
5493                                 focusUI->SetStateString( "player_health", va("%i", health ) );
5494                                 focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
5495                                 focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
5496
5497                                 kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
5498                                 while ( kv ) {
5499                                         focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
5500                                         kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
5501                                 }
5502                         }
5503
5504                         // clamp the mouse to the corner
5505                         ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
5506                         command = focusUI->HandleEvent( &ev, gameLocal.time );
5507                         HandleGuiCommands( focusGUIent, command );
5508
5509                         // move to an absolute position
5510                         ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
5511                         command = focusUI->HandleEvent( &ev, gameLocal.time );
5512                         HandleGuiCommands( focusGUIent, command );
5513                         focusTime = gameLocal.time + FOCUS_GUI_TIME;
5514                         break;
5515                 }
5516         }
5517
5518         if ( focusGUIent && focusUI ) {
5519                 if ( !oldFocus || oldFocus != focusGUIent ) {
5520                         command = focusUI->Activate( true, gameLocal.time );
5521                         HandleGuiCommands( focusGUIent, command );
5522                         StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
5523                         // HideTip();
5524                         // HideObjective();
5525                 }
5526         } else if ( oldFocus && oldUI ) {
5527                 command = oldUI->Activate( false, gameLocal.time );
5528                 HandleGuiCommands( oldFocus, command );
5529                 StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
5530         }
5531
5532         if ( cursor && ( oldTalkCursor != talkCursor ) ) {
5533                 cursor->SetStateInt( "talkcursor", talkCursor );
5534         }
5535
5536         if ( oldChar != focusCharacter && hud ) {
5537                 if ( focusCharacter ) {
5538                         hud->SetStateString( "npc", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
5539 #ifdef _D3XP
5540                         //Use to code to update the npc action string to fix bug 1159
5541                         hud->SetStateString( "npc_action", common->GetLanguageDict()->GetString( "#str_02036" ));
5542 #endif
5543                         hud->HandleNamedEvent( "showNPC" );
5544                         // HideTip();
5545                         // HideObjective();
5546                 } else {
5547                         hud->SetStateString( "npc", "" );
5548 #ifdef _D3XP
5549                         hud->SetStateString( "npc_action", "" );
5550 #endif
5551                         hud->HandleNamedEvent( "hideNPC" );
5552                 }
5553         }
5554 }
5555
5556 /*
5557 =================
5558 idPlayer::CrashLand
5559
5560 Check for hard landings that generate sound events
5561 =================
5562 */
5563 void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
5564         idVec3          origin, velocity;
5565         idVec3          gravityVector, gravityNormal;
5566         float           delta;
5567         float           hardDelta, fatalDelta;
5568         float           dist;
5569         float           vel, acc;
5570         float           t;
5571         float           a, b, c, den;
5572         waterLevel_t waterLevel;
5573         bool            noDamage;
5574
5575         AI_SOFTLANDING = false;
5576         AI_HARDLANDING = false;
5577
5578         // if the player is not on the ground
5579         if ( !physicsObj.HasGroundContacts() ) {
5580                 return;
5581         }
5582
5583         gravityNormal = physicsObj.GetGravityNormal();
5584
5585         // if the player wasn't going down
5586         if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
5587                 return;
5588         }
5589
5590         waterLevel = physicsObj.GetWaterLevel();
5591
5592         // never take falling damage if completely underwater
5593         if ( waterLevel == WATERLEVEL_HEAD ) {
5594                 return;
5595         }
5596
5597         // no falling damage if touching a nodamage surface
5598         noDamage = false;
5599         for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
5600                 const contactInfo_t &contact = physicsObj.GetContact( i );
5601                 if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
5602                         noDamage = true;
5603                         StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
5604                         break;
5605                 }
5606         }
5607
5608         origin = GetPhysics()->GetOrigin();
5609         gravityVector = physicsObj.GetGravity();
5610
5611         // calculate the exact velocity on landing
5612         dist = ( origin - oldOrigin ) * -gravityNormal;
5613         vel = oldVelocity * -gravityNormal;
5614         acc = -gravityVector.Length();
5615
5616         a = acc / 2.0f;
5617         b = vel;
5618         c = -dist;
5619
5620         den = b * b - 4.0f * a * c;
5621         if ( den < 0 ) {
5622                 return;
5623         }
5624         t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
5625
5626         delta = vel + t * acc;
5627         delta = delta * delta * 0.0001;
5628
5629         // reduce falling damage if there is standing water
5630         if ( waterLevel == WATERLEVEL_WAIST ) {
5631                 delta *= 0.25f;
5632         }
5633         if ( waterLevel == WATERLEVEL_FEET ) {
5634                 delta *= 0.5f;
5635         }
5636
5637         if ( delta < 1.0f ) {
5638                 return;
5639         }
5640
5641         // allow falling a bit further for multiplayer
5642         if ( gameLocal.isMultiplayer ) {
5643                 fatalDelta      = 75.0f;
5644                 hardDelta       = 50.0f;
5645         } else {
5646                 fatalDelta      = 65.0f;
5647                 hardDelta       = 45.0f;
5648         }
5649
5650         if ( delta > fatalDelta ) {
5651                 AI_HARDLANDING = true;
5652                 landChange = -32;
5653                 landTime = gameLocal.time;
5654                 if ( !noDamage ) {
5655                         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
5656                         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
5657                 }
5658         } else if ( delta > hardDelta ) {
5659                 AI_HARDLANDING = true;
5660                 landChange      = -24;
5661                 landTime        = gameLocal.time;
5662                 if ( !noDamage ) {
5663                         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
5664                         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
5665                 }
5666         } else if ( delta > 30 ) {
5667                 AI_HARDLANDING = true;
5668                 landChange      = -16;
5669                 landTime        = gameLocal.time;
5670                 if ( !noDamage ) {
5671                         pain_debounce_time = gameLocal.time + pain_delay + 1;  // ignore pain since we'll play our landing anim
5672                         Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
5673                 }
5674         } else if ( delta > 7 ) {
5675                 AI_SOFTLANDING = true;
5676                 landChange      = -8;
5677                 landTime        = gameLocal.time;
5678         } else if ( delta > 3 ) {
5679                 // just walk on
5680         }
5681 }
5682
5683 /*
5684 ===============
5685 idPlayer::BobCycle
5686 ===============
5687 */
5688 void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
5689         float           bobmove;
5690         int                     old, deltaTime;
5691         idVec3          vel, gravityDir, velocity;
5692         idMat3          viewaxis;
5693         float           bob;
5694         float           delta;
5695         float           speed;
5696         float           f;
5697
5698         //
5699         // calculate speed and cycle to be used for
5700         // all cyclic walking effects
5701         //
5702         velocity = physicsObj.GetLinearVelocity() - pushVelocity;
5703
5704         gravityDir = physicsObj.GetGravityNormal();
5705         vel = velocity - ( velocity * gravityDir ) * gravityDir;
5706         xyspeed = vel.LengthFast();
5707
5708         // do not evaluate the bob for other clients
5709         // when doing a spectate follow, don't do any weapon bobbing
5710         if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
5711                 viewBobAngles.Zero();
5712                 viewBob.Zero();
5713                 return;
5714         }
5715
5716         if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( gameLocal.isMultiplayer && spectating ) ) {
5717                 // airborne
5718                 bobCycle = 0;
5719                 bobFoot = 0;
5720                 bobfracsin = 0;
5721         } else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
5722                 // start at beginning of cycle again
5723                 bobCycle = 0;
5724                 bobFoot = 0;
5725                 bobfracsin = 0;
5726         } else {
5727                 if ( physicsObj.IsCrouching() ) {
5728                         bobmove = pm_crouchbob.GetFloat();
5729                         // ducked characters never play footsteps
5730                 } else {
5731                         // vary the bobbing based on the speed of the player
5732                         bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
5733                 }
5734
5735                 // check for footstep / splash sounds
5736                 old = bobCycle;
5737                 bobCycle = (int)( old + bobmove * gameLocal.msec ) & 255;
5738                 bobFoot = ( bobCycle & 128 ) >> 7;
5739                 bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
5740         }
5741
5742         // calculate angles for view bobbing
5743         viewBobAngles.Zero();
5744
5745         viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
5746
5747         // add angles based on velocity
5748         delta = velocity * viewaxis[0];
5749         viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
5750         
5751         delta = velocity * viewaxis[1];
5752         viewBobAngles.roll -= delta * pm_runroll.GetFloat();
5753
5754         // add angles based on bob
5755         // make sure the bob is visible even at low speeds
5756         speed = xyspeed > 200 ? xyspeed : 200;
5757
5758         delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
5759         if ( physicsObj.IsCrouching() ) {
5760                 delta *= 3;             // crouching
5761         }
5762         viewBobAngles.pitch += delta;
5763         delta = bobfracsin * pm_bobroll.GetFloat() * speed;
5764         if ( physicsObj.IsCrouching() ) {
5765                 delta *= 3;             // crouching accentuates roll
5766         }
5767         if ( bobFoot & 1 ) {
5768                 delta = -delta;
5769         }
5770         viewBobAngles.roll += delta;
5771
5772         // calculate position for view bobbing
5773         viewBob.Zero();
5774
5775         if ( physicsObj.HasSteppedUp() ) {
5776
5777                 // check for stepping up before a previous step is completed
5778                 deltaTime = gameLocal.time - stepUpTime;
5779                 if ( deltaTime < STEPUP_TIME ) {
5780                         stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
5781                 } else {
5782                         stepUpDelta = physicsObj.GetStepUp();
5783                 }
5784                 if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
5785                         stepUpDelta = 2.0f * pm_stepsize.GetFloat();
5786                 }
5787                 stepUpTime = gameLocal.time;
5788         }
5789
5790         idVec3 gravity = physicsObj.GetGravityNormal();
5791
5792         // if the player stepped up recently
5793         deltaTime = gameLocal.time - stepUpTime;
5794         if ( deltaTime < STEPUP_TIME ) {
5795                 viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
5796         }
5797
5798         // add bob height after any movement smoothing
5799         bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
5800         if ( bob > 6 ) {
5801                 bob = 6;
5802         }
5803         viewBob[2] += bob;
5804
5805         // add fall height
5806         delta = gameLocal.time - landTime;
5807         if ( delta < LAND_DEFLECT_TIME ) {
5808                 f = delta / LAND_DEFLECT_TIME;
5809                 viewBob -= gravity * ( landChange * f );
5810         } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
5811                 delta -= LAND_DEFLECT_TIME;
5812                 f = 1.0 - ( delta / LAND_RETURN_TIME );
5813                 viewBob -= gravity * ( landChange * f );
5814         }
5815 }
5816
5817 /*
5818 ================
5819 idPlayer::UpdateDeltaViewAngles
5820 ================
5821 */
5822 void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
5823         // set the delta angle
5824         idAngles delta;
5825         for( int i = 0; i < 3; i++ ) {
5826                 delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
5827         }
5828         SetDeltaViewAngles( delta );
5829 }
5830
5831 /*
5832 ================
5833 idPlayer::SetViewAngles
5834 ================
5835 */
5836 void idPlayer::SetViewAngles( const idAngles &angles ) {
5837         UpdateDeltaViewAngles( angles );
5838         viewAngles = angles;
5839 }
5840
5841 /*
5842 ================
5843 idPlayer::UpdateViewAngles
5844 ================
5845 */
5846 void idPlayer::UpdateViewAngles( void ) {
5847         int i;
5848         idAngles delta;
5849
5850         if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
5851                 // no view changes at all, but we still want to update the deltas or else when
5852                 // we get out of this mode, our view will snap to a kind of random angle
5853                 UpdateDeltaViewAngles( viewAngles );
5854                 return;
5855         }
5856
5857         // if dead
5858         if ( health <= 0 ) {
5859                 if ( pm_thirdPersonDeath.GetBool() ) {
5860                         viewAngles.roll = 0.0f;
5861                         viewAngles.pitch = 30.0f;
5862                 } else {
5863                         viewAngles.roll = 40.0f;
5864                         viewAngles.pitch = -15.0f;
5865                 }
5866                 return;
5867         }
5868
5869         // circularly clamp the angles with deltas
5870         for ( i = 0; i < 3; i++ ) {
5871                 cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
5872                 if ( influenceActive == INFLUENCE_LEVEL3 ) {
5873                         viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
5874                 } else {
5875                         viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] );
5876                 }
5877         }
5878         if ( !centerView.IsDone( gameLocal.time ) ) {
5879                 viewAngles.pitch = centerView.GetCurrentValue(gameLocal.time);
5880         }
5881
5882         // clamp the pitch
5883         if ( noclip ) {
5884                 if ( viewAngles.pitch > 89.0f ) {
5885                         // don't let the player look down more than 89 degrees while noclipping
5886                         viewAngles.pitch = 89.0f;
5887                 } else if ( viewAngles.pitch < -89.0f ) {
5888                         // don't let the player look up more than 89 degrees while noclipping
5889                         viewAngles.pitch = -89.0f;
5890                 }
5891 #ifdef _D3XP
5892         } else if ( mountedObject ) {
5893                 int yaw_min, yaw_max, varc;
5894
5895                 mountedObject->GetAngleRestrictions( yaw_min, yaw_max, varc );
5896
5897                 if ( yaw_min < yaw_max ) {
5898                         viewAngles.yaw = idMath::ClampFloat( yaw_min, yaw_max, viewAngles.yaw );
5899                 } else {
5900                         if ( viewAngles.yaw < 0 ) {
5901                                 viewAngles.yaw = idMath::ClampFloat( -180.f, yaw_max, viewAngles.yaw );
5902                         } else {
5903                                 viewAngles.yaw = idMath::ClampFloat( yaw_min, 180.f, viewAngles.yaw );
5904                         }
5905                 }
5906                 viewAngles.pitch = idMath::ClampFloat( -varc, varc, viewAngles.pitch );
5907 #endif
5908         } else {
5909                 if ( viewAngles.pitch > pm_maxviewpitch.GetFloat() ) {
5910                         // don't let the player look down enough to see the shadow of his (non-existant) feet
5911                         viewAngles.pitch = pm_maxviewpitch.GetFloat();
5912                 } else if ( viewAngles.pitch < pm_minviewpitch.GetFloat() ) {
5913                         // don't let the player look up more than 89 degrees
5914                         viewAngles.pitch = pm_minviewpitch.GetFloat();
5915                 }
5916         }
5917
5918         UpdateDeltaViewAngles( viewAngles );
5919
5920         // orient the model towards the direction we're looking
5921         SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
5922
5923         // save in the log for analyzing weapon angle offsets
5924         loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
5925 }
5926
5927 /*
5928 ==============
5929 idPlayer::AdjustHeartRate
5930
5931 Player heartrate works as follows
5932
5933 DEF_HEARTRATE is resting heartrate
5934
5935 Taking damage when health is above 75 adjusts heart rate by 1 beat per second
5936 Taking damage when health is below 75 adjusts heart rate by 5 beats per second
5937 Maximum heartrate from damage is MAX_HEARTRATE
5938
5939 Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
5940
5941 Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
5942
5943 All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
5944 Once it starts falling it always tries to get to DEF_HEARTRATE
5945
5946 The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling 
5947 immediately to zero
5948
5949 Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is 
5950 scaled linearly based on the actual rate
5951
5952 Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
5953 it is audible or -10db and scales to 8db on the last few beats
5954 ==============
5955 */
5956 void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
5957
5958         if ( heartInfo.GetEndValue() == target ) {
5959                 return;
5960         }
5961
5962         if ( AI_DEAD && !force ) {
5963                 return;
5964         }
5965
5966     lastHeartAdjust = gameLocal.time;
5967
5968         heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
5969 }
5970
5971 /*
5972 ==============
5973 idPlayer::GetBaseHeartRate
5974 ==============
5975 */
5976 int idPlayer::GetBaseHeartRate( void ) {
5977         int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
5978         int rate = idMath::FtoiFast( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
5979         int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
5980         rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
5981         return rate;
5982 }
5983
5984 /*
5985 ==============
5986 idPlayer::SetCurrentHeartRate
5987 ==============
5988 */
5989 void idPlayer::SetCurrentHeartRate( void ) {
5990
5991         int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
5992
5993         if ( PowerUpActive( ADRENALINE )) {
5994                 heartRate = 135;
5995         } else {
5996                 heartRate = idMath::FtoiFast( heartInfo.GetCurrentValue( gameLocal.time ) );
5997                 int currentRate = GetBaseHeartRate();
5998                 if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
5999                         AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
6000                 }
6001         }
6002
6003         int bps = idMath::FtoiFast( 60.0f / heartRate * 1000.0f );
6004         if ( gameLocal.time - lastHeartBeat > bps ) {
6005                 int dmgVol = DMG_VOLUME;
6006                 int deathVol = DEATH_VOLUME;
6007                 int zeroVol = ZERO_VOLUME;
6008                 float pct = 0.0;
6009                 if ( heartRate > BASE_HEARTRATE && health > 0 ) {
6010                         pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
6011                         pct *= ((float)dmgVol - (float)zeroVol);
6012                 } else if ( health <= 0 ) {
6013                         pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
6014                         if ( pct > 1.0f ) {
6015                                 pct = 1.0f;
6016                         } else if (pct < 0.0f) {
6017                                 pct = 0.0f;
6018                         }
6019                         pct *= ((float)deathVol - (float)zeroVol);
6020                 } 
6021
6022                 pct += (float)zeroVol;
6023
6024                 if ( pct != zeroVol ) {
6025                         StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
6026                         // modify just this channel to a custom volume
6027                         soundShaderParms_t      parms;
6028                         memset( &parms, 0, sizeof( parms ) );
6029                         parms.volume = pct;
6030                         refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
6031                 }
6032
6033                 lastHeartBeat = gameLocal.time;
6034         }
6035 }
6036
6037 /*
6038 ==============
6039 idPlayer::UpdateAir
6040 ==============
6041 */
6042 void idPlayer::UpdateAir( void ) {      
6043         if ( health <= 0 ) {
6044                 return;
6045         }
6046
6047         // see if the player is connected to the info_vacuum
6048         bool    newAirless = false;
6049
6050         if ( gameLocal.vacuumAreaNum != -1 ) {
6051                 int     num = GetNumPVSAreas();
6052                 if ( num > 0 ) {
6053                         int             areaNum;
6054
6055                         // if the player box spans multiple areas, get the area from the origin point instead,
6056                         // otherwise a rotating player box may poke into an outside area
6057                         if ( num == 1 ) {
6058                                 const int       *pvsAreas = GetPVSAreas();
6059                                 areaNum = pvsAreas[0];
6060                         } else {
6061                                 areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
6062                         }
6063                         newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
6064                 }
6065         }
6066
6067 #ifdef _D3XP
6068         if ( PowerUpActive( ENVIROTIME ) ) {
6069                 newAirless = false;
6070         }
6071 #endif
6072
6073         if ( newAirless ) {
6074                 if ( !airless ) {
6075                         StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
6076                         StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
6077                         if ( hud ) {
6078                                 hud->HandleNamedEvent( "noAir" );
6079                         }
6080                 }
6081                 airTics--;
6082                 if ( airTics < 0 ) {
6083                         airTics = 0;
6084                         // check for damage
6085                         const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
6086                         int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
6087                         if ( gameLocal.time > lastAirDamage + dmgTiming ) {
6088                                 Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
6089                                 lastAirDamage = gameLocal.time;
6090                         }
6091                 }
6092                 
6093         } else {
6094                 if ( airless ) {
6095                         StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
6096                         StopSound( SND_CHANNEL_BODY2, false );
6097                         if ( hud ) {
6098                                 hud->HandleNamedEvent( "Air" );
6099                         }
6100                 }
6101                 airTics+=2;     // regain twice as fast as lose
6102                 if ( airTics > pm_airTics.GetInteger() ) {
6103                         airTics = pm_airTics.GetInteger();
6104                 }
6105         }
6106
6107         airless = newAirless;
6108
6109         if ( hud ) {
6110                 hud->SetStateInt( "player_air", 100 * airTics / pm_airTics.GetInteger() );
6111         }
6112 }
6113
6114 void idPlayer::UpdatePowerupHud() {
6115         
6116         if ( health <= 0 ) {
6117                 return;
6118         }
6119
6120         if(lastHudPowerup != hudPowerup) {
6121                 
6122                 if(hudPowerup == -1) {
6123                         //The powerup hud should be turned off
6124                         if ( hud ) {
6125                                 hud->HandleNamedEvent( "noPowerup" );
6126                         }
6127                 } else {
6128                         //Turn the pwoerup hud on
6129                         if ( hud ) {
6130                                 hud->HandleNamedEvent( "Powerup" );
6131                         }
6132                 }
6133
6134                 lastHudPowerup = hudPowerup;
6135         }
6136
6137         if(hudPowerup != -1) {
6138                 if(PowerUpActive(hudPowerup)) {
6139                         int remaining = inventory.powerupEndTime[ hudPowerup ] - gameLocal.time;
6140                         int filledbar = idMath::ClampInt( 0, hudPowerupDuration, remaining );
6141                         
6142                         if ( hud ) {
6143                                 hud->SetStateInt( "player_powerup", 100 * filledbar / hudPowerupDuration );
6144                                 hud->SetStateInt( "player_poweruptime", remaining / 1000 );
6145                         }
6146                 }
6147         }
6148 }
6149
6150 /*
6151 ==============
6152 idPlayer::AddGuiPDAData
6153 ==============
6154  */
6155 int idPlayer::AddGuiPDAData( const declType_t dataType, const char *listName, const idDeclPDA *src, idUserInterface *gui ) {
6156         int c, i;
6157         idStr work;
6158         if ( dataType == DECL_EMAIL ) {
6159                 c = src->GetNumEmails();
6160                 for ( i = 0; i < c; i++ ) {
6161                         const idDeclEmail *email = src->GetEmailByIndex( i );
6162                         if ( email == NULL ) {
6163                                 work = va( "-\tEmail %d not found\t-", i );
6164                         } else {
6165                                 work = email->GetFrom();
6166                                 work += "\t";
6167                                 work += email->GetSubject();
6168                                 work += "\t";
6169                                 work += email->GetDate();
6170                         }
6171                         gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6172                 }
6173                 return c;
6174         } else if ( dataType == DECL_AUDIO ) {
6175                 c = src->GetNumAudios();
6176                 for ( i = 0; i < c; i++ ) {
6177                         const idDeclAudio *audio = src->GetAudioByIndex( i );
6178                         if ( audio == NULL ) {
6179                                 work = va( "Audio Log %d not found", i );
6180                         } else {
6181                                 work = audio->GetAudioName();
6182                         }
6183                         gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6184                 }
6185                 return c;
6186         } else if ( dataType == DECL_VIDEO ) {
6187                 c = inventory.videos.Num();
6188                 for ( i = 0; i < c; i++ ) {
6189                         const idDeclVideo *video = GetVideo( i );
6190                         if ( video == NULL ) {
6191                                 work = va( "Video CD %s not found", inventory.videos[i].c_str() );
6192                         } else {
6193                                 work = video->GetVideoName();
6194                         }
6195                         gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6196                 }
6197                 return c;
6198         }
6199         return 0;
6200 }
6201
6202 /*
6203 ==============
6204 idPlayer::GetPDA
6205 ==============
6206  */
6207 const idDeclPDA *idPlayer::GetPDA( void ) const {
6208         if ( inventory.pdas.Num() ) {
6209                 return static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[ 0 ] ) );
6210         } else {
6211                 return NULL;
6212         }
6213 }
6214
6215
6216 /*
6217 ==============
6218 idPlayer::GetVideo
6219 ==============
6220 */
6221 const idDeclVideo *idPlayer::GetVideo( int index ) {
6222         if ( index >= 0 && index < inventory.videos.Num() ) {
6223                 return static_cast< const idDeclVideo* >( declManager->FindType( DECL_VIDEO, inventory.videos[index], false ) );
6224         }
6225         return NULL;
6226 }
6227
6228
6229 /*
6230 ==============
6231 idPlayer::UpdatePDAInfo
6232 ==============
6233 */
6234 void idPlayer::UpdatePDAInfo( bool updatePDASel ) {
6235         int j, sel;
6236
6237         if ( objectiveSystem == NULL ) {
6238                 return;
6239         }
6240
6241         assert( hud );
6242
6243         int currentPDA = objectiveSystem->State().GetInt( "listPDA_sel_0", "0" );
6244         if ( currentPDA == -1 ) {
6245                 currentPDA = 0;
6246         }
6247
6248         if ( updatePDASel ) {
6249                 objectiveSystem->SetStateInt( "listPDAVideo_sel_0", 0 );
6250                 objectiveSystem->SetStateInt( "listPDAEmail_sel_0", 0 );
6251                 objectiveSystem->SetStateInt( "listPDAAudio_sel_0", 0 );
6252         }
6253
6254         if ( currentPDA > 0 ) {
6255                 currentPDA = inventory.pdas.Num() - currentPDA;
6256         }
6257
6258         // Mark in the bit array that this pda has been read
6259         if ( currentPDA < 128 ) {
6260                 inventory.pdasViewed[currentPDA >> 5] |= 1 << (currentPDA & 31);
6261         }
6262
6263         pdaAudio = "";
6264         pdaVideo = "";
6265         pdaVideoWave = "";
6266         idStr name, data, preview, info, wave;
6267         for ( j = 0; j < MAX_PDAS; j++ ) {
6268                 objectiveSystem->SetStateString( va( "listPDA_item_%i", j ), "" );
6269         }
6270         for ( j = 0; j < MAX_PDA_ITEMS; j++ ) {
6271                 objectiveSystem->SetStateString( va( "listPDAVideo_item_%i", j ), "" );
6272                 objectiveSystem->SetStateString( va( "listPDAAudio_item_%i", j ), "" );
6273                 objectiveSystem->SetStateString( va( "listPDAEmail_item_%i", j ), "" );
6274                 objectiveSystem->SetStateString( va( "listPDASecurity_item_%i", j ), "" );
6275         }
6276         for ( j = 0; j < inventory.pdas.Num(); j++ ) {
6277
6278                 const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[j], false ) );
6279
6280                 if ( pda == NULL ) {
6281                         continue;
6282                 }
6283
6284                 int index = inventory.pdas.Num() - j;
6285                 if ( j == 0 ) {
6286                         // Special case for the first PDA
6287                         index = 0;
6288                 }
6289
6290                 if ( j != currentPDA && j < 128 && inventory.pdasViewed[j >> 5] & (1 << (j & 31)) ) {
6291                         // This pda has been read already, mark in gray
6292                         objectiveSystem->SetStateString( va( "listPDA_item_%i", index), va(S_COLOR_GRAY "%s", pda->GetPdaName()) );
6293                 } else {
6294                         // This pda has not been read yet
6295                 objectiveSystem->SetStateString( va( "listPDA_item_%i", index), pda->GetPdaName() );
6296                 }
6297
6298                 const char *security = pda->GetSecurity();
6299                 if ( j == currentPDA || (currentPDA == 0 && security && *security ) ) {
6300                         if ( *security == NULL ) {
6301                                 security = common->GetLanguageDict()->GetString( "#str_00066" );
6302                         }
6303                         objectiveSystem->SetStateString( "PDASecurityClearance", security );
6304                 }
6305
6306                 if ( j == currentPDA ) {
6307
6308                         objectiveSystem->SetStateString( "pda_icon", pda->GetIcon() );
6309                         objectiveSystem->SetStateString( "pda_id", pda->GetID() );
6310                         objectiveSystem->SetStateString( "pda_title", pda->GetTitle() );
6311
6312                         if ( j == 0 ) {
6313                                 // Selected, personal pda
6314                                 // Add videos
6315                                 if ( updatePDASel || !inventory.pdaOpened ) {
6316                                 objectiveSystem->HandleNamedEvent( "playerPDAActive" );
6317                                 objectiveSystem->SetStateString( "pda_personal", "1" );
6318                                         inventory.pdaOpened = true;
6319                                 }
6320                                 objectiveSystem->SetStateString( "pda_location", hud->State().GetString("location") );
6321                                 objectiveSystem->SetStateString( "pda_name", cvarSystem->GetCVarString( "ui_name") );
6322                                 AddGuiPDAData( DECL_VIDEO, "listPDAVideo", pda, objectiveSystem );
6323                                 sel = objectiveSystem->State().GetInt( "listPDAVideo_sel_0", "0" );
6324                                 const idDeclVideo *vid = NULL;
6325                                 if ( sel >= 0 && sel < inventory.videos.Num() ) {
6326                                         vid = static_cast< const idDeclVideo * >( declManager->FindType( DECL_VIDEO, inventory.videos[ sel ], false ) );
6327                                 }
6328                                 if ( vid ) {
6329                                         pdaVideo = vid->GetRoq();
6330                                         pdaVideoWave = vid->GetWave();
6331                                         objectiveSystem->SetStateString( "PDAVideoTitle", vid->GetVideoName() );
6332                                         objectiveSystem->SetStateString( "PDAVideoVid", vid->GetRoq() );
6333                                         objectiveSystem->SetStateString( "PDAVideoIcon", vid->GetPreview() );
6334                                         objectiveSystem->SetStateString( "PDAVideoInfo", vid->GetInfo() );
6335                                 } else {
6336                                         //FIXME: need to precache these in the player def
6337                                         objectiveSystem->SetStateString( "PDAVideoVid", "sound/vo/video/welcome.tga" );
6338                                         objectiveSystem->SetStateString( "PDAVideoIcon", "sound/vo/video/welcome.tga" );
6339                                         objectiveSystem->SetStateString( "PDAVideoTitle", "" );
6340                                         objectiveSystem->SetStateString( "PDAVideoInfo", "" );
6341                                 }
6342                         } else {
6343                                 // Selected, non-personal pda
6344                                 // Add audio logs
6345                                 if ( updatePDASel ) {
6346                                 objectiveSystem->HandleNamedEvent( "playerPDANotActive" );
6347                                 objectiveSystem->SetStateString( "pda_personal", "0" );
6348                                         inventory.pdaOpened = true;
6349                                 }
6350                                 objectiveSystem->SetStateString( "pda_location", pda->GetPost() );
6351                                 objectiveSystem->SetStateString( "pda_name", pda->GetFullName() );
6352                                 int audioCount = AddGuiPDAData( DECL_AUDIO, "listPDAAudio", pda, objectiveSystem );
6353                                 objectiveSystem->SetStateInt( "audioLogCount", audioCount );
6354                                 sel = objectiveSystem->State().GetInt( "listPDAAudio_sel_0", "0" );
6355                                 const idDeclAudio *aud = NULL;
6356                                 if ( sel >= 0 ) {
6357                                         aud = pda->GetAudioByIndex( sel );
6358                                 }
6359                                 if ( aud ) {
6360                                         pdaAudio = aud->GetWave();
6361                                         objectiveSystem->SetStateString( "PDAAudioTitle", aud->GetAudioName() );
6362                                         objectiveSystem->SetStateString( "PDAAudioIcon", aud->GetPreview() );
6363                                         objectiveSystem->SetStateString( "PDAAudioInfo", aud->GetInfo() );
6364                                 } else {
6365                                         objectiveSystem->SetStateString( "PDAAudioIcon", "sound/vo/video/welcome.tga" );
6366                                         objectiveSystem->SetStateString( "PDAAutioTitle", "" );
6367                                         objectiveSystem->SetStateString( "PDAAudioInfo", "" );
6368                                 }
6369                         }
6370                         // add emails
6371                         name = "";
6372                         data = "";
6373                         int numEmails = pda->GetNumEmails();
6374                         if ( numEmails > 0 ) {
6375                                 AddGuiPDAData( DECL_EMAIL, "listPDAEmail", pda, objectiveSystem );
6376                                 sel = objectiveSystem->State().GetInt( "listPDAEmail_sel_0", "-1" );
6377                                 if ( sel >= 0 && sel < numEmails ) {
6378                                         const idDeclEmail *email = pda->GetEmailByIndex( sel );
6379                                         name = email->GetSubject();
6380                                         data = email->GetBody();
6381                                 }
6382                         }
6383                         objectiveSystem->SetStateString( "PDAEmailTitle", name );
6384                         objectiveSystem->SetStateString( "PDAEmailText", data );
6385                 }
6386         }
6387         if ( objectiveSystem->State().GetInt( "listPDA_sel_0", "-1" ) == -1 ) {
6388                 objectiveSystem->SetStateInt( "listPDA_sel_0", 0 );
6389         }
6390         objectiveSystem->StateChanged( gameLocal.time );
6391 }
6392
6393 /*
6394 ==============
6395 idPlayer::TogglePDA
6396 ==============
6397 */
6398 void idPlayer::TogglePDA( void ) {
6399         if ( objectiveSystem == NULL ) {
6400                 return;
6401         }
6402
6403         if ( inventory.pdas.Num() == 0 ) {
6404                 ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
6405                 return;
6406         }
6407
6408         assert( hud );
6409
6410         if ( !objectiveSystemOpen ) {
6411                 int j, c = inventory.items.Num();
6412                 objectiveSystem->SetStateInt( "inv_count", c );
6413                 for ( j = 0; j < MAX_INVENTORY_ITEMS; j++ ) {
6414                         objectiveSystem->SetStateString( va( "inv_name_%i", j ), "" );
6415                         objectiveSystem->SetStateString( va( "inv_icon_%i", j ), "" );
6416                         objectiveSystem->SetStateString( va( "inv_text_%i", j ), "" );
6417                 }
6418                 for ( j = 0; j < c; j++ ) {
6419                         idDict *item = inventory.items[j];
6420                         if ( !item->GetBool( "inv_pda" ) ) {
6421                                 const char *iname = item->GetString( "inv_name" );
6422                                 const char *iicon = item->GetString( "inv_icon" );
6423                                 const char *itext = item->GetString( "inv_text" );
6424                                 objectiveSystem->SetStateString( va( "inv_name_%i", j ), iname );
6425                                 objectiveSystem->SetStateString( va( "inv_icon_%i", j ), iicon );
6426                                 objectiveSystem->SetStateString( va( "inv_text_%i", j ), itext );
6427                                 const idKeyValue *kv = item->MatchPrefix( "inv_id", NULL );
6428                                 if ( kv ) {
6429                                         objectiveSystem->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
6430                                 }
6431                         }
6432                 }
6433
6434                 for ( j = 0; j < MAX_WEAPONS; j++ ) {
6435                         const char *weapnum = va( "def_weapon%d", j );
6436                         const char *hudWeap = va( "weapon%d", j );
6437                         int weapstate = 0;
6438                         if ( inventory.weapons & ( 1 << j ) ) {
6439                                 const char *weap = spawnArgs.GetString( weapnum );
6440                                 if ( weap && *weap ) {
6441                                         weapstate++;
6442                                 }
6443                         }
6444                         objectiveSystem->SetStateInt( hudWeap, weapstate );
6445                 }
6446
6447                 objectiveSystem->SetStateInt( "listPDA_sel_0", inventory.selPDA );
6448                 objectiveSystem->SetStateInt( "listPDAVideo_sel_0", inventory.selVideo );
6449                 objectiveSystem->SetStateInt( "listPDAAudio_sel_0", inventory.selAudio );
6450                 objectiveSystem->SetStateInt( "listPDAEmail_sel_0", inventory.selEMail );
6451                 UpdatePDAInfo( false );
6452                 UpdateObjectiveInfo();
6453                 objectiveSystem->Activate( true, gameLocal.time );
6454                 hud->HandleNamedEvent( "pdaPickupHide" );
6455                 hud->HandleNamedEvent( "videoPickupHide" );
6456         } else {
6457                 inventory.selPDA = objectiveSystem->State().GetInt( "listPDA_sel_0" );
6458                 inventory.selVideo = objectiveSystem->State().GetInt( "listPDAVideo_sel_0" );
6459                 inventory.selAudio = objectiveSystem->State().GetInt( "listPDAAudio_sel_0" );
6460                 inventory.selEMail = objectiveSystem->State().GetInt( "listPDAEmail_sel_0" );
6461                 objectiveSystem->Activate( false, gameLocal.time );
6462         }
6463         objectiveSystemOpen ^= 1;
6464 }
6465
6466 /*
6467 ==============
6468 idPlayer::ToggleScoreboard
6469 ==============
6470 */
6471 void idPlayer::ToggleScoreboard( void ) {
6472         scoreBoardOpen ^= 1;
6473 }
6474
6475 /*
6476 ==============
6477 idPlayer::Spectate
6478 ==============
6479 */
6480 void idPlayer::Spectate( bool spectate ) {
6481         idBitMsg        msg;
6482         byte            msgBuf[MAX_EVENT_PARAM_SIZE];
6483
6484         // track invisible player bug
6485         // all hiding and showing should be performed through Spectate calls
6486         // except for the private camera view, which is used for teleports
6487         assert( ( teleportEntity.GetEntity() != NULL ) || ( IsHidden() == spectating ) );
6488
6489         if ( spectating == spectate ) {
6490                 return;
6491         }
6492
6493         spectating = spectate;
6494
6495         if ( gameLocal.isServer ) {
6496                 msg.Init( msgBuf, sizeof( msgBuf ) );
6497                 msg.WriteBits( spectating, 1 );
6498                 ServerSendEvent( EVENT_SPECTATE, &msg, false, -1 );
6499         }
6500
6501         if ( spectating ) {
6502                 // join the spectators
6503                 ClearPowerUps();
6504                 spectator = this->entityNumber;
6505                 Init();
6506                 StopRagdoll();
6507                 SetPhysics( &physicsObj );
6508                 physicsObj.DisableClip();
6509                 Hide();
6510                 Event_DisableWeapon();
6511                 if ( hud ) {
6512                         hud->HandleNamedEvent( "aim_clear" );
6513                         MPAimFadeTime = 0;
6514                 }
6515         } else {
6516                 // put everything back together again
6517                 currentWeapon = -1;     // to make sure the def will be loaded if necessary
6518                 Show();
6519                 Event_EnableWeapon();
6520         }
6521         SetClipModel();
6522 }
6523
6524 /*
6525 ==============
6526 idPlayer::SetClipModel
6527 ==============
6528 */
6529 void idPlayer::SetClipModel( void ) {
6530         idBounds bounds;
6531
6532         if ( spectating ) {
6533                 bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
6534         } else {
6535                 bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
6536                 bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
6537         }
6538         // the origin of the clip model needs to be set before calling SetClipModel
6539         // otherwise our physics object's current origin value gets reset to 0
6540         idClipModel *newClip;
6541         if ( pm_usecylinder.GetBool() ) {
6542                 newClip = new idClipModel( idTraceModel( bounds, 8 ) );
6543                 newClip->Translate( physicsObj.PlayerGetOrigin() );
6544                 physicsObj.SetClipModel( newClip, 1.0f );
6545         } else {
6546                 newClip = new idClipModel( idTraceModel( bounds ) );
6547                 newClip->Translate( physicsObj.PlayerGetOrigin() );
6548                 physicsObj.SetClipModel( newClip, 1.0f );
6549         }
6550 }
6551
6552 /*
6553 ==============
6554 idPlayer::UseVehicle
6555 ==============
6556 */
6557 void idPlayer::UseVehicle( void ) {
6558         trace_t trace;
6559         idVec3 start, end;
6560         idEntity *ent;
6561
6562         if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
6563                 Show();
6564                 static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
6565         } else {
6566                 start = GetEyePosition();
6567                 end = start + viewAngles.ToForward() * 80.0f;
6568                 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
6569                 if ( trace.fraction < 1.0f ) {
6570                         ent = gameLocal.entities[ trace.c.entityNum ];
6571                         if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
6572                                 Hide();
6573                                 static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
6574                         }
6575                 }
6576         }
6577 }
6578
6579 /*
6580 ==============
6581 idPlayer::PerformImpulse
6582 ==============
6583 */
6584 void idPlayer::PerformImpulse( int impulse ) {
6585
6586         if ( gameLocal.isClient ) {
6587                 idBitMsg        msg;
6588                 byte            msgBuf[MAX_EVENT_PARAM_SIZE];
6589
6590                 assert( entityNumber == gameLocal.localClientNum );
6591                 msg.Init( msgBuf, sizeof( msgBuf ) );
6592                 msg.BeginWriting();
6593                 msg.WriteBits( impulse, 6 );
6594                 ClientSendEvent( EVENT_IMPULSE, &msg );
6595         }
6596
6597         if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 ) {
6598                 SelectWeapon( impulse, false );
6599                 return;
6600         }
6601
6602         switch( impulse ) {
6603                 case IMPULSE_13: {
6604                         Reload();
6605                         break;
6606                 }
6607                 case IMPULSE_14: {
6608                         NextWeapon();
6609                         break;
6610                 }
6611                 case IMPULSE_15: {
6612                         PrevWeapon();
6613                         break;
6614                 }
6615                 case IMPULSE_17: {
6616                         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6617                                 gameLocal.mpGame.ToggleReady();
6618                         }
6619                         break;
6620                 }
6621                 case IMPULSE_18: {
6622                         centerView.Init(gameLocal.time, 200, viewAngles.pitch, 0);
6623                         break;
6624                 }
6625                 case IMPULSE_19: {
6626                         // when we're not in single player, IMPULSE_19 is used for showScores
6627                         // otherwise it opens the pda
6628                         if ( !gameLocal.isMultiplayer ) {
6629                                 if ( objectiveSystemOpen ) {
6630                                         TogglePDA();
6631                                 } else if ( weapon_pda >= 0 ) {
6632                                         SelectWeapon( weapon_pda, true );
6633                                 }
6634                         }
6635                         break;
6636                 }
6637                 case IMPULSE_20: {
6638                         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6639                                 gameLocal.mpGame.ToggleTeam();
6640                         }
6641                         break;
6642                 }
6643                 case IMPULSE_22: {
6644                         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6645                                 gameLocal.mpGame.ToggleSpectate();
6646                         }
6647                         break;
6648                 }
6649
6650                 case IMPULSE_25: {
6651                         if ( gameLocal.isServer && gameLocal.mpGame.IsGametypeFlagBased() && (gameLocal.serverInfo.GetInt( "si_midnight" ) == 2) ) {
6652                                 if ( enviroSuitLight.IsValid() ) {
6653                                         enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
6654                                         enviroSuitLight = NULL;
6655                                 } else {
6656                                         const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
6657                                         if ( lightDef ) {
6658                                                 idEntity *temp = static_cast<idEntity *>(enviroSuitLight.GetEntity());
6659                                                 idAngles lightAng = firstPersonViewAxis.ToAngles();
6660                                                 idVec3 lightOrg = firstPersonViewOrigin;
6661
6662                                                 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
6663                                                 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
6664
6665                                                 gameLocal.SpawnEntityDef( *lightDef, &temp, false );
6666                                                 enviroSuitLight = static_cast<idLight *>(temp);
6667
6668                                                 enviroSuitLight.GetEntity()->fl.networkSync = true;
6669
6670                                                 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
6671                                                 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
6672                                                 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
6673                                                 lightAng.pitch += enviroAngleOffset.x;
6674                                                 lightAng.yaw += enviroAngleOffset.y;
6675                                                 lightAng.roll += enviroAngleOffset.z;
6676
6677                                                 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
6678                                                 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
6679
6680                                                 enviroSuitLight.GetEntity()->UpdateVisuals();
6681                                                 enviroSuitLight.GetEntity()->Present();
6682                                         }
6683                                 }
6684                         }
6685                         break;
6686                 }
6687
6688                 case IMPULSE_28: {
6689                         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6690                                 gameLocal.mpGame.CastVote( gameLocal.localClientNum, true );
6691                         }
6692                         break;
6693                 }
6694                 case IMPULSE_29: {
6695                         if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6696                                 gameLocal.mpGame.CastVote( gameLocal.localClientNum, false );
6697                         }
6698                         break;
6699                 }
6700                 case IMPULSE_40: {
6701                         UseVehicle();
6702                         break;
6703                 }
6704 #ifdef _D3XP
6705                  //Hack so the chainsaw will work in MP
6706                 case IMPULSE_27: {
6707                         SelectWeapon(18, false);
6708                         break;
6709                 }
6710 #endif
6711         } 
6712 }
6713
6714 bool idPlayer::HandleESC( void ) {
6715         if ( gameLocal.inCinematic ) {
6716                 return SkipCinematic();
6717         }
6718
6719         if ( objectiveSystemOpen ) {
6720                 TogglePDA();
6721                 return true;
6722         }
6723
6724         return false;
6725 }
6726
6727 bool idPlayer::SkipCinematic( void ) {
6728         StartSound( "snd_skipcinematic", SND_CHANNEL_ANY, 0, false, NULL );
6729         return gameLocal.SkipCinematic();
6730 }
6731
6732 /*
6733 ==============
6734 idPlayer::EvaluateControls
6735 ==============
6736 */
6737 void idPlayer::EvaluateControls( void ) {
6738         // check for respawning
6739         if ( health <= 0 ) {
6740                 if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
6741                         forceRespawn = true;
6742                 } else if ( gameLocal.time > maxRespawnTime ) {
6743                         forceRespawn = true;
6744                 }
6745         }
6746
6747         // in MP, idMultiplayerGame decides spawns
6748         if ( forceRespawn && !gameLocal.isMultiplayer && !g_testDeath.GetBool() ) {
6749                 // in single player, we let the session handle restarting the level or loading a game
6750                 gameLocal.sessionCommand = "died";
6751         }
6752
6753         if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
6754                 PerformImpulse( usercmd.impulse );
6755         }
6756
6757         scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
6758
6759         oldFlags = usercmd.flags;
6760
6761         AdjustSpeed();
6762
6763         // update the viewangles
6764         UpdateViewAngles();
6765 }
6766
6767 /*
6768 ==============
6769 idPlayer::AdjustSpeed
6770 ==============
6771 */
6772 void idPlayer::AdjustSpeed( void ) {
6773         float speed;
6774         float rate;
6775
6776         if ( spectating ) {
6777                 speed = pm_spectatespeed.GetFloat();
6778                 bobFrac = 0.0f;
6779         } else if ( noclip ) {
6780                 speed = pm_noclipspeed.GetFloat();
6781                 bobFrac = 0.0f;
6782         } else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && ( usercmd.upmove >= 0 ) ) {
6783                 if ( !gameLocal.isMultiplayer && !physicsObj.IsCrouching() && !PowerUpActive( ADRENALINE ) ) {
6784                         stamina -= MS2SEC( gameLocal.msec );
6785                 }
6786                 if ( stamina < 0 ) {
6787                         stamina = 0;
6788                 }
6789                 if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
6790                         bobFrac = 1.0f;
6791                 } else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
6792                         bobFrac = 0.0f;
6793                 } else {
6794                         bobFrac = stamina / pm_staminathreshold.GetFloat();
6795                 }
6796                 speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
6797         } else {
6798                 rate = pm_staminarate.GetFloat();
6799                 
6800                 // increase 25% faster when not moving
6801                 if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( usercmd.upmove == 0 ) ) ) {
6802                          rate *= 1.25f;
6803                 }
6804
6805                 stamina += rate * MS2SEC( gameLocal.msec );
6806                 if ( stamina > pm_stamina.GetFloat() ) {
6807                         stamina = pm_stamina.GetFloat();
6808                 }
6809                 speed = pm_walkspeed.GetFloat();
6810                 bobFrac = 0.0f;
6811         }
6812
6813         speed *= PowerUpModifier(SPEED);
6814
6815         if ( influenceActive == INFLUENCE_LEVEL3 ) {
6816                 speed *= 0.33f;
6817         }
6818
6819         physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
6820 }
6821
6822 /*
6823 ==============
6824 idPlayer::AdjustBodyAngles
6825 ==============
6826 */
6827 void idPlayer::AdjustBodyAngles( void ) {
6828         idMat3  lookAxis;
6829         idMat3  legsAxis;
6830         bool    blend;
6831         float   diff;
6832         float   frac;
6833         float   upBlend;
6834         float   forwardBlend;
6835         float   downBlend;
6836
6837         if ( health < 0 ) {
6838                 return;
6839         }
6840
6841         blend = true;
6842
6843         if ( !physicsObj.HasGroundContacts() ) {
6844                 idealLegsYaw = 0.0f;
6845                 legsForward = true;
6846         } else if ( usercmd.forwardmove < 0 ) {
6847                 idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
6848                 legsForward = false;
6849         } else if ( usercmd.forwardmove > 0 ) {
6850                 idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
6851                 legsForward = true;
6852         } else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
6853                 if ( !legsForward ) {
6854                         idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
6855                 } else {
6856                         idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
6857                 }
6858         } else if ( usercmd.rightmove != 0 ) {
6859                 idealLegsYaw = 0.0f;
6860                 legsForward = true;
6861         } else {
6862                 legsForward = true;
6863                 diff = idMath::Fabs( idealLegsYaw - legsYaw );
6864                 idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
6865                 if ( diff < 0.1f ) {
6866                         legsYaw = idealLegsYaw;
6867                         blend = false;
6868                 }
6869         }
6870
6871         if ( !physicsObj.IsCrouching() ) {
6872                 legsForward = true;
6873         }
6874
6875         oldViewYaw = viewAngles.yaw;
6876
6877         AI_TURN_LEFT = false;
6878         AI_TURN_RIGHT = false;
6879         if ( idealLegsYaw < -45.0f ) {
6880                 idealLegsYaw = 0;
6881                 AI_TURN_RIGHT = true;
6882                 blend = true;
6883         } else if ( idealLegsYaw > 45.0f ) {
6884                 idealLegsYaw = 0;
6885                 AI_TURN_LEFT = true;
6886                 blend = true;
6887         }
6888
6889         if ( blend ) {
6890                 legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
6891         }
6892         legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
6893         animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
6894
6895         // calculate the blending between down, straight, and up
6896         frac = viewAngles.pitch / 90.0f;
6897         if ( frac > 0.0f ) {
6898                 downBlend               = frac;
6899                 forwardBlend    = 1.0f - frac;
6900                 upBlend                 = 0.0f;
6901         } else {
6902                 downBlend               = 0.0f;
6903                 forwardBlend    = 1.0f + frac;
6904                 upBlend                 = -frac;
6905         }
6906
6907     animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
6908         animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
6909         animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
6910
6911         animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
6912         animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
6913         animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
6914 }
6915
6916 /*
6917 ==============
6918 idPlayer::InitAASLocation
6919 ==============
6920 */
6921 void idPlayer::InitAASLocation( void ) {
6922         int             i;
6923         int             num;
6924         idVec3  size;
6925         idBounds bounds;
6926         idAAS   *aas;
6927         idVec3  origin;
6928
6929         GetFloorPos( 64.0f, origin );
6930
6931         num = gameLocal.NumAAS();
6932         aasLocation.SetGranularity( 1 );
6933         aasLocation.SetNum( num );      
6934         for( i = 0; i < aasLocation.Num(); i++ ) {
6935                 aasLocation[ i ].areaNum = 0;
6936                 aasLocation[ i ].pos = origin;
6937                 aas = gameLocal.GetAAS( i );
6938                 if ( aas && aas->GetSettings() ) {
6939                         size = aas->GetSettings()->boundingBoxes[0][1];
6940                         bounds[0] = -size;
6941                         size.z = 32.0f;
6942                         bounds[1] = size;
6943
6944                         aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
6945                 }
6946         }
6947 }
6948
6949 /*
6950 ==============
6951 idPlayer::SetAASLocation
6952 ==============
6953 */
6954 void idPlayer::SetAASLocation( void ) {
6955         int             i;
6956         int             areaNum;
6957         idVec3  size;
6958         idBounds bounds;
6959         idAAS   *aas;
6960         idVec3  origin;
6961
6962         if ( !GetFloorPos( 64.0f, origin ) ) {
6963                 return;
6964         }
6965         
6966         for( i = 0; i < aasLocation.Num(); i++ ) {
6967                 aas = gameLocal.GetAAS( i );
6968                 if ( !aas ) {
6969                         continue;
6970                 }
6971
6972                 size = aas->GetSettings()->boundingBoxes[0][1];
6973                 bounds[0] = -size;
6974                 size.z = 32.0f;
6975                 bounds[1] = size;
6976
6977                 areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
6978                 if ( areaNum ) {
6979                         aasLocation[ i ].pos = origin;
6980                         aasLocation[ i ].areaNum = areaNum;
6981                 }
6982         }
6983 }
6984
6985 /*
6986 ==============
6987 idPlayer::GetAASLocation
6988 ==============
6989 */
6990 void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
6991         int i;
6992
6993         if ( aas != NULL ) {
6994                 for( i = 0; i < aasLocation.Num(); i++ ) {
6995                         if ( aas == gameLocal.GetAAS( i ) ) {
6996                                 areaNum = aasLocation[ i ].areaNum;
6997                                 pos = aasLocation[ i ].pos;
6998                                 return;
6999                         }
7000                 }
7001         }
7002
7003         areaNum = 0;
7004         pos = physicsObj.GetOrigin();
7005 }
7006
7007 /*
7008 ==============
7009 idPlayer::Move
7010 ==============
7011 */
7012 void idPlayer::Move( void ) {
7013         float newEyeOffset;
7014         idVec3 oldOrigin;
7015         idVec3 oldVelocity;
7016         idVec3 pushVelocity;
7017
7018         // save old origin and velocity for crashlanding
7019         oldOrigin = physicsObj.GetOrigin();
7020         oldVelocity = physicsObj.GetLinearVelocity();
7021         pushVelocity = physicsObj.GetPushedLinearVelocity();
7022
7023         // set physics variables
7024         physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
7025         physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
7026
7027         if ( noclip ) {
7028                 physicsObj.SetContents( 0 );
7029                 physicsObj.SetMovementType( PM_NOCLIP );
7030         } else if ( spectating ) {
7031                 physicsObj.SetContents( 0 );
7032                 physicsObj.SetMovementType( PM_SPECTATOR );
7033         } else if ( health <= 0 ) {
7034                 physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
7035                 physicsObj.SetMovementType( PM_DEAD );
7036         } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
7037                 physicsObj.SetContents( CONTENTS_BODY );
7038                 physicsObj.SetMovementType( PM_FREEZE );
7039 #ifdef _D3XP
7040         } else if ( mountedObject ) {
7041                 physicsObj.SetContents( 0 );
7042                 physicsObj.SetMovementType( PM_FREEZE );
7043 #endif
7044         } else {
7045                 physicsObj.SetContents( CONTENTS_BODY );
7046                 physicsObj.SetMovementType( PM_NORMAL );
7047         }
7048
7049         if ( spectating ) {
7050                 physicsObj.SetClipMask( MASK_DEADSOLID );
7051         } else if ( health <= 0 ) {
7052                 physicsObj.SetClipMask( MASK_DEADSOLID );
7053         } else {
7054                 physicsObj.SetClipMask( MASK_PLAYERSOLID );
7055         }
7056
7057         physicsObj.SetDebugLevel( g_debugMove.GetBool() );
7058         physicsObj.SetPlayerInput( usercmd, viewAngles );
7059
7060         // FIXME: physics gets disabled somehow
7061         BecomeActive( TH_PHYSICS );
7062         RunPhysics();
7063
7064         // update our last valid AAS location for the AI
7065         SetAASLocation();
7066
7067         if ( spectating ) {
7068                 newEyeOffset = 0.0f;
7069         } else if ( health <= 0 ) {
7070                 newEyeOffset = pm_deadviewheight.GetFloat();
7071         } else if ( physicsObj.IsCrouching() ) {
7072                 newEyeOffset = pm_crouchviewheight.GetFloat();
7073         } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
7074                 newEyeOffset = 0.0f;
7075         } else {
7076                 newEyeOffset = pm_normalviewheight.GetFloat();
7077         }
7078
7079         if ( EyeHeight() != newEyeOffset ) {
7080                 if ( spectating ) {
7081                         SetEyeHeight( newEyeOffset );
7082                 } else {
7083                         // smooth out duck height changes
7084                         SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
7085                 }
7086         }
7087
7088         if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
7089                 AI_CROUCH       = false;
7090                 AI_ONGROUND     = ( influenceActive == INFLUENCE_LEVEL2 );
7091                 AI_ONLADDER     = false;
7092                 AI_JUMP         = false;
7093         } else {
7094                 AI_CROUCH       = physicsObj.IsCrouching();
7095                 AI_ONGROUND     = physicsObj.HasGroundContacts();
7096                 AI_ONLADDER     = physicsObj.OnLadder();
7097                 AI_JUMP         = physicsObj.HasJumped();
7098
7099                 // check if we're standing on top of a monster and give a push if we are
7100                 idEntity *groundEnt = physicsObj.GetGroundEntity();
7101                 if ( groundEnt && groundEnt->IsType( idAI::Type ) ) {
7102                         idVec3 vel = physicsObj.GetLinearVelocity();
7103                         if ( vel.ToVec2().LengthSqr() < 0.1f ) {
7104                                 vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
7105                                 vel.ToVec2().NormalizeFast();
7106                                 vel.ToVec2() *= pm_walkspeed.GetFloat();
7107                         } else {
7108                                 // give em a push in the direction they're going
7109                                 vel *= 1.1f;
7110                         }
7111                         physicsObj.SetLinearVelocity( vel );
7112                 }
7113         }
7114
7115         if ( AI_JUMP ) {
7116                 // bounce the view weapon
7117                 loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7118                 currentLoggedAccel++;
7119                 acc->time = gameLocal.time;
7120                 acc->dir[2] = 200;
7121                 acc->dir[0] = acc->dir[1] = 0;
7122         }
7123
7124         if ( AI_ONLADDER ) {
7125                 int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
7126                 int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
7127
7128                 if ( old_rung != new_rung ) {
7129                         StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
7130                 }
7131         }
7132
7133         BobCycle( pushVelocity );
7134         CrashLand( oldOrigin, oldVelocity );
7135 }
7136
7137 /*
7138 ==============
7139 idPlayer::UpdateHud
7140 ==============
7141 */
7142 void idPlayer::UpdateHud( void ) {
7143         idPlayer *aimed;
7144
7145         if ( !hud ) {
7146                 return;
7147         }
7148
7149         if ( entityNumber != gameLocal.localClientNum ) {
7150                 return;
7151         }
7152
7153         int c = inventory.pickupItemNames.Num();
7154         if ( c > 0 ) {
7155                 if ( gameLocal.time > inventory.nextItemPickup ) {
7156                         if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
7157                                 inventory.nextItemNum = 1;
7158                         }
7159                         int i;
7160
7161 #ifdef _D3XP
7162                         int count = 5;
7163                         if(gameLocal.isMultiplayer) {
7164                                 count = 3;
7165                         }
7166 #endif
7167                         for ( i = 0; i < count, i < c; i++ ) { //_D3XP
7168                                 hud->SetStateString( va( "itemtext%i", inventory.nextItemNum ), inventory.pickupItemNames[0].name );
7169                                 hud->SetStateString( va( "itemicon%i", inventory.nextItemNum ), inventory.pickupItemNames[0].icon );
7170                                 hud->HandleNamedEvent( va( "itemPickup%i", inventory.nextItemNum++ ) );
7171                                 inventory.pickupItemNames.RemoveIndex( 0 );
7172                                 if (inventory.nextItemNum == 1 ) {
7173                                         inventory.onePickupTime = gameLocal.time;
7174                                 } else  if ( inventory.nextItemNum > count ) { //_D3XP
7175                                         inventory.nextItemNum = 1;
7176                                         inventory.nextItemPickup = inventory.onePickupTime + 2000;
7177                                 } else {
7178                                         inventory.nextItemPickup = gameLocal.time + 400;
7179                                 }
7180                         }
7181                 }
7182         }
7183
7184         if ( gameLocal.realClientTime == lastMPAimTime ) {
7185                 if ( MPAim != -1 && gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
7186                         && gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
7187                         && static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
7188                                 aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
7189                                 hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
7190                                 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
7191                                 hud->HandleNamedEvent( "aim_flash" );
7192                                 MPAimHighlight = true;
7193                                 MPAimFadeTime = 0;      // no fade till loosing focus
7194                 } else if ( MPAimHighlight ) {
7195                         hud->HandleNamedEvent( "aim_fade" );
7196                         MPAimFadeTime = gameLocal.realClientTime;
7197                         MPAimHighlight = false;
7198                 }
7199         }
7200         if ( MPAimFadeTime ) {
7201                 assert( !MPAimHighlight );
7202                 if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
7203                         MPAimFadeTime = 0;
7204                 }
7205         }
7206
7207         hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
7208         if ( numProjectilesFired ) {
7209                 hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
7210         } else {
7211                 hud->SetStateString( "projectilepct", "Hit % 0.0" );
7212         }
7213
7214         if ( isLagged && gameLocal.isMultiplayer && gameLocal.localClientNum == entityNumber ) {
7215                 hud->SetStateString( "hudLag", "1" );
7216         } else {
7217                 hud->SetStateString( "hudLag", "0" );
7218         }
7219 }
7220
7221 /*
7222 ==============
7223 idPlayer::UpdateDeathSkin
7224 ==============
7225 */
7226 void idPlayer::UpdateDeathSkin( bool state_hitch ) {
7227         if ( !( gameLocal.isMultiplayer || g_testDeath.GetBool() ) ) {
7228                 return;
7229         }
7230         if ( health <= 0 ) {
7231                 if ( !doingDeathSkin ) {
7232                         deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
7233                         doingDeathSkin = true;
7234                         renderEntity.noShadow = true;
7235                         if ( state_hitch ) {
7236                                 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
7237                         } else {
7238                                 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
7239                         }
7240                         UpdateVisuals();
7241                 }
7242
7243                 // wait a bit before switching off the content
7244                 if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
7245                         SetCombatContents( false );
7246                         deathClearContentsTime = 0;
7247                 }
7248         } else {
7249                 renderEntity.noShadow = false;
7250                 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
7251                 UpdateVisuals();
7252                 doingDeathSkin = false;
7253         }
7254 }
7255
7256 /*
7257 ==============
7258 idPlayer::StartFxOnBone
7259 ==============
7260 */
7261 void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
7262         idVec3 offset;
7263         idMat3 axis;
7264         jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
7265
7266         if ( jointHandle == INVALID_JOINT ) {
7267                 gameLocal.Printf( "Cannot find bone %s\n", bone );
7268                 return;
7269         }
7270
7271         if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
7272                 offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
7273                 axis = axis * GetPhysics()->GetAxis();
7274         }
7275
7276         idEntityFx::StartFx( fx, &offset, &axis, this, true );
7277 }
7278
7279 /*
7280 ==============
7281 idPlayer::Think
7282
7283 Called every tic for each player
7284 ==============
7285 */
7286 void idPlayer::Think( void ) {
7287         renderEntity_t *headRenderEnt;
7288
7289         UpdatePlayerIcons();
7290
7291         // latch button actions
7292         oldButtons = usercmd.buttons;
7293
7294         // grab out usercmd
7295         usercmd_t oldCmd = usercmd;
7296         usercmd = gameLocal.usercmds[ entityNumber ];
7297         buttonMask &= usercmd.buttons;
7298         usercmd.buttons &= ~buttonMask;
7299
7300         if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
7301                 return;
7302         }
7303
7304         // clear the ik before we do anything else so the skeleton doesn't get updated twice
7305         walkIK.ClearJointMods();
7306
7307         // if this is the very first frame of the map, set the delta view angles
7308         // based on the usercmd angles
7309         if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
7310                 spawnAnglesSet = true;
7311                 SetViewAngles( spawnAngles );
7312                 oldFlags = usercmd.flags;
7313         }
7314
7315 #ifdef _D3XP
7316         if ( mountedObject ) {
7317                 usercmd.forwardmove = 0;
7318                 usercmd.rightmove = 0;
7319                 usercmd.upmove = 0;
7320         }
7321 #endif
7322
7323         if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
7324                 if ( objectiveSystemOpen && AI_PAIN ) {
7325                         TogglePDA();
7326                 }
7327                 usercmd.forwardmove = 0;
7328                 usercmd.rightmove = 0;
7329                 usercmd.upmove = 0;
7330         }
7331
7332         // log movement changes for weapon bobbing effects
7333         if ( usercmd.forwardmove != oldCmd.forwardmove ) {
7334                 loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7335                 currentLoggedAccel++;
7336                 acc->time = gameLocal.time;
7337                 acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
7338                 acc->dir[1] = acc->dir[2] = 0;
7339         }
7340
7341         if ( usercmd.rightmove != oldCmd.rightmove ) {
7342                 loggedAccel_t   *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7343                 currentLoggedAccel++;
7344                 acc->time = gameLocal.time;
7345                 acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
7346                 acc->dir[0] = acc->dir[2] = 0;
7347         }
7348
7349         // freelook centering
7350         if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_MLOOK ) {
7351                 centerView.Init( gameLocal.time, 200, viewAngles.pitch, 0 );
7352         }
7353
7354         // zooming
7355         if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
7356                 if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
7357                         zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
7358                 } else {
7359                         zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
7360                 }
7361         }
7362
7363         // if we have an active gui, we will unrotate the view angles as
7364         // we turn the mouse movements into gui events
7365         idUserInterface *gui = ActiveGui();
7366         if ( gui && gui != focusUI ) {
7367                 RouteGuiMouse( gui );
7368         }
7369
7370         // set the push velocity on the weapon before running the physics
7371         if ( weapon.GetEntity() ) {
7372                 weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
7373         }
7374
7375         EvaluateControls();
7376
7377         if ( !af.IsActive() ) {
7378                 AdjustBodyAngles();
7379                 CopyJointsFromBodyToHead();
7380         }
7381
7382         Move();
7383
7384         if ( !g_stopTime.GetBool() ) {
7385
7386                 if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
7387                         TouchTriggers();
7388                 }
7389
7390                 // not done on clients for various reasons. don't do it on server and save the sound channel for other things
7391                 if ( !gameLocal.isMultiplayer ) {
7392                         SetCurrentHeartRate();
7393 #ifdef _D3XP
7394                         float scale = new_g_damageScale;
7395 #else
7396                         float scale = g_damageScale.GetFloat();
7397 #endif
7398                         if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
7399                                 if ( scale < 1.0f ) {
7400                                         scale += 0.05f;
7401                                 }
7402                                 if ( scale > 1.0f ) {
7403                                         scale = 1.0f;
7404                                 }
7405 #ifdef _D3XP
7406                                 new_g_damageScale = scale;
7407 #else
7408                                 g_damageScale.SetFloat( scale );
7409 #endif
7410                         }
7411                 }
7412
7413                 // update GUIs, Items, and character interactions
7414                 UpdateFocus();
7415                 
7416                 UpdateLocation();
7417
7418                 // update player script
7419                 UpdateScript();
7420
7421                 // service animations
7422                 if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
7423                 UpdateConditions();
7424                         UpdateAnimState();
7425                         CheckBlink();
7426                 }
7427
7428                 // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
7429                 AI_PAIN = false;
7430         }
7431
7432         // calculate the exact bobbed view position, which is used to
7433         // position the view weapon, among other things
7434         CalculateFirstPersonView();
7435
7436         // this may use firstPersonView, or a thirdPeroson / camera view
7437         CalculateRenderView();
7438
7439         inventory.UpdateArmor();
7440
7441         if ( spectating ) {
7442                 UpdateSpectating();
7443         } else if ( health > 0 ) {
7444                 UpdateWeapon();
7445         }
7446
7447         UpdateAir();
7448
7449 #ifdef _D3XP
7450         UpdatePowerupHud();
7451 #endif
7452         
7453         UpdateHud();
7454
7455         UpdatePowerUps();
7456
7457         UpdateDeathSkin( false );
7458
7459         if ( gameLocal.isMultiplayer ) {
7460                 DrawPlayerIcons();
7461
7462 #ifdef _D3XP
7463                 if ( enviroSuitLight.IsValid() ) {
7464                         idAngles lightAng = firstPersonViewAxis.ToAngles();
7465                         idVec3 lightOrg = firstPersonViewOrigin;
7466                         const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
7467
7468                         idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
7469                         idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
7470
7471                         lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
7472                         lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
7473                         lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
7474                         lightAng.pitch += enviroAngleOffset.x;
7475                         lightAng.yaw += enviroAngleOffset.y;
7476                         lightAng.roll += enviroAngleOffset.z;
7477
7478                         enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
7479                         enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
7480                         enviroSuitLight.GetEntity()->UpdateVisuals();
7481                         enviroSuitLight.GetEntity()->Present();
7482                 }
7483 #endif
7484         }
7485
7486         if ( head.GetEntity() ) {
7487                 headRenderEnt = head.GetEntity()->GetRenderEntity();
7488         } else {
7489                 headRenderEnt = NULL;
7490         }
7491
7492         if ( headRenderEnt ) {
7493                 if ( influenceSkin ) {
7494                         headRenderEnt->customSkin = influenceSkin;
7495                 } else {
7496                         headRenderEnt->customSkin = NULL;
7497                 }
7498         }
7499
7500         if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
7501                 renderEntity.suppressShadowInViewID     = 0;
7502                 if ( headRenderEnt ) {
7503                         headRenderEnt->suppressShadowInViewID = 0;
7504                 }
7505         } else {
7506                 renderEntity.suppressShadowInViewID     = entityNumber+1;
7507                 if ( headRenderEnt ) {
7508                         headRenderEnt->suppressShadowInViewID = entityNumber+1;
7509                 }
7510         }
7511         // never cast shadows from our first-person muzzle flashes
7512         renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
7513         if ( headRenderEnt ) {
7514                 headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
7515         }
7516
7517         if ( !g_stopTime.GetBool() ) {
7518                 UpdateAnimation();
7519
7520         Present();
7521
7522                 UpdateDamageEffects();
7523
7524                 LinkCombat();
7525
7526                 playerView.CalculateShake();
7527         }
7528
7529         if ( !( thinkFlags & TH_THINK ) ) {
7530                 gameLocal.Printf( "player %d not thinking?\n", entityNumber );
7531         }
7532
7533         if ( g_showEnemies.GetBool() ) {
7534                 idActor *ent;
7535                 int num = 0;
7536                 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
7537                         gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
7538                         gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
7539                         num++;
7540                 }
7541                 gameLocal.Printf( "%d: enemies\n", num );
7542         }
7543
7544 #ifdef _D3XP
7545         inventory.RechargeAmmo(this);
7546
7547         if(healthRecharge) {
7548                 int elapsed = gameLocal.time - lastHealthRechargeTime;
7549                 if(elapsed >= rechargeSpeed) {
7550                         int intervals = (gameLocal.time - lastHealthRechargeTime)/rechargeSpeed;
7551                         Give("health", va("%d", intervals));
7552                         lastHealthRechargeTime += intervals*rechargeSpeed;
7553                 }
7554         }
7555
7556         // determine if portal sky is in pvs
7557         gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( gameLocal.GetPlayerPVS(), GetPhysics()->GetOrigin() );
7558 #endif
7559 }
7560
7561 #ifdef _D3XP
7562 /*
7563 =================
7564 idPlayer::StartHealthRecharge
7565 =================
7566 */
7567 void idPlayer::StartHealthRecharge(int speed) {
7568         lastHealthRechargeTime = gameLocal.time;
7569         healthRecharge = true;
7570         rechargeSpeed = speed;
7571 }
7572
7573 /*
7574 =================
7575 idPlayer::StopHealthRecharge
7576 =================
7577 */
7578 void idPlayer::StopHealthRecharge() {
7579         healthRecharge = false;
7580 }
7581
7582 /*
7583 =================
7584 idPlayer::GetCurrentWeapon
7585 =================
7586 */
7587 idStr idPlayer::GetCurrentWeapon() {
7588         const char *weapon;
7589
7590         if ( currentWeapon >= 0 ) {
7591                 weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
7592                 return weapon;
7593         } else {
7594                 return "";
7595         }
7596 }
7597
7598 /*
7599 =================
7600 idPlayer::CanGive
7601 =================
7602 */
7603 bool idPlayer::CanGive( const char *statname, const char *value ) {
7604         if ( AI_DEAD ) {
7605                 return false;
7606         }
7607
7608         if ( !idStr::Icmp( statname, "health" ) ) {
7609                 if ( health >= inventory.maxHealth ) {
7610                         return false;
7611                 }
7612                 return true;
7613         } else if ( !idStr::Icmp( statname, "stamina" ) ) {
7614                 if ( stamina >= 100 ) {
7615                         return false;
7616                 }
7617                 return true;
7618
7619         } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
7620                 return true;
7621
7622         } else if ( !idStr::Icmp( statname, "air" ) ) {
7623                 if ( airTics >= pm_airTics.GetInteger() ) {
7624                         return false;
7625                 }
7626                 return true;
7627         } else {
7628                 return inventory.CanGive( this, spawnArgs, statname, value, &idealWeapon );
7629         }
7630
7631         return false;
7632 }
7633
7634 /*
7635 =================
7636 idPlayer::StopHelltime
7637
7638 provides a quick non-ramping way of stopping helltime
7639 =================
7640 */
7641 void idPlayer::StopHelltime( bool quick ) {
7642         if ( !PowerUpActive( HELLTIME ) ) {
7643                 return;
7644         }
7645
7646         // take away the powerups
7647         if ( PowerUpActive( INVULNERABILITY ) ) {
7648                 ClearPowerup( INVULNERABILITY );
7649         }
7650
7651         if ( PowerUpActive( BERSERK ) ) {
7652                 ClearPowerup( BERSERK );
7653         }
7654
7655         if ( PowerUpActive( HELLTIME ) ) {
7656                 ClearPowerup( HELLTIME );
7657         }
7658
7659         // stop the looping sound
7660         StopSound( SND_CHANNEL_DEMONIC, false );
7661
7662         // reset the game vars
7663         if ( quick ) {
7664                 gameLocal.QuickSlowmoReset();
7665         }
7666 }
7667
7668 /*
7669 =================
7670 idPlayer::Event_ToggleBloom
7671 =================
7672 */
7673 void idPlayer::Event_ToggleBloom( int on ) {
7674         if ( on ) {
7675                 bloomEnabled = true;
7676         }
7677         else {
7678                 bloomEnabled = false;
7679         }
7680 }
7681
7682 /*
7683 =================
7684 idPlayer::Event_SetBloomParms
7685 =================
7686 */
7687 void idPlayer::Event_SetBloomParms( float speed, float intensity ) {
7688         bloomSpeed = speed;
7689         bloomIntensity = intensity;
7690 }
7691
7692 /*
7693 =================
7694 idPlayer::PlayHelltimeStopSound
7695 =================
7696 */
7697 void idPlayer::PlayHelltimeStopSound() {
7698         const char* sound;
7699
7700         if ( spawnArgs.GetString( "snd_helltime_stop", "", &sound ) ) {
7701                 PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
7702         }
7703 }
7704 #endif
7705
7706 /*
7707 =================
7708 idPlayer::RouteGuiMouse
7709 =================
7710 */
7711 void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
7712         sysEvent_t ev;
7713         const char *command;
7714
7715         if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
7716                 ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
7717                 command = gui->HandleEvent( &ev, gameLocal.time );
7718                 oldMouseX = usercmd.mx;
7719                 oldMouseY = usercmd.my;
7720         }
7721 }
7722
7723 /*
7724 ==================
7725 idPlayer::LookAtKiller
7726 ==================
7727 */
7728 void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
7729         idVec3 dir;
7730         
7731         if ( attacker && attacker != this ) {
7732                 dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
7733         } else if ( inflictor && inflictor != this ) {
7734                 dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
7735         } else {
7736                 dir = viewAxis[ 0 ];
7737         }
7738
7739         idAngles ang( 0, dir.ToYaw(), 0 );
7740         SetViewAngles( ang );
7741 }
7742
7743 /*
7744 ==============
7745 idPlayer::Kill
7746 ==============
7747 */
7748 void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
7749         if ( spectating ) {
7750                 SpectateFreeFly( false );
7751         } else if ( health > 0 ) {
7752                 godmode = false;
7753                 if ( nodamage ) {
7754                         ServerSpectate( true );
7755                         forceRespawn = true;
7756                 } else {
7757                         Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
7758                         if ( delayRespawn ) {
7759                                 forceRespawn = false;
7760                                 int delay = spawnArgs.GetFloat( "respawn_delay" );
7761                                 minRespawnTime = gameLocal.time + SEC2MS( delay );
7762                                 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7763                         }
7764                 }
7765         }
7766 }
7767
7768 /*
7769 ==================
7770 idPlayer::Killed
7771 ==================
7772 */
7773 void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
7774         float delay;
7775
7776         assert( !gameLocal.isClient );
7777
7778         // stop taking knockback once dead
7779         fl.noknockback = true;
7780         if ( health < -999 ) {
7781                 health = -999;
7782         }
7783
7784         if ( AI_DEAD ) {
7785                 AI_PAIN = true;
7786                 return;
7787         }
7788
7789         heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
7790         AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
7791
7792         if ( !g_testDeath.GetBool() ) {
7793                 playerView.Fade( colorBlack, 12000 );
7794         }
7795
7796         AI_DEAD = true;
7797         SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
7798         SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
7799         SetWaitState( "" );
7800
7801         animator.ClearAllJoints();
7802
7803         if ( StartRagdoll() ) {
7804                 pm_modelView.SetInteger( 0 );
7805                 minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
7806                 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7807         } else {
7808                 // don't allow respawn until the death anim is done
7809                 // g_forcerespawn may force spawning at some later time
7810                 delay = spawnArgs.GetFloat( "respawn_delay" );
7811                 minRespawnTime = gameLocal.time + SEC2MS( delay );
7812                 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7813         }
7814
7815         physicsObj.SetMovementType( PM_DEAD );
7816         StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
7817         StopSound( SND_CHANNEL_BODY2, false );
7818
7819         fl.takedamage = true;           // can still be gibbed
7820
7821         // get rid of weapon
7822         weapon.GetEntity()->OwnerDied();
7823
7824         // drop the weapon as an item
7825         DropWeapon( true );
7826
7827 #ifdef CTF
7828         // drop the flag if player was carrying it
7829         if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() && 
7830                  carryingFlag )
7831         {
7832                 DropFlag();
7833         }
7834 #endif
7835
7836         if ( !g_testDeath.GetBool() ) {
7837                 LookAtKiller( inflictor, attacker );
7838         }
7839
7840         if ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) {
7841                 idPlayer *killer = NULL;
7842                 // no gibbing in MP. Event_Gib will early out in MP
7843                 if ( attacker->IsType( idPlayer::Type ) ) {
7844                         killer = static_cast<idPlayer*>(attacker);
7845                         if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
7846                                 gibDeath = true;
7847                                 gibsDir = dir;
7848                                 gibsLaunched = false;
7849                         }
7850                 }
7851                 gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
7852         } else {
7853                 physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
7854         }
7855
7856         ClearPowerUps();
7857
7858         UpdateVisuals();
7859
7860         isChatting = false;
7861 }
7862
7863 /*
7864 =====================
7865 idPlayer::GetAIAimTargets
7866
7867 Returns positions for the AI to aim at.
7868 =====================
7869 */
7870 void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
7871         idVec3 offset;
7872         idMat3 axis;
7873         idVec3 origin;
7874         
7875         origin = lastSightPos - physicsObj.GetOrigin();
7876
7877         GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
7878         headPos = offset + origin;
7879
7880         GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
7881         chestPos = offset + origin;
7882 }
7883
7884 /*
7885 ================
7886 idPlayer::DamageFeedback
7887
7888 callback function for when another entity received damage from this entity.  damage can be adjusted and returned to the caller.
7889 ================
7890 */
7891 void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
7892         assert( !gameLocal.isClient );
7893         damage *= PowerUpModifier( BERSERK );
7894         if ( damage && ( victim != this ) && ( victim->IsType( idActor::Type ) || victim->IsType( idDamagable::Type ) ) ) {
7895
7896         idPlayer *victimPlayer = NULL;
7897         
7898         /* No damage feedback sound for hitting friendlies in CTF */
7899                 if ( victim->IsType( idPlayer::Type ) ) {
7900             victimPlayer = static_cast<idPlayer*>(victim);
7901                 }
7902
7903         if ( gameLocal.mpGame.IsGametypeFlagBased() && victimPlayer && this->team == victimPlayer->team ) {
7904                         /* Do nothing ... */ 
7905                 }
7906                 else {
7907                 SetLastHitTime( gameLocal.time );
7908                 }
7909         }
7910 }
7911
7912 /*
7913 =================
7914 idPlayer::CalcDamagePoints
7915
7916 Calculates how many health and armor points will be inflicted, but
7917 doesn't actually do anything with them.  This is used to tell when an attack
7918 would have killed the player, possibly allowing a "saving throw"
7919 =================
7920 */
7921 void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
7922                                                            const float damageScale, const int location, int *health, int *armor ) {
7923         int             damage;
7924         int             armorSave;
7925
7926         damageDef->GetInt( "damage", "20", damage );
7927         damage = GetDamageForLocation( damage, location );
7928
7929         idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
7930         if ( !gameLocal.isMultiplayer ) {
7931                 if ( inflictor != gameLocal.world ) {
7932                         switch ( g_skill.GetInteger() ) {
7933                                 case 0: 
7934                                         damage *= 0.80f;
7935                                         if ( damage < 1 ) {
7936                                                 damage = 1;
7937                                         }
7938                                         break;
7939                                 case 2:
7940                                         damage *= 1.70f;
7941                                         break;
7942                                 case 3:
7943                                         damage *= 3.5f;
7944                                         break;
7945                                 default:
7946                                         break;
7947                         }
7948                 }
7949         }
7950
7951         damage *= damageScale;
7952
7953         // always give half damage if hurting self
7954         if ( attacker == this ) {
7955                 if ( gameLocal.isMultiplayer ) {
7956                         // only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
7957                         damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
7958                 } else {
7959                         damage *= damageDef->GetFloat( "selfDamageScale", "1" );
7960                 }
7961         }
7962
7963         // check for completely getting out of the damage
7964         if ( !damageDef->GetBool( "noGod" ) ) {
7965                 // check for godmode
7966                 if ( godmode ) {
7967                         damage = 0;
7968                 }
7969 #ifdef _D3XP
7970                 //Invulnerability is just like god mode
7971                 if( PowerUpActive( INVULNERABILITY ) ) {
7972                         damage = 0;
7973                 }
7974 #endif
7975         }
7976
7977         // inform the attacker that they hit someone
7978         attacker->DamageFeedback( this, inflictor, damage );
7979
7980         // save some from armor
7981         if ( !damageDef->GetBool( "noArmor" ) ) {
7982                 float armor_protection;
7983
7984                 armor_protection = ( gameLocal.isMultiplayer ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
7985
7986                 armorSave = ceil( damage * armor_protection );
7987                 if ( armorSave >= inventory.armor ) {
7988                         armorSave = inventory.armor;
7989                 }
7990
7991                 if ( !damage ) {
7992                         armorSave = 0;
7993                 } else if ( armorSave >= damage ) {
7994                         armorSave = damage - 1;
7995                         damage = 1;
7996                 } else {
7997                         damage -= armorSave;
7998                 }
7999         } else {
8000                 armorSave = 0;
8001         }
8002
8003         // check for team damage
8004         if ( gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
8005                 && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
8006                 && !damageDef->GetBool( "noTeam" )
8007                 && player
8008                 && player != this               // you get self damage no matter what
8009                 && player->team == team ) {
8010                         damage = 0;
8011         }
8012
8013         *health = damage;
8014         *armor = armorSave;
8015 }
8016
8017 /*
8018 ============
8019 Damage
8020
8021 this            entity that is being damaged
8022 inflictor       entity that is causing the damage
8023 attacker        entity that caused the inflictor to damage targ
8024         example: this=monster, inflictor=rocket, attacker=player
8025
8026 dir                     direction of the attack for knockback in global space
8027
8028 damageDef       an idDict with all the options for damage effects
8029
8030 inflictor, attacker, dir, and point can be NULL for environmental effects
8031 ============
8032 */
8033 void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
8034                                            const char *damageDefName, const float damageScale, const int location ) {
8035         idVec3          kick;
8036         int                     damage;
8037         int                     armorSave;
8038         int                     knockback;
8039         idVec3          damage_from;
8040         idVec3          localDamageVector;      
8041         float           attackerPushScale;
8042 #ifdef _D3XP
8043         SetTimeState ts( timeGroup );
8044 #endif
8045
8046         // damage is only processed on server
8047         if ( gameLocal.isClient ) {
8048                 return;
8049         }
8050         
8051         if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
8052                 return;
8053         }
8054
8055         if ( !inflictor ) {
8056                 inflictor = gameLocal.world;
8057         }
8058         if ( !attacker ) {
8059                 attacker = gameLocal.world;
8060         }
8061
8062         if ( attacker->IsType( idAI::Type ) ) {
8063 #ifndef _D3XP
8064                 if ( PowerUpActive( BERSERK ) ) {
8065                         return;
8066                 }
8067 #endif
8068                 // don't take damage from monsters during influences
8069                 if ( influenceActive != 0 ) {
8070                         return;
8071                 }
8072         }
8073
8074         const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
8075         if ( !damageDef ) {
8076                 gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
8077                 return;
8078         }
8079
8080         if ( damageDef->dict.GetBool( "ignore_player" ) ) {
8081                 return;
8082         }
8083
8084         CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
8085         
8086         // determine knockback
8087         damageDef->dict.GetInt( "knockback", "20", knockback );
8088
8089 /*#ifdef _D3XP
8090         idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
8091
8092         if ( gameLocal.mpGame.IsGametypeTeamBased()
8093                 && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
8094                 && !damageDef->dict.GetBool( "noTeam" )
8095                 && player
8096                 && player != this               // you get self damage no matter what
8097                 && player->team == team ) {
8098                         knockback = 0;
8099                 }
8100 #endif*/
8101
8102         if ( knockback != 0 && !fl.noknockback ) {
8103                 if ( attacker == this ) {
8104                         damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
8105                 } else {
8106                         attackerPushScale = 1.0f;
8107                 }
8108
8109                 kick = dir;
8110                 kick.Normalize();
8111                 kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
8112                 physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
8113
8114                 // set the timer so that the player can't cancel out the movement immediately
8115                 physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
8116         }
8117
8118
8119         // give feedback on the player view and audibly when armor is helping
8120         if ( armorSave ) {
8121                 inventory.armor -= armorSave;
8122
8123                 if ( gameLocal.time > lastArmorPulse + 200 ) {
8124                         StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
8125                 }
8126                 lastArmorPulse = gameLocal.time;
8127         }
8128         
8129         if ( damageDef->dict.GetBool( "burn" ) ) {
8130                 StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
8131         } else if ( damageDef->dict.GetBool( "no_air" ) ) {
8132                 if ( !armorSave && health > 0 ) {
8133                         StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
8134                 }
8135         }
8136
8137         if ( g_debugDamage.GetInteger() ) {
8138                 gameLocal.Printf( "client:%i health:%i damage:%i armor:%i\n", 
8139                         entityNumber, health, damage, armorSave );
8140         }
8141
8142         // move the world direction vector to local coordinates
8143         damage_from = dir;
8144         damage_from.Normalize();
8145         
8146         viewAxis.ProjectVector( damage_from, localDamageVector );
8147
8148         // add to the damage inflicted on a player this frame
8149         // the total will be turned into screen blends and view angle kicks
8150         // at the end of the frame
8151         if ( health > 0 ) {
8152                 playerView.DamageImpulse( localDamageVector, &damageDef->dict );
8153         }
8154
8155         // do the damage
8156         if ( damage > 0 ) {
8157
8158                 if ( !gameLocal.isMultiplayer ) {
8159 #ifdef _D3XP
8160                         float scale = new_g_damageScale;
8161 #else
8162                         float scale = g_damageScale.GetFloat();
8163 #endif
8164                         if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
8165                                 if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
8166                                         scale -= 0.05f;
8167 #ifdef _D3XP
8168                                         new_g_damageScale = scale;
8169 #else
8170                                         g_damageScale.SetFloat( scale );
8171 #endif
8172                                 }
8173                         }
8174
8175                         if ( scale > 0.0f ) {
8176                                 damage *= scale;
8177                         }
8178                 }
8179
8180                 if ( damage < 1 ) {
8181                         damage = 1;
8182                 }
8183
8184                 int oldHealth = health;
8185                 health -= damage;
8186
8187                 if ( health <= 0 ) {
8188
8189                         if ( health < -999 ) {
8190                                 health = -999;
8191                         }
8192
8193                         isTelefragged = damageDef->dict.GetBool( "telefrag" );
8194
8195                         lastDmgTime = gameLocal.time;
8196                         Killed( inflictor, attacker, damage, dir, location );
8197
8198                 } else {
8199                         // force a blink
8200                         blink_time = 0;
8201
8202                         // let the anim script know we took damage
8203                         AI_PAIN = Pain( inflictor, attacker, damage, dir, location );
8204                         if ( !g_testDeath.GetBool() ) {
8205                                 lastDmgTime = gameLocal.time;
8206                         }
8207                 }
8208         } else {
8209                 // don't accumulate impulses
8210                 if ( af.IsLoaded() ) {
8211                         // clear impacts
8212                         af.Rest();
8213
8214                         // physics is turned off by calling af.Rest()
8215                         BecomeActive( TH_PHYSICS );
8216                 }
8217         }
8218
8219         lastDamageDef = damageDef->Index();
8220         lastDamageDir = damage_from;
8221         lastDamageLocation = location;
8222 }
8223
8224 /*
8225 ===========
8226 idPlayer::Teleport
8227 ============
8228 */
8229 void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
8230         idVec3 org;
8231
8232         if ( weapon.GetEntity() ) {
8233                 weapon.GetEntity()->LowerWeapon();
8234         }
8235
8236         SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
8237         if ( !gameLocal.isMultiplayer && GetFloorPos( 16.0f, org ) ) {
8238                 SetOrigin( org );
8239         }
8240
8241         // clear the ik heights so model doesn't appear in the wrong place
8242         walkIK.EnableAll();
8243
8244         GetPhysics()->SetLinearVelocity( vec3_origin );
8245
8246         SetViewAngles( angles );
8247
8248         legsYaw = 0.0f;
8249         idealLegsYaw = 0.0f;
8250         oldViewYaw = viewAngles.yaw;
8251
8252         if ( gameLocal.isMultiplayer ) {
8253                 playerView.Flash( colorWhite, 140 );
8254         }
8255
8256         UpdateVisuals();
8257
8258         teleportEntity = destination;
8259
8260         if ( !gameLocal.isClient && !noclip ) {
8261                 if ( gameLocal.isMultiplayer ) {
8262                         // kill anything at the new position or mark for kill depending on immediate or delayed teleport
8263                         gameLocal.KillBox( this, destination != NULL );
8264                 } else {
8265                         // kill anything at the new position
8266                         gameLocal.KillBox( this, true );
8267                 }
8268         }
8269
8270 #ifdef _D3XP
8271         if ( PowerUpActive( HELLTIME ) ) {
8272                 StopHelltime();
8273         }
8274 #endif
8275 }
8276
8277 /*
8278 ====================
8279 idPlayer::SetPrivateCameraView
8280 ====================
8281 */
8282 void idPlayer::SetPrivateCameraView( idCamera *camView ) {
8283         privateCameraView = camView;
8284         if ( camView ) {
8285                 StopFiring();
8286                 Hide();
8287         } else {
8288                 if ( !spectating ) {
8289                         Show();
8290                 }
8291         }
8292 }
8293
8294 /*
8295 ====================
8296 idPlayer::DefaultFov
8297
8298 Returns the base FOV
8299 ====================
8300 */
8301 float idPlayer::DefaultFov( void ) const {
8302         float fov;
8303
8304         fov = g_fov.GetFloat();
8305         if ( gameLocal.isMultiplayer ) {
8306                 if ( fov < 90.0f ) {
8307                         return 90.0f;
8308                 } else if ( fov > 110.0f ) {
8309                         return 110.0f;
8310                 }
8311         }
8312
8313         return fov;
8314 }
8315
8316 /*
8317 ====================
8318 idPlayer::CalcFov
8319
8320 Fixed fov at intermissions, otherwise account for fov variable and zooms.
8321 ====================
8322 */
8323 float idPlayer::CalcFov( bool honorZoom ) {
8324         float fov;
8325
8326         if ( fxFov ) {
8327                 return DefaultFov() + 10.0f + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
8328         }
8329
8330         if ( influenceFov ) {
8331                 return influenceFov;
8332         }
8333
8334         if ( zoomFov.IsDone( gameLocal.time ) ) {
8335                 fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->GetZoomFov() : DefaultFov();
8336         } else {
8337                 fov = zoomFov.GetCurrentValue( gameLocal.time );
8338         }
8339
8340         // bound normal viewsize
8341         if ( fov < 1 ) {
8342                 fov = 1;
8343         } else if ( fov > 179 ) {
8344                 fov = 179;
8345         }
8346
8347         return fov;
8348 }
8349
8350 /*
8351 ==============
8352 idPlayer::GunTurningOffset
8353
8354 generate a rotational offset for the gun based on the view angle
8355 history in loggedViewAngles
8356 ==============
8357 */
8358 idAngles idPlayer::GunTurningOffset( void ) {
8359         idAngles        a;
8360
8361         a.Zero();
8362
8363         if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
8364                 return a;
8365         }
8366
8367         idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
8368
8369         idAngles        av, base;
8370         int weaponAngleOffsetAverages;
8371         float weaponAngleOffsetScale, weaponAngleOffsetMax;
8372
8373         weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
8374
8375         av = current;
8376
8377         // calcualte this so the wrap arounds work properly
8378         for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
8379                 idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
8380
8381                 idAngles delta = a2 - current;
8382
8383                 if ( delta[1] > 180 ) {
8384                         delta[1] -= 360;
8385                 } else if ( delta[1] < -180 ) {
8386                         delta[1] += 360;
8387                 }
8388
8389                 av += delta * ( 1.0f / weaponAngleOffsetAverages );
8390         }
8391
8392         a = ( av - current ) * weaponAngleOffsetScale;
8393
8394         for ( int i = 0 ; i < 3 ; i++ ) {
8395                 if ( a[i] < -weaponAngleOffsetMax ) {
8396                         a[i] = -weaponAngleOffsetMax;
8397                 } else if ( a[i] > weaponAngleOffsetMax ) {
8398                         a[i] = weaponAngleOffsetMax;
8399                 }
8400         }
8401
8402         return a;
8403 }
8404
8405 /*
8406 ==============
8407 idPlayer::GunAcceleratingOffset
8408
8409 generate a positional offset for the gun based on the movement
8410 history in loggedAccelerations
8411 ==============
8412 */
8413 idVec3  idPlayer::GunAcceleratingOffset( void ) {
8414         idVec3  ofs;
8415
8416         float weaponOffsetTime, weaponOffsetScale;
8417
8418         ofs.Zero();
8419
8420         weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
8421
8422         int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
8423         if ( stop < 0 ) {
8424                 stop = 0;
8425         }
8426         for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
8427                 loggedAccel_t   *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
8428
8429                 float   f;
8430                 float   t = gameLocal.time - acc->time;
8431                 if ( t >= weaponOffsetTime ) {
8432                         break;  // remainder are too old to care about
8433                 }
8434
8435                 f = t / weaponOffsetTime;
8436                 f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
8437                 ofs += f * weaponOffsetScale * acc->dir;
8438         }
8439
8440         return ofs;
8441 }
8442
8443 /*
8444 ==============
8445 idPlayer::CalculateViewWeaponPos
8446
8447 Calculate the bobbing position of the view weapon
8448 ==============
8449 */
8450 void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
8451         float           scale;
8452         float           fracsin;
8453         idAngles        angles;
8454         int                     delta;
8455
8456         // CalculateRenderView must have been called first
8457         const idVec3 &viewOrigin = firstPersonViewOrigin;
8458         const idMat3 &viewAxis = firstPersonViewAxis;
8459
8460         // these cvars are just for hand tweaking before moving a value to the weapon def
8461         idVec3  gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
8462
8463         // as the player changes direction, the gun will take a small lag
8464         idVec3  gunOfs = GunAcceleratingOffset();
8465         origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
8466
8467         // on odd legs, invert some angles
8468         if ( bobCycle & 128 ) {
8469                 scale = -xyspeed;
8470         } else {
8471                 scale = xyspeed;
8472         }
8473
8474         // gun angles from bobbing
8475         angles.roll             = scale * bobfracsin * 0.005f;
8476         angles.yaw              = scale * bobfracsin * 0.01f;
8477         angles.pitch    = xyspeed * bobfracsin * 0.005f;
8478
8479         // gun angles from turning
8480         if ( gameLocal.isMultiplayer ) {
8481                 idAngles offset = GunTurningOffset();
8482                 offset *= g_mpWeaponAngleScale.GetFloat();
8483                 angles += offset;
8484         } else {
8485                 angles += GunTurningOffset();
8486         }
8487
8488         idVec3 gravity = physicsObj.GetGravityNormal();
8489
8490         // drop the weapon when landing after a jump / fall
8491         delta = gameLocal.time - landTime;
8492         if ( delta < LAND_DEFLECT_TIME ) {
8493                 origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
8494         } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
8495                 origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
8496         }
8497
8498         // speed sensitive idle drift
8499         scale = xyspeed + 40.0f;
8500         fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
8501         angles.roll             += fracsin;
8502         angles.yaw              += fracsin;
8503         angles.pitch    += fracsin;
8504
8505         axis = angles.ToMat3() * viewAxis;
8506 }
8507
8508 /*
8509 ===============
8510 idPlayer::OffsetThirdPersonView
8511 ===============
8512 */
8513 void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
8514         idVec3                  view;
8515         idVec3                  focusAngles;
8516         trace_t                 trace;
8517         idVec3                  focusPoint;
8518         float                   focusDist;
8519         float                   forwardScale, sideScale;
8520         idVec3                  origin;
8521         idAngles                angles;
8522         idMat3                  axis;
8523         idBounds                bounds;
8524
8525         angles = viewAngles;
8526         GetViewPos( origin, axis );
8527
8528         if ( angle ) {
8529                 angles.pitch = 0.0f;
8530         }
8531
8532         if ( angles.pitch > 45.0f ) {
8533                 angles.pitch = 45.0f;           // don't go too far overhead
8534         }
8535
8536         focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
8537         focusPoint.z += height;
8538         view = origin;
8539         view.z += 8 + height;
8540
8541         angles.pitch *= 0.5f;
8542         renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
8543
8544         idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
8545         view -= range * forwardScale * renderView->viewaxis[ 0 ];
8546         view += range * sideScale * renderView->viewaxis[ 1 ];
8547
8548         if ( clip ) {
8549                 // trace a ray from the origin to the viewpoint to make sure the view isn't
8550                 // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
8551                 bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
8552                 gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
8553                 if ( trace.fraction != 1.0f ) {
8554                         view = trace.endpos;
8555                         view.z += ( 1.0f - trace.fraction ) * 32.0f;
8556
8557                         // try another trace to this position, because a tunnel may have the ceiling
8558                         // close enough that this is poking out
8559                         gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
8560                         view = trace.endpos;
8561                 }
8562         }
8563
8564         // select pitch to look at focus point from vieword
8565         focusPoint -= view;
8566         focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
8567         if ( focusDist < 1.0f ) {
8568                 focusDist = 1.0f;       // should never happen
8569         }
8570
8571         angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) );
8572         angles.yaw -= angle;
8573
8574         renderView->vieworg = view;
8575         renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
8576         renderView->viewID = 0;
8577 }
8578
8579 /*
8580 ===============
8581 idPlayer::GetEyePosition
8582 ===============
8583 */
8584 idVec3 idPlayer::GetEyePosition( void ) const {
8585         idVec3 org;
8586  
8587         // use the smoothed origin if spectating another player in multiplayer
8588         if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
8589                 org = smoothedOrigin;
8590         } else {
8591                 org = GetPhysics()->GetOrigin();
8592         }
8593         return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
8594 }
8595
8596 /*
8597 ===============
8598 idPlayer::GetViewPos
8599 ===============
8600 */
8601 void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
8602         idAngles angles;
8603
8604         // if dead, fix the angle and don't add any kick
8605         if ( health <= 0 ) {
8606                 angles.yaw = viewAngles.yaw;
8607                 angles.roll = 40;
8608                 angles.pitch = -15;
8609                 axis = angles.ToMat3();
8610                 origin = GetEyePosition();
8611         } else {
8612                 origin = GetEyePosition() + viewBob;
8613                 angles = viewAngles + viewBobAngles + playerView.AngleOffset();
8614
8615                 axis = angles.ToMat3() * physicsObj.GetGravityAxis();
8616
8617                 // adjust the origin based on the camera nodal distance (eye distance from neck)
8618                 origin += physicsObj.GetGravityNormal() * g_viewNodalZ.GetFloat();
8619                 origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
8620         }
8621 }
8622
8623 /*
8624 ===============
8625 idPlayer::CalculateFirstPersonView
8626 ===============
8627 */
8628 void idPlayer::CalculateFirstPersonView( void ) {
8629         if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
8630                 //      Displays the view from the point of view of the "camera" joint in the player model
8631
8632                 idMat3 axis;
8633                 idVec3 origin;
8634                 idAngles ang;
8635
8636                 ang = viewBobAngles + playerView.AngleOffset();
8637                 ang.yaw += viewAxis[ 0 ].ToYaw();
8638                 
8639                 jointHandle_t joint = animator.GetJointHandle( "camera" );
8640                 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
8641                 firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
8642                 firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
8643         } else {
8644                 // offset for local bobbing and kicks
8645                 GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
8646 #if 0
8647                 // shakefrom sound stuff only happens in first person
8648                 firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
8649 #endif
8650         }
8651 }
8652
8653 /*
8654 ==================
8655 idPlayer::GetRenderView
8656
8657 Returns the renderView that was calculated for this tic
8658 ==================
8659 */
8660 renderView_t *idPlayer::GetRenderView( void ) {
8661         return renderView;
8662 }
8663
8664 /*
8665 ==================
8666 idPlayer::CalculateRenderView
8667
8668 create the renderView for the current tic
8669 ==================
8670 */
8671 void idPlayer::CalculateRenderView( void ) {
8672         int i;
8673         float range;
8674
8675         if ( !renderView ) {
8676                 renderView = new renderView_t;
8677         }
8678         memset( renderView, 0, sizeof( *renderView ) );
8679
8680         // copy global shader parms
8681         for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
8682                 renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
8683         }
8684         renderView->globalMaterial = gameLocal.GetGlobalMaterial();
8685
8686 #ifdef _D3XP
8687         renderView->time = gameLocal.slow.time;
8688 #endif
8689
8690         // calculate size of 3D view
8691         renderView->x = 0;
8692         renderView->y = 0;
8693         renderView->width = SCREEN_WIDTH;
8694         renderView->height = SCREEN_HEIGHT;
8695         renderView->viewID = 0;
8696
8697         // check if we should be drawing from a camera's POV
8698         if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
8699                 // get origin, axis, and fov
8700                 if ( privateCameraView ) {
8701                         privateCameraView->GetViewParms( renderView );
8702                 } else {
8703                         gameLocal.GetCamera()->GetViewParms( renderView );
8704                 }
8705         } else {
8706                 if ( g_stopTime.GetBool() ) {
8707                         renderView->vieworg = firstPersonViewOrigin;
8708                         renderView->viewaxis = firstPersonViewAxis;
8709
8710                         if ( !pm_thirdPerson.GetBool() ) {
8711                                 // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
8712                                 // allow the right player view weapons
8713                                 renderView->viewID = entityNumber + 1;
8714                         }
8715                 } else if ( pm_thirdPerson.GetBool() ) {
8716                         OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
8717                 } else if ( pm_thirdPersonDeath.GetBool() ) {
8718                         range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
8719                         OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
8720                 } else {
8721                         renderView->vieworg = firstPersonViewOrigin;
8722                         renderView->viewaxis = firstPersonViewAxis;
8723
8724                         // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
8725                         // allow the right player view weapons
8726                         renderView->viewID = entityNumber + 1;
8727                 }
8728                 
8729                 // field of view
8730                 gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
8731         }
8732
8733         if ( renderView->fov_y == 0 ) {
8734                 common->Error( "renderView->fov_y == 0" );
8735         }
8736
8737         if ( g_showviewpos.GetBool() ) {
8738                 gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
8739         }
8740 }
8741
8742 /*
8743 =============
8744 idPlayer::AddAIKill
8745 =============
8746 */
8747 void idPlayer::AddAIKill( void ) {
8748
8749 #ifndef _D3XP
8750
8751         int max_souls;
8752         int ammo_souls;
8753
8754         if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
8755                 return;
8756         }
8757
8758         assert( hud );
8759
8760
8761         ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
8762         max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
8763         if ( inventory.ammo[ ammo_souls ] < max_souls ) {
8764                 inventory.ammo[ ammo_souls ]++;
8765                 if ( inventory.ammo[ ammo_souls ] >= max_souls ) {
8766                         hud->HandleNamedEvent( "soulCubeReady" );
8767                         StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
8768                 }
8769         }
8770 #endif
8771 }
8772
8773 /*
8774 =============
8775 idPlayer::SetSoulCubeProjectile
8776 =============
8777 */
8778 void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
8779         soulCubeProjectile = projectile;
8780 }
8781
8782 /*
8783 =============
8784 idPlayer::AddProjectilesFired
8785 =============
8786 */
8787 void idPlayer::AddProjectilesFired( int count ) {
8788         numProjectilesFired += count;
8789 }
8790
8791 /*
8792 =============
8793 idPlayer::AddProjectileHites
8794 =============
8795 */
8796 void idPlayer::AddProjectileHits( int count ) {
8797         numProjectileHits += count;
8798 }
8799
8800 /*
8801 =============
8802 idPlayer::SetLastHitTime
8803 =============
8804 */
8805 void idPlayer::SetLastHitTime( int time ) {
8806         idPlayer *aimed = NULL;
8807
8808         if ( time && lastHitTime != time ) {
8809                 lastHitToggle ^= 1;
8810         }
8811         lastHitTime = time;
8812         if ( !time ) {
8813                 // level start and inits
8814                 return;
8815         }
8816         if ( gameLocal.isMultiplayer && ( time - lastSndHitTime ) > 10 ) {
8817                 lastSndHitTime = time;
8818                 StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
8819         }
8820         if ( cursor ) {
8821                 cursor->HandleNamedEvent( "hitTime" );
8822         }
8823         if ( hud ) {
8824                 if ( MPAim != -1 ) {
8825                         if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
8826                                 aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
8827                         }
8828                         assert( aimed );
8829                         // full highlight, no fade till loosing aim
8830                         hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
8831                         if ( aimed ) {
8832                                 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
8833                         }
8834                         hud->HandleNamedEvent( "aim_flash" );
8835                         MPAimHighlight = true;
8836                         MPAimFadeTime = 0;
8837                 } else if ( lastMPAim != -1 ) {
8838                         if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
8839                                 aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
8840                         }
8841                         assert( aimed );
8842                         // start fading right away
8843                         hud->SetStateString( "aim_text", gameLocal.userInfo[ lastMPAim ].GetString( "ui_name" ) );
8844                         if ( aimed ) {
8845                                 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
8846                         }
8847                         hud->HandleNamedEvent( "aim_flash" );
8848                         hud->HandleNamedEvent( "aim_fade" );
8849                         MPAimHighlight = false;
8850                         MPAimFadeTime = gameLocal.realClientTime;
8851                 }
8852         }
8853 }
8854
8855 /*
8856 =============
8857 idPlayer::SetInfluenceLevel
8858 =============
8859 */
8860 void idPlayer::SetInfluenceLevel( int level ) {
8861         if ( level != influenceActive ) {
8862                 if ( level ) {
8863                         for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
8864                                 if ( ent->IsType( idProjectile::Type ) ) {
8865                                         // remove all projectiles
8866                                         ent->PostEventMS( &EV_Remove, 0 );
8867                                 }
8868                         }
8869                         if ( weaponEnabled && weapon.GetEntity() ) {
8870                                 weapon.GetEntity()->EnterCinematic();
8871                         }
8872                 } else {
8873                         physicsObj.SetLinearVelocity( vec3_origin );
8874                         if ( weaponEnabled && weapon.GetEntity() ) {
8875                                 weapon.GetEntity()->ExitCinematic();
8876                         }
8877                 }
8878                 influenceActive = level;
8879         }
8880 }
8881
8882 /*
8883 =============
8884 idPlayer::SetInfluenceView
8885 =============
8886 */
8887 void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
8888         influenceMaterial = NULL;
8889         influenceEntity = NULL;
8890         influenceSkin = NULL;
8891         if ( mtr && *mtr ) {
8892                 influenceMaterial = declManager->FindMaterial( mtr );
8893         }
8894         if ( skinname && *skinname ) {
8895                 influenceSkin = declManager->FindSkin( skinname );
8896                 if ( head.GetEntity() ) {
8897                         head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
8898                 }
8899                 UpdateVisuals();
8900         }
8901         influenceRadius = radius;
8902         if ( radius > 0.0f ) {
8903                 influenceEntity = ent;
8904         }
8905 }
8906
8907 /*
8908 =============
8909 idPlayer::SetInfluenceFov
8910 =============
8911 */
8912 void idPlayer::SetInfluenceFov( float fov ) {
8913         influenceFov = fov;
8914 }
8915
8916 /*
8917 ================
8918 idPlayer::OnLadder
8919 ================
8920 */
8921 bool idPlayer::OnLadder( void ) const {
8922         return physicsObj.OnLadder();
8923 }
8924
8925 /*
8926 ==================
8927 idPlayer::Event_GetButtons
8928 ==================
8929 */
8930 void idPlayer::Event_GetButtons( void ) {
8931         idThread::ReturnInt( usercmd.buttons );
8932 }
8933
8934 /*
8935 ==================
8936 idPlayer::Event_GetMove
8937 ==================
8938 */
8939 void idPlayer::Event_GetMove( void ) {
8940         idVec3 move( usercmd.forwardmove, usercmd.rightmove, usercmd.upmove );
8941         idThread::ReturnVector( move );
8942 }
8943
8944 /*
8945 ================
8946 idPlayer::Event_GetViewAngles
8947 ================
8948 */
8949 void idPlayer::Event_GetViewAngles( void ) {
8950         idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
8951 }
8952
8953 /*
8954 ==================
8955 idPlayer::Event_StopFxFov
8956 ==================
8957 */
8958 void idPlayer::Event_StopFxFov( void ) {
8959         fxFov = false;
8960 }
8961
8962 /*
8963 ==================
8964 idPlayer::StartFxFov 
8965 ==================
8966 */
8967 void idPlayer::StartFxFov( float duration ) { 
8968         fxFov = true;
8969         PostEventSec( &EV_Player_StopFxFov, duration );
8970 }
8971
8972 /*
8973 ==================
8974 idPlayer::Event_EnableWeapon 
8975 ==================
8976 */
8977 void idPlayer::Event_EnableWeapon( void ) {
8978         hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
8979         weaponEnabled = true;
8980         if ( weapon.GetEntity() ) {
8981                 weapon.GetEntity()->ExitCinematic();
8982         }
8983 }
8984
8985 /*
8986 ==================
8987 idPlayer::Event_DisableWeapon
8988 ==================
8989 */
8990 void idPlayer::Event_DisableWeapon( void ) {
8991         hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
8992         weaponEnabled = false;
8993         if ( weapon.GetEntity() ) {
8994                 weapon.GetEntity()->EnterCinematic();
8995         }
8996 }
8997
8998 #ifdef _D3XP
8999 /*
9000 ==================
9001 idPlayer::Event_GiveInventoryItem 
9002 ==================
9003 */
9004 void idPlayer::Event_GiveInventoryItem( const char* name ) {
9005         GiveInventoryItem(name);
9006 }
9007
9008 /*
9009 ==================
9010 idPlayer::Event_RemoveInventoryItem 
9011 ==================
9012 */
9013 void idPlayer::Event_RemoveInventoryItem( const char* name ) {
9014         RemoveInventoryItem(name);
9015 }
9016
9017 /*
9018 ==================
9019 idPlayer::Event_GetIdealWeapon 
9020 ==================
9021 */
9022 void idPlayer::Event_GetIdealWeapon( void ) {
9023         const char *weapon;
9024
9025         if ( idealWeapon >= 0 ) {
9026                 weapon = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
9027                 idThread::ReturnString( weapon );
9028         } else {
9029                 idThread::ReturnString( "" );
9030         }
9031 }
9032
9033 /*
9034 ==================
9035 idPlayer::Event_SetPowerupTime 
9036 ==================
9037 */
9038 void idPlayer::Event_SetPowerupTime( int powerup, int time ) {
9039         if ( time > 0 ) {
9040                 GivePowerUp( powerup, time );
9041         } else {
9042                 ClearPowerup( powerup );
9043         }
9044 }
9045
9046 /*
9047 ==================
9048 idPlayer::Event_IsPowerupActive 
9049 ==================
9050 */
9051 void idPlayer::Event_IsPowerupActive( int powerup ) {
9052         idThread::ReturnInt(this->PowerUpActive(powerup) ? 1 : 0);
9053 }
9054
9055 /*
9056 ==================
9057 idPlayer::Event_StartWarp
9058 ==================
9059 */
9060 void idPlayer::Event_StartWarp() {
9061         playerView.AddWarp( idVec3( 0, 0, 0 ), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100, 1000 );
9062 }
9063
9064 /*
9065 ==================
9066 idPlayer::Event_StopHelltime
9067 ==================
9068 */
9069 void idPlayer::Event_StopHelltime( int mode ) {
9070         if ( mode == 1 ) {
9071                 StopHelltime( true );
9072         }
9073         else {
9074                 StopHelltime( false );
9075         }
9076 }
9077
9078 /*
9079 ==================
9080 idPlayer::Event_WeaponAvailable
9081 ==================
9082 */
9083 void idPlayer::Event_WeaponAvailable( const char* name ) {
9084
9085         idThread::ReturnInt( WeaponAvailable(name) ? 1 : 0 );
9086 }
9087
9088 bool idPlayer::WeaponAvailable( const char* name ) {
9089         for( int i = 0; i < MAX_WEAPONS; i++ ) {
9090                 if ( inventory.weapons & ( 1 << i ) ) {
9091                         const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
9092                         if ( !idStr::Cmp( weap, name ) ) {
9093                                 return true;
9094                         }
9095                 }
9096         }
9097         return false;
9098 }
9099
9100 #endif
9101
9102 /*
9103 ==================
9104 idPlayer::Event_GetCurrentWeapon
9105 ==================
9106 */
9107 void idPlayer::Event_GetCurrentWeapon( void ) {
9108         const char *weapon;
9109
9110         if ( currentWeapon >= 0 ) {
9111                 weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
9112                 idThread::ReturnString( weapon );
9113         } else {
9114                 idThread::ReturnString( "" );
9115         }
9116 }
9117
9118 /*
9119 ==================
9120 idPlayer::Event_GetPreviousWeapon
9121 ==================
9122 */
9123 void idPlayer::Event_GetPreviousWeapon( void ) {
9124         const char *weapon;
9125
9126         if ( previousWeapon >= 0 ) {
9127                 int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
9128                 weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
9129                 idThread::ReturnString( weapon );
9130         } else {
9131                 idThread::ReturnString( spawnArgs.GetString( "def_weapon0" ) );
9132         }
9133 }
9134
9135 /*
9136 ==================
9137 idPlayer::Event_SelectWeapon
9138 ==================
9139 */
9140 void idPlayer::Event_SelectWeapon( const char *weaponName ) {
9141         int i;
9142         int weaponNum;
9143
9144         if ( gameLocal.isClient ) {
9145                 gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
9146                 return;
9147         }
9148
9149         if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
9150                 idealWeapon = weapon_fists;
9151                 weapon.GetEntity()->HideWeapon();
9152                 return;
9153         }
9154
9155         weaponNum = -1;
9156         for( i = 0; i < MAX_WEAPONS; i++ ) {
9157                 if ( inventory.weapons & ( 1 << i ) ) {
9158                         const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
9159                         if ( !idStr::Cmp( weap, weaponName ) ) {
9160                                 weaponNum = i;
9161                                 break;
9162                         }
9163                 }
9164         }
9165
9166         if ( weaponNum < 0 ) {
9167                 gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
9168                 return;
9169         }
9170
9171         hiddenWeapon = false;
9172         idealWeapon = weaponNum;
9173
9174         UpdateHudWeapon();
9175 }
9176
9177 /*
9178 ==================
9179 idPlayer::Event_GetWeaponEntity
9180 ==================
9181 */
9182 void idPlayer::Event_GetWeaponEntity( void ) {
9183         idThread::ReturnEntity( weapon.GetEntity() );
9184 }
9185
9186 /*
9187 ==================
9188 idPlayer::Event_OpenPDA
9189 ==================
9190 */
9191 void idPlayer::Event_OpenPDA( void ) {
9192         if ( !gameLocal.isMultiplayer ) {
9193                 TogglePDA();
9194         }
9195 }
9196
9197 /*
9198 ==================
9199 idPlayer::Event_InPDA
9200 ==================
9201 */
9202 void idPlayer::Event_InPDA( void ) {
9203         idThread::ReturnInt( objectiveSystemOpen );
9204 }
9205
9206 /*
9207 ==================
9208 idPlayer::TeleportDeath
9209 ==================
9210 */
9211 void idPlayer::TeleportDeath( int killer ) {
9212         teleportKiller = killer;
9213 }
9214
9215 /*
9216 ==================
9217 idPlayer::Event_ExitTeleporter
9218 ==================
9219 */
9220 void idPlayer::Event_ExitTeleporter( void ) {
9221         idEntity        *exitEnt;
9222         float           pushVel;
9223
9224         // verify and setup
9225         exitEnt = teleportEntity.GetEntity();
9226         if ( !exitEnt ) {
9227                 common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
9228                 return;
9229         }
9230
9231         pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
9232
9233         if ( gameLocal.isServer ) {
9234                 ServerSendEvent( EVENT_EXIT_TELEPORTER, NULL, false, -1 );
9235         }
9236
9237         SetPrivateCameraView( NULL );
9238         // setup origin and push according to the exit target
9239         SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
9240         SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
9241         physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
9242         physicsObj.ClearPushedVelocity();
9243         // teleport fx
9244         playerView.Flash( colorWhite, 120 );
9245
9246         // clear the ik heights so model doesn't appear in the wrong place
9247         walkIK.EnableAll();
9248
9249         UpdateVisuals();
9250
9251         StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
9252
9253         if ( teleportKiller != -1 ) {
9254                 // we got killed while being teleported
9255                 Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
9256                 teleportKiller = -1;
9257         } else {
9258                 // kill anything that would have waited at teleport exit
9259                 gameLocal.KillBox( this );
9260         }
9261         teleportEntity = NULL;
9262 }
9263
9264 /*
9265 ================
9266 idPlayer::ClientPredictionThink
9267 ================
9268 */
9269 void idPlayer::ClientPredictionThink( void ) {
9270         renderEntity_t *headRenderEnt;
9271
9272         oldFlags = usercmd.flags;
9273         oldButtons = usercmd.buttons;
9274
9275         usercmd = gameLocal.usercmds[ entityNumber ];
9276
9277         if ( entityNumber != gameLocal.localClientNum ) {
9278                 // ignore attack button of other clients. that's no good for predictions
9279                 usercmd.buttons &= ~BUTTON_ATTACK;
9280         }
9281
9282         buttonMask &= usercmd.buttons;
9283         usercmd.buttons &= ~buttonMask;
9284
9285 #ifdef _D3XP
9286         if ( mountedObject ) {
9287                 usercmd.forwardmove = 0;
9288                 usercmd.rightmove = 0;
9289                 usercmd.upmove = 0;
9290         }
9291 #endif
9292
9293         if ( objectiveSystemOpen ) {
9294                 usercmd.forwardmove = 0;
9295                 usercmd.rightmove = 0;
9296                 usercmd.upmove = 0;
9297         }
9298
9299         // clear the ik before we do anything else so the skeleton doesn't get updated twice
9300         walkIK.ClearJointMods();
9301
9302         if ( gameLocal.isNewFrame ) {
9303                 if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
9304                         PerformImpulse( usercmd.impulse );
9305                 }
9306         }
9307
9308         scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
9309
9310         AdjustSpeed();
9311
9312         UpdateViewAngles();
9313
9314         // update the smoothed view angles
9315         if ( gameLocal.framenum >= smoothedFrame && entityNumber != gameLocal.localClientNum ) {
9316                 idAngles anglesDiff = viewAngles - smoothedAngles;
9317                 anglesDiff.Normalize180();
9318                 if ( idMath::Fabs( anglesDiff.yaw ) < 90.0f && idMath::Fabs( anglesDiff.pitch ) < 90.0f ) {
9319                         // smoothen by pushing back to the previous angles
9320                         viewAngles -= gameLocal.clientSmoothing * anglesDiff;
9321                         viewAngles.Normalize180();
9322                 }
9323                 smoothedAngles = viewAngles;
9324         }
9325         smoothedOriginUpdated = false;
9326
9327         if ( !af.IsActive() ) {
9328                 AdjustBodyAngles();
9329         }
9330
9331         if ( !isLagged ) {
9332                 // don't allow client to move when lagged
9333                 Move();
9334         } 
9335
9336         // update GUIs, Items, and character interactions
9337         UpdateFocus();
9338
9339         // service animations
9340         if ( !spectating && !af.IsActive() ) {
9341         UpdateConditions();
9342                 UpdateAnimState();
9343                 CheckBlink();
9344         }
9345
9346         // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
9347         AI_PAIN = false;
9348
9349         // calculate the exact bobbed view position, which is used to
9350         // position the view weapon, among other things
9351         CalculateFirstPersonView();
9352
9353         // this may use firstPersonView, or a thirdPerson / camera view
9354         CalculateRenderView();
9355
9356         if ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
9357                 UpdateWeapon();
9358         }
9359
9360         UpdateHud();
9361
9362         if ( gameLocal.isNewFrame ) {
9363                 UpdatePowerUps();
9364         }
9365
9366         UpdateDeathSkin( false );
9367
9368         if ( head.GetEntity() ) {
9369                 headRenderEnt = head.GetEntity()->GetRenderEntity();
9370         } else {
9371                 headRenderEnt = NULL;
9372         }
9373
9374         if ( headRenderEnt ) {
9375                 if ( influenceSkin ) {
9376                         headRenderEnt->customSkin = influenceSkin;
9377                 } else {
9378                         headRenderEnt->customSkin = NULL;
9379                 }
9380         }
9381
9382         if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
9383                 renderEntity.suppressShadowInViewID     = 0;
9384                 if ( headRenderEnt ) {
9385                         headRenderEnt->suppressShadowInViewID = 0;
9386                 }
9387         } else {
9388                 renderEntity.suppressShadowInViewID     = entityNumber+1;
9389                 if ( headRenderEnt ) {
9390                         headRenderEnt->suppressShadowInViewID = entityNumber+1;
9391                 }
9392         }
9393         // never cast shadows from our first-person muzzle flashes
9394         renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
9395         if ( headRenderEnt ) {
9396                 headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
9397         }
9398
9399         if ( !gameLocal.inCinematic ) {
9400                 UpdateAnimation();
9401         }
9402
9403 #ifdef _D3XP
9404         if ( enviroSuitLight.IsValid() ) {
9405                 idAngles lightAng = firstPersonViewAxis.ToAngles();
9406                 idVec3 lightOrg = firstPersonViewOrigin;
9407                 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
9408
9409                 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
9410                 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
9411
9412                 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
9413                 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
9414                 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
9415                 lightAng.pitch += enviroAngleOffset.x;
9416                 lightAng.yaw += enviroAngleOffset.y;
9417                 lightAng.roll += enviroAngleOffset.z;
9418
9419                 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
9420                 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
9421                 enviroSuitLight.GetEntity()->UpdateVisuals();
9422                 enviroSuitLight.GetEntity()->Present();
9423         }
9424 #endif
9425
9426         if ( gameLocal.isMultiplayer ) {
9427                 DrawPlayerIcons();
9428         }
9429
9430         Present();
9431
9432         UpdateDamageEffects();
9433
9434         LinkCombat();
9435
9436         if ( gameLocal.isNewFrame && entityNumber == gameLocal.localClientNum ) {
9437                 playerView.CalculateShake();
9438         }
9439
9440 #ifdef _D3XP
9441         // determine if portal sky is in pvs
9442         pvsHandle_t     clientPVS = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
9443         gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( clientPVS, GetPhysics()->GetOrigin() );
9444         gameLocal.pvs.FreeCurrentPVS( clientPVS );
9445 #endif
9446 }
9447
9448 /*
9449 ================
9450 idPlayer::GetPhysicsToVisualTransform
9451 ================
9452 */
9453 bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
9454         if ( af.IsActive() ) {
9455                 af.GetPhysicsToVisualTransform( origin, axis );
9456                 return true;
9457         }
9458
9459         // smoothen the rendered origin and angles of other clients
9460         // smooth self origin if snapshots are telling us prediction is off
9461         if ( gameLocal.isClient && gameLocal.framenum >= smoothedFrame && ( entityNumber != gameLocal.localClientNum || selfSmooth ) ) {
9462                 // render origin and axis
9463                 idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
9464                 idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
9465
9466                 // update the smoothed origin
9467                 if ( !smoothedOriginUpdated ) {
9468                         idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
9469                         if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
9470                                 // smoothen by pushing back to the previous position
9471                                 if ( selfSmooth ) {
9472                                         assert( entityNumber == gameLocal.localClientNum );
9473                                         renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
9474                                 } else {
9475                                         renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
9476                                 }
9477                         }
9478                         smoothedOrigin = renderOrigin;
9479
9480                         smoothedFrame = gameLocal.framenum;
9481                         smoothedOriginUpdated = true;
9482                 }
9483
9484                 axis = idAngles( 0.0f, smoothedAngles.yaw, 0.0f ).ToMat3();
9485                 origin = ( smoothedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
9486
9487         } else {
9488
9489                 axis = viewAxis;
9490                 origin = modelOffset;
9491         }
9492         return true;
9493 }
9494
9495 /*
9496 ================
9497 idPlayer::GetPhysicsToSoundTransform
9498 ================
9499 */
9500 bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
9501         idCamera *camera;
9502
9503         if ( privateCameraView ) {
9504                 camera = privateCameraView;
9505         } else {
9506                 camera = gameLocal.GetCamera();
9507         }
9508
9509         if ( camera ) {
9510                 renderView_t view;
9511
9512                 memset( &view, 0, sizeof( view ) );
9513                 camera->GetViewParms( &view );
9514                 origin = view.vieworg;
9515                 axis = view.viewaxis;
9516                 return true;
9517         } else {
9518                 return idActor::GetPhysicsToSoundTransform( origin, axis );
9519         }
9520 }
9521
9522 /*
9523 ================
9524 idPlayer::WriteToSnapshot
9525 ================
9526 */
9527 void idPlayer::WriteToSnapshot( idBitMsgDelta &msg ) const {
9528         physicsObj.WriteToSnapshot( msg );
9529         WriteBindToSnapshot( msg );
9530         msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
9531         msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
9532         msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
9533         msg.WriteShort( health );
9534         msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, lastDamageDef ), gameLocal.entityDefBits );
9535         msg.WriteDir( lastDamageDir, 9 );
9536         msg.WriteShort( lastDamageLocation );
9537         msg.WriteBits( idealWeapon, idMath::BitsForInteger( MAX_WEAPONS ) );
9538         msg.WriteBits( inventory.weapons, MAX_WEAPONS );
9539         msg.WriteBits( weapon.GetSpawnId(), 32 );
9540         msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
9541         msg.WriteBits( lastHitToggle, 1 );
9542         msg.WriteBits( weaponGone, 1 );
9543         msg.WriteBits( isLagged, 1 );
9544         msg.WriteBits( isChatting, 1 );
9545 #ifdef CTF
9546     /* Needed for the scoreboard */
9547     msg.WriteBits( carryingFlag, 1 ); 
9548 #endif
9549 #ifdef _D3XP
9550         msg.WriteBits( enviroSuitLight.GetSpawnId(), 32 );
9551 #endif
9552 }
9553
9554 /*
9555 ================
9556 idPlayer::ReadFromSnapshot
9557 ================
9558 */
9559 void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
9560         int             i, oldHealth, newIdealWeapon, weaponSpawnId;
9561         bool    newHitToggle, stateHitch;
9562
9563         if ( snapshotSequence - lastSnapshotSequence > 1 ) {
9564                 stateHitch = true;
9565         } else {
9566                 stateHitch = false;
9567         }
9568         lastSnapshotSequence = snapshotSequence;
9569
9570         oldHealth = health;
9571
9572         physicsObj.ReadFromSnapshot( msg );
9573         ReadBindFromSnapshot( msg );
9574         deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
9575         deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
9576         deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
9577         health = msg.ReadShort();
9578         lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( gameLocal.entityDefBits ) );
9579         lastDamageDir = msg.ReadDir( 9 );
9580         lastDamageLocation = msg.ReadShort();
9581         newIdealWeapon = msg.ReadBits( idMath::BitsForInteger( MAX_WEAPONS ) );
9582         inventory.weapons = msg.ReadBits( MAX_WEAPONS );
9583         weaponSpawnId = msg.ReadBits( 32 );
9584         spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
9585         newHitToggle = msg.ReadBits( 1 ) != 0;
9586         weaponGone = msg.ReadBits( 1 ) != 0;
9587         isLagged = msg.ReadBits( 1 ) != 0;
9588         isChatting = msg.ReadBits( 1 ) != 0;
9589 #ifdef CTF
9590         carryingFlag = msg.ReadBits( 1 ) != 0;
9591 #endif
9592 #ifdef _D3XP
9593         int enviroSpawnId;
9594         enviroSpawnId = msg.ReadBits( 32 );
9595         enviroSuitLight.SetSpawnId( enviroSpawnId );
9596 #endif
9597
9598         // no msg reading below this
9599
9600         if ( weapon.SetSpawnId( weaponSpawnId ) ) {
9601                 if ( weapon.GetEntity() ) {
9602                         // maintain ownership locally
9603                         weapon.GetEntity()->SetOwner( this );
9604                 }
9605                 currentWeapon = -1;
9606         }
9607         // if not a local client assume the client has all ammo types
9608         if ( entityNumber != gameLocal.localClientNum ) {
9609                 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9610                         inventory.ammo[ i ] = 999;
9611                 }
9612         }
9613
9614         if ( oldHealth > 0 && health <= 0 ) {
9615                 if ( stateHitch ) {
9616                         // so we just hide and don't show a death skin
9617                         UpdateDeathSkin( true );
9618                 }
9619                 // die
9620                 AI_DEAD = true;
9621                 ClearPowerUps();
9622                 SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
9623                 SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
9624                 SetWaitState( "" );
9625                 animator.ClearAllJoints();
9626                 if ( entityNumber == gameLocal.localClientNum ) {
9627                         playerView.Fade( colorBlack, 12000 );
9628                 }
9629                 StartRagdoll();
9630                 physicsObj.SetMovementType( PM_DEAD );
9631                 if ( !stateHitch ) {
9632                         StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
9633                 }
9634                 if ( weapon.GetEntity() ) {
9635                         weapon.GetEntity()->OwnerDied();
9636                 }
9637         } else if ( oldHealth <= 0 && health > 0 ) {
9638                 // respawn
9639                 Init();
9640                 StopRagdoll();
9641                 SetPhysics( &physicsObj );
9642                 physicsObj.EnableClip();
9643                 SetCombatContents( true );
9644         } else if ( health < oldHealth && health > 0 ) {
9645                 if ( stateHitch ) {
9646                         lastDmgTime = gameLocal.time;
9647                 } else {
9648                         // damage feedback
9649                         const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
9650                         if ( def ) {
9651                                 playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
9652                                 AI_PAIN = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
9653                                 lastDmgTime = gameLocal.time;
9654                         } else {
9655                                 common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
9656                         }
9657                 }
9658         } else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !stateHitch ) {
9659                 // just pulse, for any health raise
9660                 healthPulse = true;
9661         }
9662
9663 #ifdef _D3XP
9664         // If the player is alive, restore proper physics object
9665         if ( health > 0 && IsActiveAF() ) {
9666                 StopRagdoll();
9667                 SetPhysics( &physicsObj );
9668                 physicsObj.EnableClip();
9669                 SetCombatContents( true );
9670         }
9671 #endif
9672
9673         if ( idealWeapon != newIdealWeapon ) {
9674                 if ( stateHitch ) {
9675                         weaponCatchup = true;
9676                 }
9677                 idealWeapon = newIdealWeapon;
9678                 UpdateHudWeapon();
9679         }
9680
9681         if ( lastHitToggle != newHitToggle ) {
9682                 SetLastHitTime( gameLocal.realClientTime );
9683         }
9684
9685         if ( msg.HasChanged() ) {
9686                 UpdateVisuals();
9687         }
9688 }
9689
9690 /*
9691 ================
9692 idPlayer::WritePlayerStateToSnapshot
9693 ================
9694 */
9695 void idPlayer::WritePlayerStateToSnapshot( idBitMsgDelta &msg ) const {
9696         int i;
9697
9698         msg.WriteByte( bobCycle );
9699         msg.WriteLong( stepUpTime );
9700         msg.WriteFloat( stepUpDelta );
9701 #ifdef _D3XP
9702         msg.WriteLong( inventory.weapons );
9703 #else
9704         msg.WriteShort( inventory.weapons );
9705 #endif
9706         msg.WriteByte( inventory.armor );
9707
9708         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9709                 msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
9710         }
9711         for( i = 0; i < MAX_WEAPONS; i++ ) {
9712                 msg.WriteBits( inventory.clip[i], ASYNC_PLAYER_INV_CLIP_BITS );
9713         }
9714 }
9715
9716 /*
9717 ================
9718 idPlayer::ReadPlayerStateFromSnapshot
9719 ================
9720 */
9721 void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
9722         int i, ammo;
9723
9724         bobCycle = msg.ReadByte();
9725         stepUpTime = msg.ReadLong();
9726         stepUpDelta = msg.ReadFloat();
9727 #ifdef _D3XP
9728         inventory.weapons = msg.ReadLong();
9729 #else
9730         inventory.weapons = msg.ReadShort();
9731 #endif
9732         inventory.armor = msg.ReadByte();
9733
9734         for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9735                 ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
9736                 if ( gameLocal.time >= inventory.ammoPredictTime ) {
9737                         inventory.ammo[ i ] = ammo;
9738                 }
9739         }
9740         for( i = 0; i < MAX_WEAPONS; i++ ) {
9741                 inventory.clip[i] = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
9742         }
9743 }
9744
9745 /*
9746 ================
9747 idPlayer::ServerReceiveEvent
9748 ================
9749 */
9750 bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
9751
9752         if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
9753                 return true;
9754         }
9755
9756         // client->server events
9757         switch( event ) {
9758                 case EVENT_IMPULSE: {
9759                         PerformImpulse( msg.ReadBits( 6 ) );
9760                         return true;
9761                 }
9762                 default: {
9763                         return false;
9764                 }
9765         }
9766 }
9767
9768 /*
9769 ================
9770 idPlayer::ClientReceiveEvent
9771 ================
9772 */
9773 bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
9774         int powerup;
9775         bool start;
9776
9777         switch ( event ) {
9778                 case EVENT_EXIT_TELEPORTER:
9779                         Event_ExitTeleporter();
9780                         return true;
9781                 case EVENT_ABORT_TELEPORTER:
9782                         SetPrivateCameraView( NULL );
9783                         return true;
9784                 case EVENT_POWERUP: {
9785                         powerup = msg.ReadShort();
9786                         start = msg.ReadBits( 1 ) != 0;
9787                         if ( start ) {
9788                                 GivePowerUp( powerup, 0 );
9789                         } else {
9790                                 ClearPowerup( powerup );
9791                         }       
9792                         return true;
9793                 }
9794 #ifdef _D3XP
9795                 case EVENT_PICKUPNAME: {
9796                         char buf[MAX_EVENT_PARAM_SIZE];
9797                         msg.ReadString(buf, MAX_EVENT_PARAM_SIZE);
9798                         inventory.AddPickupName(buf, "", this); //_D3XP
9799                         return true;
9800                 }
9801 #endif
9802                 case EVENT_SPECTATE: {
9803                         bool spectate = ( msg.ReadBits( 1 ) != 0 );
9804                         Spectate( spectate );
9805                         return true;
9806                 }
9807                 case EVENT_ADD_DAMAGE_EFFECT: {
9808                         if ( spectating ) {
9809                                 // if we're spectating, ignore
9810                                 // happens if the event and the spectate change are written on the server during the same frame (fraglimit)
9811                                 return true;
9812                         }
9813                         return idActor::ClientReceiveEvent( event, time, msg );
9814                 }
9815                 default: {
9816                         return idActor::ClientReceiveEvent( event, time, msg );
9817                 }
9818         }
9819         return false;
9820 }
9821
9822 /*
9823 ================
9824 idPlayer::Hide
9825 ================
9826 */
9827 void idPlayer::Hide( void ) {
9828         idWeapon *weap;
9829
9830         idActor::Hide();
9831         weap = weapon.GetEntity();
9832         if ( weap ) {
9833                 weap->HideWorldModel();
9834         }
9835 }
9836
9837 /*
9838 ================
9839 idPlayer::Show
9840 ================
9841 */
9842 void idPlayer::Show( void ) {
9843         idWeapon *weap;
9844         
9845         idActor::Show();
9846         weap = weapon.GetEntity();
9847         if ( weap ) {
9848                 weap->ShowWorldModel();
9849         }
9850 }
9851
9852 /*
9853 ===============
9854 idPlayer::StartAudioLog
9855 ===============
9856 */
9857 void idPlayer::StartAudioLog( void ) {
9858         if ( hud ) {
9859                 hud->HandleNamedEvent( "audioLogUp" );
9860         }
9861 }
9862
9863 /*
9864 ===============
9865 idPlayer::StopAudioLog
9866 ===============
9867 */
9868 void idPlayer::StopAudioLog( void ) {
9869         if ( hud ) {
9870                 hud->HandleNamedEvent( "audioLogDown" );
9871         }
9872 }
9873
9874 /*
9875 ===============
9876 idPlayer::ShowTip
9877 ===============
9878 */
9879 void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
9880         if ( tipUp ) {
9881                 return;
9882         }
9883         hud->SetStateString( "tip", tip );
9884         hud->SetStateString( "tiptitle", title );
9885         hud->HandleNamedEvent( "tipWindowUp" ); 
9886         if ( autoHide ) {
9887                 PostEventSec( &EV_Player_HideTip, 5.0f );
9888         }
9889         tipUp = true;
9890 }
9891
9892 /*
9893 ===============
9894 idPlayer::HideTip
9895 ===============
9896 */
9897 void idPlayer::HideTip( void ) {
9898         hud->HandleNamedEvent( "tipWindowDown" ); 
9899         tipUp = false;
9900 }
9901
9902 /*
9903 ===============
9904 idPlayer::Event_HideTip
9905 ===============
9906 */
9907 void idPlayer::Event_HideTip( void ) {
9908         HideTip();
9909 }
9910
9911 /*
9912 ===============
9913 idPlayer::ShowObjective
9914 ===============
9915 */
9916 void idPlayer::ShowObjective( const char *obj ) {
9917         hud->HandleNamedEvent( obj );
9918         objectiveUp = true;
9919 }
9920
9921 /*
9922 ===============
9923 idPlayer::HideObjective
9924 ===============
9925 */
9926 void idPlayer::HideObjective( void ) {
9927         hud->HandleNamedEvent( "closeObjective" );
9928         objectiveUp = false;
9929 }
9930
9931 /*
9932 ===============
9933 idPlayer::Event_StopAudioLog
9934 ===============
9935 */
9936 void idPlayer::Event_StopAudioLog( void ) {
9937         StopAudioLog();
9938 }
9939
9940 /*
9941 ===============
9942 idPlayer::SetSpectateOrigin
9943 ===============
9944 */
9945 void idPlayer::SetSpectateOrigin( void ) {
9946         idVec3 neworig;
9947
9948         neworig = GetPhysics()->GetOrigin();
9949         neworig[ 2 ] += EyeHeight();
9950         neworig[ 2 ] += 25;
9951         SetOrigin( neworig );
9952 }
9953
9954 /*
9955 ===============
9956 idPlayer::RemoveWeapon
9957 ===============
9958 */
9959 void idPlayer::RemoveWeapon( const char *weap ) {
9960         if ( weap && *weap ) {
9961                 inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
9962         }
9963 }
9964
9965 /*
9966 ===============
9967 idPlayer::CanShowWeaponViewmodel
9968 ===============
9969 */
9970 bool idPlayer::CanShowWeaponViewmodel( void ) const {
9971         return showWeaponViewModel;
9972 }
9973
9974 /*
9975 ===============
9976 idPlayer::SetLevelTrigger
9977 ===============
9978 */
9979 void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
9980         if ( levelName && *levelName && triggerName && *triggerName ) {
9981                 idLevelTriggerInfo lti;
9982                 lti.levelName = levelName;
9983                 lti.triggerName = triggerName;
9984                 inventory.levelTriggers.Append( lti );
9985         }
9986 }
9987
9988 /*
9989 ===============
9990 idPlayer::Event_LevelTrigger
9991 ===============
9992 */
9993 void idPlayer::Event_LevelTrigger( void ) {
9994         idStr mapName = gameLocal.GetMapName();
9995         mapName.StripPath();
9996         mapName.StripFileExtension();
9997         for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
9998                 if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
9999                         idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
10000                         if ( ent ) {
10001                                 ent->PostEventMS( &EV_Activate, 1, this );
10002                         }
10003                 }
10004         }
10005 }
10006
10007 /*
10008 ===============
10009 idPlayer::Event_Gibbed
10010 ===============
10011 */
10012 void idPlayer::Event_Gibbed( void ) {
10013         // do nothing
10014 }
10015
10016 /*
10017 ===============
10018 idPlayer::UpdatePlayerIcons
10019 ===============
10020 */
10021 void idPlayer::UpdatePlayerIcons( void ) {
10022         int time = networkSystem->ServerGetClientTimeSinceLastPacket( entityNumber );
10023         if ( time > cvarSystem->GetCVarInteger( "net_clientMaxPrediction" ) ) {
10024                 isLagged = true;
10025         } else {
10026                 isLagged = false;
10027         }
10028         // TODO: chatting, PDA, etc?
10029 }
10030
10031 /*
10032 ===============
10033 idPlayer::DrawPlayerIcons
10034 ===============
10035 */
10036 void idPlayer::DrawPlayerIcons( void ) {
10037         if ( !NeedsIcon() ) {
10038                 playerIcon.FreeIcon();
10039                 return;
10040         }
10041
10042 #ifdef CTF
10043     // Never draw icons for hidden players.
10044     if ( this->IsHidden() )
10045         return;
10046 #endif    
10047     
10048         playerIcon.Draw( this, headJoint );
10049 }
10050
10051 /*
10052 ===============
10053 idPlayer::HidePlayerIcons
10054 ===============
10055 */
10056 void idPlayer::HidePlayerIcons( void ) {
10057         playerIcon.FreeIcon();
10058 }
10059
10060 /*
10061 ===============
10062 idPlayer::NeedsIcon
10063 ==============
10064 */
10065 bool idPlayer::NeedsIcon( void ) {
10066         // local clients don't render their own icons... they're only info for other clients
10067 #ifdef CTF
10068         // always draw icons in CTF games
10069         return entityNumber != gameLocal.localClientNum && ( ( g_CTFArrows.GetBool() && gameLocal.mpGame.IsGametypeFlagBased() && !IsHidden() && !AI_DEAD ) || ( isLagged || isChatting ) );
10070 #else
10071         return entityNumber != gameLocal.localClientNum && ( isLagged || isChatting );
10072 #endif
10073 }
10074
10075 #ifdef CTF
10076 /*
10077 ===============
10078 idPlayer::DropFlag()
10079 ==============
10080 */
10081 void idPlayer::DropFlag( void ) {
10082         if ( !carryingFlag || !gameLocal.isMultiplayer || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
10083                 return;
10084
10085         idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - this->latchedTeam );
10086         if ( entity ) {
10087                 idItemTeam * item = static_cast<idItemTeam*>(entity);
10088         
10089                 if ( item->carried && !item->dropped ) {
10090                         item->Drop( health <= 0 );
10091                         carryingFlag = false;
10092                 }
10093         }
10094
10095 }
10096
10097 void idPlayer::ReturnFlag() {
10098
10099         if ( !carryingFlag || !gameLocal.isMultiplayer || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
10100                 return;
10101
10102         idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - this->latchedTeam );
10103         if ( entity ) {
10104                 idItemTeam * item = static_cast<idItemTeam*>(entity);
10105
10106                 if ( item->carried && !item->dropped ) {
10107                         item->Return();
10108                         carryingFlag = false;
10109                 }
10110         }
10111 }
10112
10113 void idPlayer::FreeModelDef( void ) {
10114         idAFEntity_Base::FreeModelDef();
10115         if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() )
10116                 playerIcon.FreeIcon();
10117 }
10118
10119 #endif