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