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