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