2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
35 ===============================================================================
37 Player control of the Doom Marine.
38 This object handles all player movement and world interaction.
40 ===============================================================================
43 // distance between ladder rungs (actually is half that distance, but this sounds better)
44 const int LADDER_RUNG_DISTANCE = 32;
46 // amount of health per dose from the health station
47 const int HEALTH_PER_DOSE = 10;
49 // time before a weapon dropped to the floor disappears
50 const int WEAPON_DROP_TIME = 20 * 1000;
52 // time before a next or prev weapon switch happens
53 const int WEAPON_SWITCH_DELAY = 150;
55 // how many units to raise spectator above default view height so it's in the head of someone
56 const int SPECTATE_RAISE = 25;
58 const int HEALTHPULSE_TIME = 333;
60 // minimum speed to bob and play run/walk animations at
61 const float MIN_BOB_SPEED = 5.0f;
63 const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
64 const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
65 const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
66 const idEventDef EV_Player_StopFxFov( "stopFxFov" );
67 const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
68 const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
69 const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
70 const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
71 const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
72 const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
73 const idEventDef EV_Player_OpenPDA( "openPDA" );
74 const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
75 const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
76 const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
77 const idEventDef EV_Player_HideTip( "hideTip" );
78 const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
79 const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
81 const idEventDef EV_Player_GiveInventoryItem( "giveInventoryItem", "s" );
82 const idEventDef EV_Player_RemoveInventoryItem( "removeInventoryItem", "s" );
83 const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
84 const idEventDef EV_Player_SetPowerupTime( "setPowerupTime", "dd" );
85 const idEventDef EV_Player_IsPowerupActive( "isPowerupActive", "d", 'd' );
86 const idEventDef EV_Player_WeaponAvailable( "weaponAvailable", "s", 'd');
87 const idEventDef EV_Player_StartWarp( "startWarp" );
88 const idEventDef EV_Player_StopHelltime( "stopHelltime", "d" );
89 const idEventDef EV_Player_ToggleBloom( "toggleBloom", "d" );
90 const idEventDef EV_Player_SetBloomParms( "setBloomParms", "ff" );
93 CLASS_DECLARATION( idActor, idPlayer )
94 EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
95 EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
96 EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
97 EVENT( EV_Player_StopFxFov, idPlayer::Event_StopFxFov )
98 EVENT( EV_Player_EnableWeapon, idPlayer::Event_EnableWeapon )
99 EVENT( EV_Player_DisableWeapon, idPlayer::Event_DisableWeapon )
100 EVENT( EV_Player_GetCurrentWeapon, idPlayer::Event_GetCurrentWeapon )
101 EVENT( EV_Player_GetPreviousWeapon, idPlayer::Event_GetPreviousWeapon )
102 EVENT( EV_Player_SelectWeapon, idPlayer::Event_SelectWeapon )
103 EVENT( EV_Player_GetWeaponEntity, idPlayer::Event_GetWeaponEntity )
104 EVENT( EV_Player_OpenPDA, idPlayer::Event_OpenPDA )
105 EVENT( EV_Player_InPDA, idPlayer::Event_InPDA )
106 EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
107 EVENT( EV_Player_StopAudioLog, idPlayer::Event_StopAudioLog )
108 EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
109 EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
110 EVENT( EV_Gibbed, idPlayer::Event_Gibbed )
112 EVENT( EV_Player_GiveInventoryItem, idPlayer::Event_GiveInventoryItem )
113 EVENT( EV_Player_RemoveInventoryItem, idPlayer::Event_RemoveInventoryItem )
114 EVENT( EV_Player_GetIdealWeapon, idPlayer::Event_GetIdealWeapon )
115 EVENT( EV_Player_WeaponAvailable, idPlayer::Event_WeaponAvailable )
116 EVENT( EV_Player_SetPowerupTime, idPlayer::Event_SetPowerupTime )
117 EVENT( EV_Player_IsPowerupActive, idPlayer::Event_IsPowerupActive )
118 EVENT( EV_Player_StartWarp, idPlayer::Event_StartWarp )
119 EVENT( EV_Player_StopHelltime, idPlayer::Event_StopHelltime )
120 EVENT( EV_Player_ToggleBloom, idPlayer::Event_ToggleBloom )
121 EVENT( EV_Player_SetBloomParms, idPlayer::Event_SetBloomParms )
125 const int MAX_RESPAWN_TIME = 10000;
126 const int RAGDOLL_DEATH_TIME = 3000;
127 const int MAX_PDAS = 64;
128 const int MAX_PDA_ITEMS = 128;
129 const int STEPUP_TIME = 200;
130 const int MAX_INVENTORY_ITEMS = 20;
134 idVec3 idPlayer::colorBarTable[ 8 ] = {
136 idVec3 idPlayer::colorBarTable[ 5 ] = {
138 idVec3( 0.25f, 0.25f, 0.25f ),
139 idVec3( 1.00f, 0.00f, 0.00f ),
140 idVec3( 0.00f, 0.80f, 0.10f ),
141 idVec3( 0.20f, 0.50f, 0.80f ),
142 idVec3( 1.00f, 0.80f, 0.10f )
144 ,idVec3( 0.425f, 0.484f, 0.445f ),
145 idVec3( 0.39f, 0.199f, 0.3f ),
146 idVec3( 0.484f, 0.312f, 0.074f)
155 void idInventory::Clear( void ) {
164 nextArmorDepleteTime = 0;
166 memset( ammo, 0, sizeof( ammo ) );
170 // set to -1 so that the gun knows to have a full clip the first time we get it and at the start of the level
171 memset( clip, -1, sizeof( clip ) );
173 items.DeleteContents( true );
174 memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
185 levelTriggers.Clear();
190 pickupItemNames.Clear();
191 objectiveNames.Clear();
204 idInventory::GivePowerUp
207 void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
209 // get the duration from the .def files
210 const idDeclEntityDef *def = NULL;
213 def = gameLocal.FindEntityDef( "powerup_berserk", false );
216 def = gameLocal.FindEntityDef( "powerup_invisibility", false );
219 def = gameLocal.FindEntityDef( "powerup_megahealth", false );
222 def = gameLocal.FindEntityDef( "powerup_adrenaline", false );
225 case INVULNERABILITY:
226 def = gameLocal.FindEntityDef( "powerup_invulnerability", false );
229 def = gameLocal.FindEntityDef( "powerup_haste", false );
234 msec = def->dict.GetInt( "time" ) * 1000;
236 powerups |= 1 << powerup;
237 powerupEndTime[ powerup ] = gameLocal.time + msec;
242 idInventory::ClearPowerUps
245 void idInventory::ClearPowerUps( void ) {
247 for ( i = 0; i < MAX_POWERUPS; i++ ) {
248 powerupEndTime[ i ] = 0;
255 idInventory::GetPersistantData
258 void idInventory::GetPersistantData( idDict &dict ) {
263 const idKeyValue *kv;
267 dict.SetInt( "armor", armor );
269 // don't bother with powerups, maxhealth, maxarmor, or the clip
272 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
273 name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
275 dict.SetInt( name, ammo[ i ] );
281 for( i = 0; i < MAX_WEAPONS; i++ ) {
282 dict.SetInt( va("clip%i", i), clip[ i ] );
288 for( i = 0; i < items.Num(); i++ ) {
291 // copy all keys with "inv_"
292 kv = item->MatchPrefix( "inv_" );
295 sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
296 dict.Set( key, kv->GetValue() );
297 kv = item->MatchPrefix( "inv_", kv );
302 dict.SetInt( "items", num );
305 for ( i = 0; i < 4; i++ ) {
306 dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
309 dict.SetInt( "selPDA", selPDA );
310 dict.SetInt( "selVideo", selVideo );
311 dict.SetInt( "selEmail", selEMail );
312 dict.SetInt( "selAudio", selAudio );
313 dict.SetInt( "pdaOpened", pdaOpened );
314 dict.SetInt( "turkeyScore", turkeyScore );
317 for ( i = 0; i < pdas.Num(); i++ ) {
318 sprintf( key, "pda_%i", i );
319 dict.Set( key, pdas[ i ] );
321 dict.SetInt( "pdas", pdas.Num() );
324 for ( i = 0; i < videos.Num(); i++ ) {
325 sprintf( key, "video_%i", i );
326 dict.Set( key, videos[ i ].c_str() );
328 dict.SetInt( "videos", videos.Num() );
331 for ( i = 0; i < emails.Num(); i++ ) {
332 sprintf( key, "email_%i", i );
333 dict.Set( key, emails[ i ].c_str() );
335 dict.SetInt( "emails", emails.Num() );
338 dict.SetInt( "weapon_bits", weapons );
340 dict.SetInt( "levelTriggers", levelTriggers.Num() );
341 for ( i = 0; i < levelTriggers.Num(); i++ ) {
342 sprintf( key, "levelTrigger_Level_%i", i );
343 dict.Set( key, levelTriggers[i].levelName );
344 sprintf( key, "levelTrigger_Trigger_%i", i );
345 dict.Set( key, levelTriggers[i].triggerName );
351 idInventory::RestoreInventory
354 void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
360 const idKeyValue *kv;
366 maxHealth = dict.GetInt( "maxhealth", "100" );
367 armor = dict.GetInt( "armor", "50" );
368 maxarmor = dict.GetInt( "maxarmor", "100" );
369 deplete_armor = dict.GetInt( "deplete_armor", "0" );
370 deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
371 deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
373 // the clip and powerups aren't restored
376 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
377 name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
379 ammo[ i ] = dict.GetInt( name );
384 //Restore the clip data
385 for( i = 0; i < MAX_WEAPONS; i++ ) {
386 clip[i] = dict.GetInt(va("clip%i", i), "-1");
391 num = dict.GetInt( "items" );
393 for( i = 0; i < num; i++ ) {
396 sprintf( itemname, "item_%i ", i );
397 kv = dict.MatchPrefix( itemname );
400 key.Strip( itemname );
401 item->Set( key, kv->GetValue() );
402 kv = dict.MatchPrefix( itemname, kv );
407 for ( i = 0; i < 4; i++ ) {
408 pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
411 selPDA = dict.GetInt( "selPDA" );
412 selEMail = dict.GetInt( "selEmail" );
413 selVideo = dict.GetInt( "selVideo" );
414 selAudio = dict.GetInt( "selAudio" );
415 pdaOpened = dict.GetBool( "pdaOpened" );
416 turkeyScore = dict.GetBool( "turkeyScore" );
419 num = dict.GetInt( "pdas" );
421 for ( i = 0; i < num; i++ ) {
422 sprintf( itemname, "pda_%i", i );
423 pdas[i] = dict.GetString( itemname, "default" );
427 num = dict.GetInt( "videos" );
428 videos.SetNum( num );
429 for ( i = 0; i < num; i++ ) {
430 sprintf( itemname, "video_%i", i );
431 videos[i] = dict.GetString( itemname, "default" );
435 num = dict.GetInt( "emails" );
436 emails.SetNum( num );
437 for ( i = 0; i < num; i++ ) {
438 sprintf( itemname, "email_%i", i );
439 emails[i] = dict.GetString( itemname, "default" );
442 // weapons are stored as a number for persistant data, but as strings in the entityDef
443 weapons = dict.GetInt( "weapon_bits", "0" );
446 Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
448 if ( g_skill.GetInteger() >= 3 ) {
449 Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false );
451 Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
455 num = dict.GetInt( "levelTriggers" );
456 for ( i = 0; i < num; i++ ) {
457 sprintf( itemname, "levelTrigger_Level_%i", i );
458 idLevelTriggerInfo lti;
459 lti.levelName = dict.GetString( itemname );
460 sprintf( itemname, "levelTrigger_Trigger_%i", i );
461 lti.triggerName = dict.GetString( itemname );
462 levelTriggers.Append( lti );
472 void idInventory::Save( idSaveGame *savefile ) const {
475 savefile->WriteInt( maxHealth );
476 savefile->WriteInt( weapons );
477 savefile->WriteInt( powerups );
478 savefile->WriteInt( armor );
479 savefile->WriteInt( maxarmor );
480 savefile->WriteInt( ammoPredictTime );
481 savefile->WriteInt( deplete_armor );
482 savefile->WriteFloat( deplete_rate );
483 savefile->WriteInt( deplete_ammount );
484 savefile->WriteInt( nextArmorDepleteTime );
486 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
487 savefile->WriteInt( ammo[ i ] );
489 for( i = 0; i < MAX_WEAPONS; i++ ) {
490 savefile->WriteInt( clip[ i ] );
492 for( i = 0; i < MAX_POWERUPS; i++ ) {
493 savefile->WriteInt( powerupEndTime[ i ] );
496 savefile->WriteInt( items.Num() );
497 for( i = 0; i < items.Num(); i++ ) {
498 savefile->WriteDict( items[ i ] );
501 savefile->WriteInt( pdasViewed[0] );
502 savefile->WriteInt( pdasViewed[1] );
503 savefile->WriteInt( pdasViewed[2] );
504 savefile->WriteInt( pdasViewed[3] );
506 savefile->WriteInt( selPDA );
507 savefile->WriteInt( selVideo );
508 savefile->WriteInt( selEMail );
509 savefile->WriteInt( selAudio );
510 savefile->WriteBool( pdaOpened );
511 savefile->WriteBool( turkeyScore );
513 savefile->WriteInt( pdas.Num() );
514 for( i = 0; i < pdas.Num(); i++ ) {
515 savefile->WriteString( pdas[ i ] );
518 savefile->WriteInt( pdaSecurity.Num() );
519 for( i=0; i < pdaSecurity.Num(); i++ ) {
520 savefile->WriteString( pdaSecurity[ i ] );
523 savefile->WriteInt( videos.Num() );
524 for( i = 0; i < videos.Num(); i++ ) {
525 savefile->WriteString( videos[ i ] );
528 savefile->WriteInt( emails.Num() );
529 for ( i = 0; i < emails.Num(); i++ ) {
530 savefile->WriteString( emails[ i ] );
533 savefile->WriteInt( nextItemPickup );
534 savefile->WriteInt( nextItemNum );
535 savefile->WriteInt( onePickupTime );
537 savefile->WriteInt( pickupItemNames.Num() );
538 for( i = 0; i < pickupItemNames.Num(); i++ ) {
539 savefile->WriteString( pickupItemNames[i].icon );
540 savefile->WriteString( pickupItemNames[i].name );
543 savefile->WriteInt( objectiveNames.Num() );
544 for( i = 0; i < objectiveNames.Num(); i++ ) {
545 savefile->WriteString( objectiveNames[i].screenshot );
546 savefile->WriteString( objectiveNames[i].text );
547 savefile->WriteString( objectiveNames[i].title );
550 savefile->WriteInt( levelTriggers.Num() );
551 for ( i = 0; i < levelTriggers.Num(); i++ ) {
552 savefile->WriteString( levelTriggers[i].levelName );
553 savefile->WriteString( levelTriggers[i].triggerName );
556 savefile->WriteBool( ammoPulse );
557 savefile->WriteBool( weaponPulse );
558 savefile->WriteBool( armorPulse );
560 savefile->WriteInt( lastGiveTime );
563 for(i = 0; i < AMMO_NUMTYPES; i++) {
564 savefile->WriteInt(rechargeAmmo[i].ammo);
565 savefile->WriteInt(rechargeAmmo[i].rechargeTime);
566 savefile->WriteString(rechargeAmmo[i].ammoName);
576 void idInventory::Restore( idRestoreGame *savefile ) {
579 savefile->ReadInt( maxHealth );
580 savefile->ReadInt( weapons );
581 savefile->ReadInt( powerups );
582 savefile->ReadInt( armor );
583 savefile->ReadInt( maxarmor );
584 savefile->ReadInt( ammoPredictTime );
585 savefile->ReadInt( deplete_armor );
586 savefile->ReadFloat( deplete_rate );
587 savefile->ReadInt( deplete_ammount );
588 savefile->ReadInt( nextArmorDepleteTime );
590 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
591 savefile->ReadInt( ammo[ i ] );
593 for( i = 0; i < MAX_WEAPONS; i++ ) {
594 savefile->ReadInt( clip[ i ] );
596 for( i = 0; i < MAX_POWERUPS; i++ ) {
597 savefile->ReadInt( powerupEndTime[ i ] );
600 savefile->ReadInt( num );
601 for( i = 0; i < num; i++ ) {
602 idDict *itemdict = new idDict;
604 savefile->ReadDict( itemdict );
605 items.Append( itemdict );
609 savefile->ReadInt( pdasViewed[0] );
610 savefile->ReadInt( pdasViewed[1] );
611 savefile->ReadInt( pdasViewed[2] );
612 savefile->ReadInt( pdasViewed[3] );
614 savefile->ReadInt( selPDA );
615 savefile->ReadInt( selVideo );
616 savefile->ReadInt( selEMail );
617 savefile->ReadInt( selAudio );
618 savefile->ReadBool( pdaOpened );
619 savefile->ReadBool( turkeyScore );
621 savefile->ReadInt( num );
622 for( i = 0; i < num; i++ ) {
624 savefile->ReadString( strPda );
625 pdas.Append( strPda );
628 // pda security clearances
629 savefile->ReadInt( num );
630 for ( i = 0; i < num; i++ ) {
632 savefile->ReadString( invName );
633 pdaSecurity.Append( invName );
637 savefile->ReadInt( num );
638 for( i = 0; i < num; i++ ) {
640 savefile->ReadString( strVideo );
641 videos.Append( strVideo );
645 savefile->ReadInt( num );
646 for( i = 0; i < num; i++ ) {
648 savefile->ReadString( strEmail );
649 emails.Append( strEmail );
652 savefile->ReadInt( nextItemPickup );
653 savefile->ReadInt( nextItemNum );
654 savefile->ReadInt( onePickupTime );
655 savefile->ReadInt( num );
656 for( i = 0; i < num; i++ ) {
659 savefile->ReadString( info.icon );
660 savefile->ReadString( info.name );
662 pickupItemNames.Append( info );
665 savefile->ReadInt( num );
666 for( i = 0; i < num; i++ ) {
669 savefile->ReadString( obj.screenshot );
670 savefile->ReadString( obj.text );
671 savefile->ReadString( obj.title );
673 objectiveNames.Append( obj );
676 savefile->ReadInt( num );
677 for ( i = 0; i < num; i++ ) {
678 idLevelTriggerInfo lti;
679 savefile->ReadString( lti.levelName );
680 savefile->ReadString( lti.triggerName );
681 levelTriggers.Append( lti );
684 savefile->ReadBool( ammoPulse );
685 savefile->ReadBool( weaponPulse );
686 savefile->ReadBool( armorPulse );
688 savefile->ReadInt( lastGiveTime );
691 for(i = 0; i < AMMO_NUMTYPES; i++) {
692 savefile->ReadInt(rechargeAmmo[i].ammo);
693 savefile->ReadInt(rechargeAmmo[i].rechargeTime);
696 savefile->ReadString(name);
697 strcpy(rechargeAmmo[i].ammoName, name);
704 idInventory::AmmoIndexForAmmoClass
707 ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
708 return idWeapon::GetAmmoNumForName( ammo_classname );
713 idInventory::AmmoIndexForAmmoClass
716 int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
717 return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
722 idInventory::AmmoPickupNameForIndex
725 const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
726 return idWeapon::GetAmmoPickupNameForNum( ammonum );
731 idInventory::WeaponIndexForAmmoClass
732 mapping could be prepared in the constructor
735 int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
737 const char *weapon_classname;
738 for( i = 0; i < MAX_WEAPONS; i++ ) {
739 weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
740 if ( !weapon_classname ) {
743 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
747 if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
756 idInventory::AmmoIndexForWeaponClass
759 ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
760 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
762 gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
764 if ( ammoRequired ) {
765 *ammoRequired = decl->dict.GetInt( "ammoRequired" );
767 ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
773 idInventory::AddPickupName
776 void idInventory::AddPickupName( const char *name, const char *icon, idPlayer* owner ) { //_D3XP
779 num = pickupItemNames.Num();
780 if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].name.Icmp( name ) != 0 ) ) {
781 idItemInfo &info = pickupItemNames.Alloc();
783 if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
784 info.name = common->GetLanguageDict()->GetString( name );
791 if ( gameLocal.isServer ) {
793 byte msgBuf[MAX_EVENT_PARAM_SIZE];
795 msg.Init( msgBuf, sizeof( msgBuf ) );
796 msg.WriteString( name, MAX_EVENT_PARAM_SIZE );
797 owner->ServerSendEvent( idPlayer::EVENT_PICKUPNAME, &msg, false, -1 );
808 bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud ) {
815 const idDeclEntityDef *weaponDecl;
822 if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
823 i = AmmoIndexForAmmoClass( statname );
824 max = MaxAmmoForAmmoClass( owner, statname );
828 ammo[ i ] += atoi( value );
830 //Already at or above the max so don't allow the give
831 if(ammo[ i ] >= max) {
835 //We were below the max so accept the give but cap it at the max
836 ammo[ i ] += atoi( value );
837 if(ammo[ i ] > max) {
844 if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
845 i = AmmoIndexForAmmoClass( statname );
846 max = MaxAmmoForAmmoClass( owner, statname );
847 if ( ammo[ i ] >= max ) {
850 amount = atoi( value );
853 if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
858 name = AmmoPickupNameForIndex( i );
859 if ( idStr::Length( name ) ) {
860 AddPickupName( name, "", owner ); //_D3XP
863 } else if ( !idStr::Icmp( statname, "armor" ) ) {
864 if ( armor >= maxarmor ) {
865 return false; // can't hold any more, so leave the item
867 amount = atoi( value );
870 if ( armor > maxarmor ) {
873 nextArmorDepleteTime = 0;
876 } else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
878 idStr temp = statname;
879 i = atoi(temp.Mid(7, 2));
881 i = WeaponIndexForAmmoClass( spawnArgs, statname + 7 );
884 // set, don't add. not going over the clip size limit.
886 clip[ i ] = atoi( value );
890 } else if ( !idStr::Icmp( statname, "invulnerability" ) ) {
891 owner->GivePowerUp( INVULNERABILITY, SEC2MS( atof( value ) ) );
892 } else if ( !idStr::Icmp( statname, "helltime" ) ) {
893 owner->GivePowerUp( HELLTIME, SEC2MS( atof( value ) ) );
894 } else if ( !idStr::Icmp( statname, "envirosuit" ) ) {
895 owner->GivePowerUp( ENVIROSUIT, SEC2MS( atof( value ) ) );
896 owner->GivePowerUp( ENVIROTIME, SEC2MS( atof( value ) ) );
897 } else if ( !idStr::Icmp( statname, "berserk" ) ) {
898 owner->GivePowerUp( BERSERK, SEC2MS( atof( value ) ) );
899 //} else if ( !idStr::Icmp( statname, "haste" ) ) {
900 // owner->GivePowerUp( HASTE, SEC2MS( atof( value ) ) );
902 } else if ( !idStr::Icmp( statname, "berserk" ) ) {
903 GivePowerUp( owner, BERSERK, SEC2MS( atof( value ) ) );
905 } else if ( !idStr::Icmp( statname, "mega" ) ) {
906 GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
907 } else if ( !idStr::Icmp( statname, "weapon" ) ) {
909 for( pos = value; pos != NULL; pos = end ) {
910 end = strchr( pos, ',' );
918 idStr weaponName( pos, 0, len );
920 // find the number of the matching weapon name
921 for( i = 0; i < MAX_WEAPONS; i++ ) {
922 if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
927 if ( i >= MAX_WEAPONS ) {
929 gameLocal.Warning( "Unknown weapon '%s'", weaponName.c_str() );
932 gameLocal.Error( "Unknown weapon '%s'", weaponName.c_str() );
936 // cache the media for this weapon
937 weaponDecl = gameLocal.FindEntityDef( weaponName, false );
939 // don't pickup "no ammo" weapon types twice
940 // not for D3 SP .. there is only one case in the game where you can get a no ammo
941 // weapon when you might already have it, in that case it is more conistent to pick it up
942 if ( gameLocal.isMultiplayer && weaponDecl && ( weapons & ( 1 << i ) ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
946 if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
947 if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
948 if ( owner->GetUserInfo()->GetBool( "ui_autoSwitch" ) && idealWeapon && i != owner->weapon_bloodstone_active1 && i != owner->weapon_bloodstone_active2 && i != owner->weapon_bloodstone_active3) {
949 assert( !gameLocal.isClient );
952 if ( owner->hud && updateHud && lastGiveTime + 1000 < gameLocal.time ) {
953 owner->hud->SetStateInt( "newWeapon", i );
954 owner->hud->HandleNamedEvent( "newWeapon" );
955 lastGiveTime = gameLocal.time;
958 weapons |= ( 1 << i );
964 } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
965 // ignore these as they're handled elsewhere
969 gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
981 void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
982 // remove the weapon bit
983 // also remove the ammo associated with the weapon as we pushed it in the item
984 assert( weapon_index != -1 || weapon_classname );
985 if ( weapon_index == -1 ) {
986 for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
987 if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
991 if ( weapon_index >= MAX_WEAPONS ) {
992 gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
994 } else if ( !weapon_classname ) {
995 weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
997 weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
998 ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
1000 clip[ weapon_index ] = -1;
1007 idInventory::HasAmmo
1010 int idInventory::HasAmmo( ammo_t type, int amount ) {
1011 if ( ( type == 0 ) || !amount ) {
1012 // always allow weapons that don't use ammo to fire
1016 // check if we have infinite ammo
1017 if ( ammo[ type ] < 0 ) {
1021 // return how many shots we can fire
1022 return ammo[ type ] / amount;
1028 idInventory::HasAmmo
1031 int idInventory::HasAmmo( const char *weapon_classname, bool includeClip, idPlayer* owner ) { //_D3XP
1033 ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
1036 int ammoCount = HasAmmo( ammo_i, ammoRequired );
1037 if(includeClip && owner) {
1038 ammoCount += clip[owner->SlotForWeapon(weapon_classname)];
1042 return HasAmmo( ammo_i, ammoRequired );
1050 idInventory::HasEmptyClipCannotRefill
1053 bool idInventory::HasEmptyClipCannotRefill(const char *weapon_classname, idPlayer* owner) {
1055 int clipSize = clip[owner->SlotForWeapon(weapon_classname)];
1060 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
1062 gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
1064 int minclip = decl->dict.GetInt("minclipsize");
1069 ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
1070 int ammoRequired = decl->dict.GetInt( "ammoRequired" );
1071 int ammoCount = HasAmmo( ammo_i, ammoRequired );
1072 if(ammoCount < minclip) {
1081 idInventory::UseAmmo
1084 bool idInventory::UseAmmo( ammo_t type, int amount ) {
1085 if ( !HasAmmo( type, amount ) ) {
1089 // take an ammo away if not infinite
1090 if ( ammo[ type ] >= 0 ) {
1091 ammo[ type ] -= amount;
1092 ammoPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots
1100 idInventory::UpdateArmor
1103 void idInventory::UpdateArmor( void ) {
1104 if ( deplete_armor != 0.0f && deplete_armor < armor ) {
1105 if ( !nextArmorDepleteTime ) {
1106 nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
1107 } else if ( gameLocal.time > nextArmorDepleteTime ) {
1108 armor -= deplete_ammount;
1109 if ( armor < deplete_armor ) {
1110 armor = deplete_armor;
1112 nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
1120 idInventory::InitRechargeAmmo
1122 * Loads any recharge ammo definitions from the ammo_types entity definitions.
1124 void idInventory::InitRechargeAmmo(idPlayer *owner) {
1126 memset (rechargeAmmo, 0, sizeof(rechargeAmmo));
1128 const idKeyValue *kv = owner->spawnArgs.MatchPrefix( "ammorecharge_" );
1130 idStr key = kv->GetKey();
1131 idStr ammoname = key.Right(key.Length()- strlen("ammorecharge_"));
1132 int ammoType = AmmoIndexForAmmoClass(ammoname);
1133 rechargeAmmo[ammoType].ammo = (atof(kv->GetValue().c_str())*1000);
1134 strcpy(rechargeAmmo[ammoType].ammoName, ammoname);
1135 kv = owner->spawnArgs.MatchPrefix( "ammorecharge_", kv );
1141 idInventory::RechargeAmmo
1143 * Called once per frame to update any ammo amount for ammo types that recharge.
1145 void idInventory::RechargeAmmo(idPlayer *owner) {
1147 for(int i = 0; i < AMMO_NUMTYPES; i++) {
1148 if(rechargeAmmo[i].ammo > 0) {
1149 if(!rechargeAmmo[i].rechargeTime) {
1150 //Initialize the recharge timer.
1151 rechargeAmmo[i].rechargeTime = gameLocal.time;
1153 int elapsed = gameLocal.time - rechargeAmmo[i].rechargeTime;
1154 if(elapsed >= rechargeAmmo[i].ammo) {
1155 int intervals = (gameLocal.time - rechargeAmmo[i].rechargeTime)/rechargeAmmo[i].ammo;
1156 ammo[i] += intervals;
1158 int max = MaxAmmoForAmmoClass(owner, rechargeAmmo[i].ammoName);
1164 rechargeAmmo[i].rechargeTime += intervals*rechargeAmmo[i].ammo;
1172 idInventory::CanGive
1175 bool idInventory::CanGive( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon ) {
1177 if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
1178 int max = MaxAmmoForAmmoClass(owner, statname);
1179 int i = AmmoIndexForAmmoClass(statname);
1185 //Already at or above the max so don't allow the give
1186 if(ammo[ i ] >= max) {
1192 } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
1193 // ignore these as they're handled elsewhere
1194 //These items should not be considered as succesful gives because it messes up the max ammo items
1206 idPlayer::idPlayer() {
1207 memset( &usercmd, 0, sizeof( usercmd ) );
1212 spawnAnglesSet = false;
1213 spawnAngles = ang_zero;
1214 viewAngles = ang_zero;
1215 cmdAngles = ang_zero;
1223 lastSavingThrowTime = 0;
1228 objectiveSystem = NULL;
1229 objectiveSystemOpen = false;
1232 mountedObject = NULL;
1233 enviroSuitLight = NULL;
1236 heartRate = BASE_HEARTRATE;
1237 heartInfo.Init( 0, 0, 0, 0 );
1238 lastHeartAdjust = 0;
1241 deathClearContentsTime = 0;
1242 lastArmorPulse = -10000;
1245 nextHealthPulse = 0;
1246 healthPulse = false;
1250 scoreBoardOpen = false;
1251 forceScoreBoard = false;
1252 forceRespawn = false;
1255 colorBar = vec3_zero;
1257 forcedReady = false;
1258 wantSpectate = false;
1261 carryingFlag = false;
1264 lastHitToggle = false;
1269 firstPersonViewOrigin = vec3_zero;
1270 firstPersonViewAxis = mat3_identity;
1272 hipJoint = INVALID_JOINT;
1273 chestJoint = INVALID_JOINT;
1274 headJoint = INVALID_JOINT;
1283 idealLegsYaw = 0.0f;
1287 viewBobAngles = ang_zero;
1288 viewBob = vec3_zero;
1294 previousWeapon = -1;
1295 weaponSwitchTime = 0;
1296 weaponEnabled = true;
1297 weapon_soulcube = -1;
1301 weapon_bloodstone = -1;
1302 weapon_bloodstone_active1 = -1;
1303 weapon_bloodstone_active2 = -1;
1304 weapon_bloodstone_active3 = -1;
1305 harvest_lock = false;
1308 lastHudPowerup = -1;
1309 hudPowerupDuration = 0;
1311 showWeaponViewModel = true;
1317 numProjectilesFired = 0;
1318 numProjectileHits = 0;
1325 gibsLaunched = false;
1326 gibsDir = vec3_zero;
1328 zoomFov.Init( 0, 0, 0, 0 );
1329 centerView.Init( 0, 0, 0, 0 );
1333 influenceActive = 0;
1334 influenceRadius = 0.0f;
1335 influenceEntity = NULL;
1336 influenceMaterial = NULL;
1337 influenceSkin = NULL;
1339 privateCameraView = NULL;
1341 memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
1342 memset( loggedAccel, 0, sizeof( loggedAccel ) );
1343 currentLoggedAccel = 0;
1348 focusCharacter = NULL;
1350 focusVehicle = NULL;
1361 lastDamageDir = vec3_zero;
1362 lastDamageLocation = 0;
1364 smoothedOriginUpdated = false;
1365 smoothedOrigin = vec3_zero;
1366 smoothedAngles = ang_zero;
1368 fl.networkSync = true;
1371 doingDeathSkin = false;
1373 useInitialSpawns = false;
1375 lastSpectateTeleport = 0;
1377 hiddenWeapon = false;
1379 objectiveUp = false;
1380 teleportEntity = NULL;
1381 teleportKiller = -1;
1385 lastSpectateChange = 0;
1387 weaponCatchup = false;
1388 lastSnapshotSequence = 0;
1394 MPAimHighlight = false;
1397 lastManOver = false;
1398 lastManPlayAgain = false;
1399 lastManPresent = false;
1401 isTelefragged = false;
1411 idPlayer::LinkScriptVariables
1413 set up conditions for animation
1416 void idPlayer::LinkScriptVariables( void ) {
1417 AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
1418 AI_BACKWARD.LinkTo( scriptObject, "AI_BACKWARD" );
1419 AI_STRAFE_LEFT.LinkTo( scriptObject, "AI_STRAFE_LEFT" );
1420 AI_STRAFE_RIGHT.LinkTo( scriptObject, "AI_STRAFE_RIGHT" );
1421 AI_ATTACK_HELD.LinkTo( scriptObject, "AI_ATTACK_HELD" );
1422 AI_WEAPON_FIRED.LinkTo( scriptObject, "AI_WEAPON_FIRED" );
1423 AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
1424 AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
1425 AI_CROUCH.LinkTo( scriptObject, "AI_CROUCH" );
1426 AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
1427 AI_ONLADDER.LinkTo( scriptObject, "AI_ONLADDER" );
1428 AI_HARDLANDING.LinkTo( scriptObject, "AI_HARDLANDING" );
1429 AI_SOFTLANDING.LinkTo( scriptObject, "AI_SOFTLANDING" );
1430 AI_RUN.LinkTo( scriptObject, "AI_RUN" );
1431 AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
1432 AI_RELOAD.LinkTo( scriptObject, "AI_RELOAD" );
1433 AI_TELEPORT.LinkTo( scriptObject, "AI_TELEPORT" );
1434 AI_TURN_LEFT.LinkTo( scriptObject, "AI_TURN_LEFT" );
1435 AI_TURN_RIGHT.LinkTo( scriptObject, "AI_TURN_RIGHT" );
1440 idPlayer::SetupWeaponEntity
1443 void idPlayer::SetupWeaponEntity( void ) {
1447 if ( weapon.GetEntity() ) {
1448 // get rid of old weapon
1449 weapon.GetEntity()->Clear();
1451 } else if ( !gameLocal.isClient ) {
1452 weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
1453 weapon.GetEntity()->SetOwner( this );
1457 for( w = 0; w < MAX_WEAPONS; w++ ) {
1458 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
1459 if ( weap && *weap ) {
1460 idWeapon::CacheWeapon( weap );
1470 void idPlayer::Init( void ) {
1472 const idKeyValue *kv;
1482 previousWeapon = -1;
1483 weaponSwitchTime = 0;
1484 weaponEnabled = true;
1485 weapon_soulcube = SlotForWeapon( "weapon_soulcube" );
1486 weapon_pda = SlotForWeapon( "weapon_pda" );
1487 weapon_fists = SlotForWeapon( "weapon_fists" );
1489 weapon_bloodstone = SlotForWeapon( "weapon_bloodstone_passive" );
1490 weapon_bloodstone_active1 = SlotForWeapon( "weapon_bloodstone_active1" );
1491 weapon_bloodstone_active2 = SlotForWeapon( "weapon_bloodstone_active2" );
1492 weapon_bloodstone_active3 = SlotForWeapon( "weapon_bloodstone_active3" );
1493 harvest_lock = false;
1495 showWeaponViewModel = GetUserInfo()->GetBool( "ui_showGun" );
1499 lastArmorPulse = -10000;
1500 lastHeartAdjust = 0;
1502 heartInfo.Init( 0, 0, 0, 0 );
1508 zoomFov.Init( 0, 0, 0, 0 );
1509 centerView.Init( 0, 0, 0, 0 );
1513 influenceActive = 0;
1514 influenceRadius = 0.0f;
1515 influenceEntity = NULL;
1516 influenceMaterial = NULL;
1517 influenceSkin = NULL;
1520 mountedObject = NULL;
1521 if( enviroSuitLight.IsValid() ) {
1522 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
1524 enviroSuitLight = NULL;
1525 healthRecharge = false;
1526 lastHealthRechargeTime = 0;
1527 rechargeSpeed = 500;
1528 new_g_damageScale = 1.f;
1529 bloomEnabled = false;
1531 bloomIntensity = -0.01f;
1532 inventory.InitRechargeAmmo(this);
1534 lastHudPowerup = -1;
1535 hudPowerupDuration = 0;
1538 currentLoggedAccel = 0;
1543 focusCharacter = NULL;
1545 focusVehicle = NULL;
1547 // remove any damage effects
1548 playerView.ClearEffects();
1551 fl.takedamage = true;
1554 // restore persistent data
1555 RestorePersistantInfo();
1560 nextHealthPulse = 0;
1561 healthPulse = false;
1565 SetupWeaponEntity();
1567 previousWeapon = -1;
1569 heartRate = BASE_HEARTRATE;
1570 AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
1572 idealLegsYaw = 0.0f;
1577 // set the pm_ cvars
1578 if ( !gameLocal.isMultiplayer || gameLocal.isServer ) {
1579 kv = spawnArgs.MatchPrefix( "pm_", NULL );
1581 cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
1582 kv = spawnArgs.MatchPrefix( "pm_", kv );
1586 // disable stamina on hell levels
1587 if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
1588 pm_stamina.SetFloat( 0.0f );
1591 // stamina always initialized to maximum
1592 stamina = pm_stamina.GetFloat();
1594 // air always initialized to maximum too
1595 airTics = pm_airTics.GetFloat();
1599 gibsLaunched = false;
1603 physicsObj.SetGravity( gameLocal.GetGravity() );
1605 // start out standing
1606 SetEyeHeight( pm_normalviewheight.GetFloat() );
1610 viewBobAngles.Zero();
1613 value = spawnArgs.GetString( "model" );
1614 if ( value && ( *value != 0 ) ) {
1619 cursor->SetStateInt( "talkcursor", 0 );
1620 cursor->SetStateString( "combatcursor", "1" );
1621 cursor->SetStateString( "itemcursor", "0" );
1622 cursor->SetStateString( "guicursor", "0" );
1624 cursor->SetStateString( "grabbercursor", "0" );
1628 if ( ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) && skin ) {
1630 renderEntity.shaderParms[6] = 0.0f;
1631 } else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
1632 skin = declManager->FindSkin( value );
1634 renderEntity.shaderParms[6] = 0.0f;
1637 value = spawnArgs.GetString( "bone_hips", "" );
1638 hipJoint = animator.GetJointHandle( value );
1639 if ( hipJoint == INVALID_JOINT ) {
1640 gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
1643 value = spawnArgs.GetString( "bone_chest", "" );
1644 chestJoint = animator.GetJointHandle( value );
1645 if ( chestJoint == INVALID_JOINT ) {
1646 gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
1649 value = spawnArgs.GetString( "bone_head", "" );
1650 headJoint = animator.GetJointHandle( value );
1651 if ( headJoint == INVALID_JOINT ) {
1652 gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
1655 // initialize the script variables
1657 AI_BACKWARD = false;
1658 AI_STRAFE_LEFT = false;
1659 AI_STRAFE_RIGHT = false;
1660 AI_ATTACK_HELD = false;
1661 AI_WEAPON_FIRED = false;
1666 AI_ONLADDER = false;
1667 AI_HARDLANDING = false;
1668 AI_SOFTLANDING = false;
1672 AI_TELEPORT = false;
1673 AI_TURN_LEFT = false;
1674 AI_TURN_RIGHT = false;
1676 // reset the script object
1677 ConstructScriptObject();
1679 // execute the script so the script object's constructor takes effect immediately
1680 scriptThread->Execute();
1682 forceScoreBoard = false;
1683 forcedReady = false;
1685 privateCameraView = NULL;
1687 lastSpectateChange = 0;
1690 hiddenWeapon = false;
1692 objectiveUp = false;
1693 teleportEntity = NULL;
1694 teleportKiller = -1;
1697 SetPrivateCameraView( NULL );
1699 lastSnapshotSequence = 0;
1705 MPAimHighlight = false;
1708 hud->HandleNamedEvent( "aim_clear" );
1711 //isChatting = false;
1712 cvarSystem->SetCVarBool("ui_chat", false);
1719 Prepare any resources used by the player.
1722 void idPlayer::Spawn( void ) {
1726 if ( entityNumber >= MAX_CLIENTS ) {
1727 gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
1730 // allow thinking during cinematics
1733 if ( gameLocal.isMultiplayer ) {
1734 // always start in spectating state waiting to be spawned in
1735 // do this before SetClipModel to get the right bounding box
1739 // set our collision model
1740 physicsObj.SetSelf( this );
1742 physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
1743 physicsObj.SetContents( CONTENTS_BODY );
1744 physicsObj.SetClipMask( MASK_PLAYERSOLID );
1745 SetPhysics( &physicsObj );
1748 skin = renderEntity.customSkin;
1750 // only the local player needs guis
1751 if ( !gameLocal.isMultiplayer || entityNumber == gameLocal.localClientNum ) {
1754 if ( gameLocal.isMultiplayer ) {
1755 hud = uiManager->FindGui( "guis/mphud.gui", true, false, true );
1756 } else if ( spawnArgs.GetString( "hud", "", temp ) ) {
1757 hud = uiManager->FindGui( temp, true, false, true );
1760 hud->Activate( true, gameLocal.time );
1762 if ( gameLocal.mpGame.IsGametypeFlagBased() ) {
1763 hud->SetStateInt( "red_team_score", gameLocal.mpGame.GetFlagPoints(0) );
1764 hud->SetStateInt( "blue_team_score", gameLocal.mpGame.GetFlagPoints(1) );
1770 if ( spawnArgs.GetString( "cursor", "", temp ) ) {
1771 cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer );
1774 cursor->Activate( true, gameLocal.time );
1777 objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true );
1778 objectiveSystemOpen = false;
1781 SetLastHitTime( 0 );
1783 // load the armor sound feedback
1784 declManager->FindSound( "player_sounds_hitArmor" );
1786 // set up conditions for animation
1787 LinkScriptVariables();
1789 animator.RemoveOriginOffset( true );
1791 // initialize user info related settings
1792 // on server, we wait for the userinfo broadcast, as this controls when the player is initially spawned in game
1793 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
1794 UserInfoChanged( false );
1797 // create combat collision hull for exact collision detection
1800 // init the damage effects
1801 playerView.SetPlayerEntity( this );
1803 // supress model in non-player views, but allow it in mirrors and remote views
1804 renderEntity.suppressSurfaceInViewID = entityNumber+1;
1806 // don't project shadow on self or weapon
1807 renderEntity.noSelfShadow = true;
1809 idAFAttachment *headEnt = head.GetEntity();
1811 headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
1812 headEnt->GetRenderEntity()->noSelfShadow = true;
1815 if ( gameLocal.isMultiplayer ) {
1817 Hide(); // properly hidden if starting as a spectator
1818 if ( !gameLocal.isClient ) {
1819 // set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
1820 SetupWeaponEntity();
1821 SpawnFromSpawnSpot();
1822 forceRespawn = true;
1823 assert( spectating );
1826 SetupWeaponEntity();
1827 SpawnFromSpawnSpot();
1830 // trigger playtesting item gives, if we didn't get here from a previous level
1831 // the devmap key will be set on the first devmap, but cleared on any level
1833 if ( !gameLocal.isMultiplayer && gameLocal.serverInfo.FindKey( "devmap" ) ) {
1834 // fire a trigger with the name "devmap"
1835 idEntity *ent = gameLocal.FindEntity( "devmap" );
1837 ent->ActivateTargets( this );
1841 // We can spawn with a full soul cube, so we need to make sure the hud knows this
1843 if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
1844 int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
1845 if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_souls" ) ] >= max_souls ) {
1846 hud->HandleNamedEvent( "soulCubeReady" );
1851 //We can spawn with a full bloodstone, so make sure the hud knows
1852 if ( weapon_bloodstone > 0 && ( inventory.weapons & ( 1 << weapon_bloodstone ) ) ) {
1853 //int max_blood = inventory.MaxAmmoForAmmoClass( this, "ammo_bloodstone" );
1854 //if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_bloodstone" ) ] >= max_blood ) {
1855 hud->HandleNamedEvent( "bloodstoneReady" );
1859 hud->HandleNamedEvent( "itemPickup" );
1863 // Add any emails from the inventory
1864 for ( int i = 0; i < inventory.emails.Num(); i++ ) {
1865 GetPDA()->AddEmail( inventory.emails[i] );
1867 GetPDA()->SetSecurity( common->GetLanguageDict()->GetString( "#str_00066" ) );
1870 if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
1871 hiddenWeapon = true;
1872 if ( weapon.GetEntity() ) {
1873 weapon.GetEntity()->LowerWeapon();
1877 hiddenWeapon = false;
1882 hud->StateChanged( gameLocal.time );
1886 objectiveUp = false;
1888 if ( inventory.levelTriggers.Num() ) {
1889 PostEventMS( &EV_Player_LevelTrigger, 0 );
1892 inventory.pdaOpened = false;
1893 inventory.selPDA = 0;
1895 if ( !gameLocal.isMultiplayer ) {
1896 if ( g_skill.GetInteger() < 2 ) {
1897 if ( health < 25 ) {
1900 if ( g_useDynamicProtection.GetBool() ) {
1902 new_g_damageScale = 1.0f;
1904 g_damageScale.SetFloat( 1.0f );
1909 new_g_damageScale = 1.0f;
1911 g_damageScale.SetFloat( 1.0f );
1913 g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
1914 #ifndef ID_DEMO_BUILD
1915 if ( g_skill.GetInteger() == 3 ) {
1917 nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
1924 //Setup the weapon toggle lists
1925 const idKeyValue *kv;
1926 kv = spawnArgs.MatchPrefix( "weapontoggle", NULL );
1928 WeaponToggle_t newToggle;
1929 strcpy(newToggle.name, kv->GetKey().c_str());
1931 idStr toggleData = kv->GetValue();
1935 src.LoadMemory(toggleData, toggleData.Length(), "toggleData");
1937 if(!src.ReadToken(&token)) {
1940 int index = atoi(token.c_str());
1941 newToggle.toggleList.Append(index);
1944 src.ReadToken(&token);
1946 weaponToggles.Set(newToggle.name, newToggle);
1948 kv = spawnArgs.MatchPrefix( "weapontoggle", kv );
1953 if(g_skill.GetInteger() >= 3) {
1954 if(!WeaponAvailable("weapon_bloodstone_passive")) {
1955 GiveInventoryItem("weapon_bloodstone_passive");
1957 if(!WeaponAvailable("weapon_bloodstone_active1")) {
1958 GiveInventoryItem("weapon_bloodstone_active1");
1960 if(!WeaponAvailable("weapon_bloodstone_active2")) {
1961 GiveInventoryItem("weapon_bloodstone_active2");
1963 if(!WeaponAvailable("weapon_bloodstone_active3")) {
1964 GiveInventoryItem("weapon_bloodstone_active3");
1968 bloomEnabled = false;
1970 bloomIntensity = -0.01f;
1976 idPlayer::~idPlayer()
1978 Release any resources used by the player.
1981 idPlayer::~idPlayer() {
1982 delete weapon.GetEntity();
1985 if ( enviroSuitLight.IsValid() ) {
1986 enviroSuitLight.GetEntity()->ProcessEvent( &EV_Remove );
1988 // have to do this here, idMultiplayerGame::DisconnectClient() is too late
1989 if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() ) {
2000 void idPlayer::Save( idSaveGame *savefile ) const {
2003 savefile->WriteUsercmd( usercmd );
2004 playerView.Save( savefile );
2006 savefile->WriteBool( noclip );
2007 savefile->WriteBool( godmode );
2009 // don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
2010 savefile->WriteAngles( spawnAngles );
2011 savefile->WriteAngles( viewAngles );
2012 savefile->WriteAngles( cmdAngles );
2014 savefile->WriteInt( buttonMask );
2015 savefile->WriteInt( oldButtons );
2016 savefile->WriteInt( oldFlags );
2018 savefile->WriteInt( lastHitTime );
2019 savefile->WriteInt( lastSndHitTime );
2020 savefile->WriteInt( lastSavingThrowTime );
2022 // idBoolFields don't need to be saved, just re-linked in Restore
2024 inventory.Save( savefile );
2025 weapon.Save( savefile );
2027 savefile->WriteUserInterface( hud, false );
2028 savefile->WriteUserInterface( objectiveSystem, false );
2029 savefile->WriteBool( objectiveSystemOpen );
2031 savefile->WriteInt( weapon_soulcube );
2032 savefile->WriteInt( weapon_pda );
2033 savefile->WriteInt( weapon_fists );
2035 savefile->WriteInt( weapon_bloodstone );
2036 savefile->WriteInt( weapon_bloodstone_active1 );
2037 savefile->WriteInt( weapon_bloodstone_active2 );
2038 savefile->WriteInt( weapon_bloodstone_active3 );
2039 savefile->WriteBool( harvest_lock );
2040 savefile->WriteInt( hudPowerup );
2041 savefile->WriteInt( lastHudPowerup );
2042 savefile->WriteInt( hudPowerupDuration );
2047 savefile->WriteInt( heartRate );
2049 savefile->WriteFloat( heartInfo.GetStartTime() );
2050 savefile->WriteFloat( heartInfo.GetDuration() );
2051 savefile->WriteFloat( heartInfo.GetStartValue() );
2052 savefile->WriteFloat( heartInfo.GetEndValue() );
2054 savefile->WriteInt( lastHeartAdjust );
2055 savefile->WriteInt( lastHeartBeat );
2056 savefile->WriteInt( lastDmgTime );
2057 savefile->WriteInt( deathClearContentsTime );
2058 savefile->WriteBool( doingDeathSkin );
2059 savefile->WriteInt( lastArmorPulse );
2060 savefile->WriteFloat( stamina );
2061 savefile->WriteFloat( healthPool );
2062 savefile->WriteInt( nextHealthPulse );
2063 savefile->WriteBool( healthPulse );
2064 savefile->WriteInt( nextHealthTake );
2065 savefile->WriteBool( healthTake );
2067 savefile->WriteBool( hiddenWeapon );
2068 soulCubeProjectile.Save( savefile );
2070 savefile->WriteInt( spectator );
2071 savefile->WriteVec3( colorBar );
2072 savefile->WriteInt( colorBarIndex );
2073 savefile->WriteBool( scoreBoardOpen );
2074 savefile->WriteBool( forceScoreBoard );
2075 savefile->WriteBool( forceRespawn );
2076 savefile->WriteBool( spectating );
2077 savefile->WriteInt( lastSpectateTeleport );
2078 savefile->WriteBool( lastHitToggle );
2079 savefile->WriteBool( forcedReady );
2080 savefile->WriteBool( wantSpectate );
2081 savefile->WriteBool( weaponGone );
2082 savefile->WriteBool( useInitialSpawns );
2083 savefile->WriteInt( latchedTeam );
2084 savefile->WriteInt( tourneyRank );
2085 savefile->WriteInt( tourneyLine );
2087 teleportEntity.Save( savefile );
2088 savefile->WriteInt( teleportKiller );
2090 savefile->WriteInt( minRespawnTime );
2091 savefile->WriteInt( maxRespawnTime );
2093 savefile->WriteVec3( firstPersonViewOrigin );
2094 savefile->WriteMat3( firstPersonViewAxis );
2096 // don't bother saving dragEntity since it's a dev tool
2098 savefile->WriteJoint( hipJoint );
2099 savefile->WriteJoint( chestJoint );
2100 savefile->WriteJoint( headJoint );
2102 savefile->WriteStaticObject( physicsObj );
2104 savefile->WriteInt( aasLocation.Num() );
2105 for( i = 0; i < aasLocation.Num(); i++ ) {
2106 savefile->WriteInt( aasLocation[ i ].areaNum );
2107 savefile->WriteVec3( aasLocation[ i ].pos );
2110 savefile->WriteInt( bobFoot );
2111 savefile->WriteFloat( bobFrac );
2112 savefile->WriteFloat( bobfracsin );
2113 savefile->WriteInt( bobCycle );
2114 savefile->WriteFloat( xyspeed );
2115 savefile->WriteInt( stepUpTime );
2116 savefile->WriteFloat( stepUpDelta );
2117 savefile->WriteFloat( idealLegsYaw );
2118 savefile->WriteFloat( legsYaw );
2119 savefile->WriteBool( legsForward );
2120 savefile->WriteFloat( oldViewYaw );
2121 savefile->WriteAngles( viewBobAngles );
2122 savefile->WriteVec3( viewBob );
2123 savefile->WriteInt( landChange );
2124 savefile->WriteInt( landTime );
2126 savefile->WriteInt( currentWeapon );
2127 savefile->WriteInt( idealWeapon );
2128 savefile->WriteInt( previousWeapon );
2129 savefile->WriteInt( weaponSwitchTime );
2130 savefile->WriteBool( weaponEnabled );
2131 savefile->WriteBool( showWeaponViewModel );
2133 savefile->WriteSkin( skin );
2134 savefile->WriteSkin( powerUpSkin );
2135 savefile->WriteString( baseSkinName );
2137 savefile->WriteInt( numProjectilesFired );
2138 savefile->WriteInt( numProjectileHits );
2140 savefile->WriteBool( airless );
2141 savefile->WriteInt( airTics );
2142 savefile->WriteInt( lastAirDamage );
2144 savefile->WriteBool( gibDeath );
2145 savefile->WriteBool( gibsLaunched );
2146 savefile->WriteVec3( gibsDir );
2148 savefile->WriteFloat( zoomFov.GetStartTime() );
2149 savefile->WriteFloat( zoomFov.GetDuration() );
2150 savefile->WriteFloat( zoomFov.GetStartValue() );
2151 savefile->WriteFloat( zoomFov.GetEndValue() );
2153 savefile->WriteFloat( centerView.GetStartTime() );
2154 savefile->WriteFloat( centerView.GetDuration() );
2155 savefile->WriteFloat( centerView.GetStartValue() );
2156 savefile->WriteFloat( centerView.GetEndValue() );
2158 savefile->WriteBool( fxFov );
2160 savefile->WriteFloat( influenceFov );
2161 savefile->WriteInt( influenceActive );
2162 savefile->WriteFloat( influenceRadius );
2163 savefile->WriteObject( influenceEntity );
2164 savefile->WriteMaterial( influenceMaterial );
2165 savefile->WriteSkin( influenceSkin );
2167 savefile->WriteObject( privateCameraView );
2169 for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
2170 savefile->WriteAngles( loggedViewAngles[ i ] );
2172 for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
2173 savefile->WriteInt( loggedAccel[ i ].time );
2174 savefile->WriteVec3( loggedAccel[ i ].dir );
2176 savefile->WriteInt( currentLoggedAccel );
2178 savefile->WriteObject( focusGUIent );
2179 // can't save focusUI
2180 savefile->WriteObject( focusCharacter );
2181 savefile->WriteInt( talkCursor );
2182 savefile->WriteInt( focusTime );
2183 savefile->WriteObject( focusVehicle );
2184 savefile->WriteUserInterface( cursor, false );
2186 savefile->WriteInt( oldMouseX );
2187 savefile->WriteInt( oldMouseY );
2189 savefile->WriteString( pdaAudio );
2190 savefile->WriteString( pdaVideo );
2191 savefile->WriteString( pdaVideoWave );
2193 savefile->WriteBool( tipUp );
2194 savefile->WriteBool( objectiveUp );
2196 savefile->WriteInt( lastDamageDef );
2197 savefile->WriteVec3( lastDamageDir );
2198 savefile->WriteInt( lastDamageLocation );
2199 savefile->WriteInt( smoothedFrame );
2200 savefile->WriteBool( smoothedOriginUpdated );
2201 savefile->WriteVec3( smoothedOrigin );
2202 savefile->WriteAngles( smoothedAngles );
2204 savefile->WriteBool( ready );
2205 savefile->WriteBool( respawning );
2206 savefile->WriteBool( leader );
2207 savefile->WriteInt( lastSpectateChange );
2208 savefile->WriteInt( lastTeleFX );
2210 savefile->WriteFloat( pm_stamina.GetFloat() );
2213 hud->SetStateString( "message", common->GetLanguageDict()->GetString( "#str_02916" ) );
2214 hud->HandleNamedEvent( "Message" );
2218 savefile->WriteInt(weaponToggles.Num());
2219 for(i = 0; i < weaponToggles.Num(); i++) {
2220 WeaponToggle_t* weaponToggle = weaponToggles.GetIndex(i);
2221 savefile->WriteString(weaponToggle->name);
2222 savefile->WriteInt(weaponToggle->toggleList.Num());
2223 for(int j = 0; j < weaponToggle->toggleList.Num(); j++) {
2224 savefile->WriteInt(weaponToggle->toggleList[j]);
2227 savefile->WriteObject( mountedObject );
2228 enviroSuitLight.Save( savefile );
2229 savefile->WriteBool( healthRecharge );
2230 savefile->WriteInt( lastHealthRechargeTime );
2231 savefile->WriteInt( rechargeSpeed );
2232 savefile->WriteFloat( new_g_damageScale );
2234 savefile->WriteBool( bloomEnabled );
2235 savefile->WriteFloat( bloomSpeed );
2236 savefile->WriteFloat( bloomIntensity );
2246 void idPlayer::Restore( idRestoreGame *savefile ) {
2251 savefile->ReadUsercmd( usercmd );
2252 playerView.Restore( savefile );
2254 savefile->ReadBool( noclip );
2255 savefile->ReadBool( godmode );
2257 savefile->ReadAngles( spawnAngles );
2258 savefile->ReadAngles( viewAngles );
2259 savefile->ReadAngles( cmdAngles );
2261 memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
2262 SetViewAngles( viewAngles );
2263 spawnAnglesSet = true;
2265 savefile->ReadInt( buttonMask );
2266 savefile->ReadInt( oldButtons );
2267 savefile->ReadInt( oldFlags );
2272 savefile->ReadInt( lastHitTime );
2273 savefile->ReadInt( lastSndHitTime );
2274 savefile->ReadInt( lastSavingThrowTime );
2276 // Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
2277 LinkScriptVariables();
2279 inventory.Restore( savefile );
2280 weapon.Restore( savefile );
2282 for ( i = 0; i < inventory.emails.Num(); i++ ) {
2283 GetPDA()->AddEmail( inventory.emails[i] );
2286 savefile->ReadUserInterface( hud );
2287 savefile->ReadUserInterface( objectiveSystem );
2288 savefile->ReadBool( objectiveSystemOpen );
2290 savefile->ReadInt( weapon_soulcube );
2291 savefile->ReadInt( weapon_pda );
2292 savefile->ReadInt( weapon_fists );
2294 savefile->ReadInt( weapon_bloodstone );
2295 savefile->ReadInt( weapon_bloodstone_active1 );
2296 savefile->ReadInt( weapon_bloodstone_active2 );
2297 savefile->ReadInt( weapon_bloodstone_active3 );
2299 savefile->ReadBool( harvest_lock );
2300 savefile->ReadInt( hudPowerup );
2301 savefile->ReadInt( lastHudPowerup );
2302 savefile->ReadInt( hudPowerupDuration );
2307 savefile->ReadInt( heartRate );
2309 savefile->ReadFloat( set );
2310 heartInfo.SetStartTime( set );
2311 savefile->ReadFloat( set );
2312 heartInfo.SetDuration( set );
2313 savefile->ReadFloat( set );
2314 heartInfo.SetStartValue( set );
2315 savefile->ReadFloat( set );
2316 heartInfo.SetEndValue( set );
2318 savefile->ReadInt( lastHeartAdjust );
2319 savefile->ReadInt( lastHeartBeat );
2320 savefile->ReadInt( lastDmgTime );
2321 savefile->ReadInt( deathClearContentsTime );
2322 savefile->ReadBool( doingDeathSkin );
2323 savefile->ReadInt( lastArmorPulse );
2324 savefile->ReadFloat( stamina );
2325 savefile->ReadFloat( healthPool );
2326 savefile->ReadInt( nextHealthPulse );
2327 savefile->ReadBool( healthPulse );
2328 savefile->ReadInt( nextHealthTake );
2329 savefile->ReadBool( healthTake );
2331 savefile->ReadBool( hiddenWeapon );
2332 soulCubeProjectile.Restore( savefile );
2334 savefile->ReadInt( spectator );
2335 savefile->ReadVec3( colorBar );
2336 savefile->ReadInt( colorBarIndex );
2337 savefile->ReadBool( scoreBoardOpen );
2338 savefile->ReadBool( forceScoreBoard );
2339 savefile->ReadBool( forceRespawn );
2340 savefile->ReadBool( spectating );
2341 savefile->ReadInt( lastSpectateTeleport );
2342 savefile->ReadBool( lastHitToggle );
2343 savefile->ReadBool( forcedReady );
2344 savefile->ReadBool( wantSpectate );
2345 savefile->ReadBool( weaponGone );
2346 savefile->ReadBool( useInitialSpawns );
2347 savefile->ReadInt( latchedTeam );
2348 savefile->ReadInt( tourneyRank );
2349 savefile->ReadInt( tourneyLine );
2351 teleportEntity.Restore( savefile );
2352 savefile->ReadInt( teleportKiller );
2354 savefile->ReadInt( minRespawnTime );
2355 savefile->ReadInt( maxRespawnTime );
2357 savefile->ReadVec3( firstPersonViewOrigin );
2358 savefile->ReadMat3( firstPersonViewAxis );
2360 // don't bother saving dragEntity since it's a dev tool
2363 savefile->ReadJoint( hipJoint );
2364 savefile->ReadJoint( chestJoint );
2365 savefile->ReadJoint( headJoint );
2367 savefile->ReadStaticObject( physicsObj );
2368 RestorePhysics( &physicsObj );
2370 savefile->ReadInt( num );
2371 aasLocation.SetGranularity( 1 );
2372 aasLocation.SetNum( num );
2373 for( i = 0; i < num; i++ ) {
2374 savefile->ReadInt( aasLocation[ i ].areaNum );
2375 savefile->ReadVec3( aasLocation[ i ].pos );
2378 savefile->ReadInt( bobFoot );
2379 savefile->ReadFloat( bobFrac );
2380 savefile->ReadFloat( bobfracsin );
2381 savefile->ReadInt( bobCycle );
2382 savefile->ReadFloat( xyspeed );
2383 savefile->ReadInt( stepUpTime );
2384 savefile->ReadFloat( stepUpDelta );
2385 savefile->ReadFloat( idealLegsYaw );
2386 savefile->ReadFloat( legsYaw );
2387 savefile->ReadBool( legsForward );
2388 savefile->ReadFloat( oldViewYaw );
2389 savefile->ReadAngles( viewBobAngles );
2390 savefile->ReadVec3( viewBob );
2391 savefile->ReadInt( landChange );
2392 savefile->ReadInt( landTime );
2394 savefile->ReadInt( currentWeapon );
2395 savefile->ReadInt( idealWeapon );
2396 savefile->ReadInt( previousWeapon );
2397 savefile->ReadInt( weaponSwitchTime );
2398 savefile->ReadBool( weaponEnabled );
2399 savefile->ReadBool( showWeaponViewModel );
2401 savefile->ReadSkin( skin );
2402 savefile->ReadSkin( powerUpSkin );
2403 savefile->ReadString( baseSkinName );
2405 savefile->ReadInt( numProjectilesFired );
2406 savefile->ReadInt( numProjectileHits );
2408 savefile->ReadBool( airless );
2409 savefile->ReadInt( airTics );
2410 savefile->ReadInt( lastAirDamage );
2412 savefile->ReadBool( gibDeath );
2413 savefile->ReadBool( gibsLaunched );
2414 savefile->ReadVec3( gibsDir );
2416 savefile->ReadFloat( set );
2417 zoomFov.SetStartTime( set );
2418 savefile->ReadFloat( set );
2419 zoomFov.SetDuration( set );
2420 savefile->ReadFloat( set );
2421 zoomFov.SetStartValue( set );
2422 savefile->ReadFloat( set );
2423 zoomFov.SetEndValue( set );
2425 savefile->ReadFloat( set );
2426 centerView.SetStartTime( set );
2427 savefile->ReadFloat( set );
2428 centerView.SetDuration( set );
2429 savefile->ReadFloat( set );
2430 centerView.SetStartValue( set );
2431 savefile->ReadFloat( set );
2432 centerView.SetEndValue( set );
2434 savefile->ReadBool( fxFov );
2436 savefile->ReadFloat( influenceFov );
2437 savefile->ReadInt( influenceActive );
2438 savefile->ReadFloat( influenceRadius );
2439 savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
2440 savefile->ReadMaterial( influenceMaterial );
2441 savefile->ReadSkin( influenceSkin );
2443 savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
2445 for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
2446 savefile->ReadAngles( loggedViewAngles[ i ] );
2448 for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
2449 savefile->ReadInt( loggedAccel[ i ].time );
2450 savefile->ReadVec3( loggedAccel[ i ].dir );
2452 savefile->ReadInt( currentLoggedAccel );
2454 savefile->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
2455 // can't save focusUI
2457 savefile->ReadObject( reinterpret_cast<idClass *&>( focusCharacter ) );
2458 savefile->ReadInt( talkCursor );
2459 savefile->ReadInt( focusTime );
2460 savefile->ReadObject( reinterpret_cast<idClass *&>( focusVehicle ) );
2461 savefile->ReadUserInterface( cursor );
2463 savefile->ReadInt( oldMouseX );
2464 savefile->ReadInt( oldMouseY );
2466 savefile->ReadString( pdaAudio );
2467 savefile->ReadString( pdaVideo );
2468 savefile->ReadString( pdaVideoWave );
2470 savefile->ReadBool( tipUp );
2471 savefile->ReadBool( objectiveUp );
2473 savefile->ReadInt( lastDamageDef );
2474 savefile->ReadVec3( lastDamageDir );
2475 savefile->ReadInt( lastDamageLocation );
2476 savefile->ReadInt( smoothedFrame );
2477 savefile->ReadBool( smoothedOriginUpdated );
2478 savefile->ReadVec3( smoothedOrigin );
2479 savefile->ReadAngles( smoothedAngles );
2481 savefile->ReadBool( ready );
2482 savefile->ReadBool( respawning );
2483 savefile->ReadBool( leader );
2484 savefile->ReadInt( lastSpectateChange );
2485 savefile->ReadInt( lastTeleFX );
2487 // set the pm_ cvars
2488 const idKeyValue *kv;
2489 kv = spawnArgs.MatchPrefix( "pm_", NULL );
2491 cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
2492 kv = spawnArgs.MatchPrefix( "pm_", kv );
2495 savefile->ReadFloat( set );
2496 pm_stamina.SetFloat( set );
2498 // create combat collision hull for exact collision detection
2502 int weaponToggleCount;
2503 savefile->ReadInt(weaponToggleCount);
2504 for(i = 0; i < weaponToggleCount; i++) {
2505 WeaponToggle_t newToggle;
2506 memset(&newToggle, 0, sizeof(newToggle));
2509 savefile->ReadString(name);
2510 strcpy(newToggle.name, name.c_str());
2513 savefile->ReadInt(indexCount);
2514 for(int j = 0; j < indexCount; j++) {
2516 savefile->ReadInt(temp);
2517 newToggle.toggleList.Append(temp);
2519 weaponToggles.Set(newToggle.name, newToggle);
2521 savefile->ReadObject(reinterpret_cast<idClass *&>(mountedObject));
2522 enviroSuitLight.Restore( savefile );
2523 savefile->ReadBool( healthRecharge );
2524 savefile->ReadInt( lastHealthRechargeTime );
2525 savefile->ReadInt( rechargeSpeed );
2526 savefile->ReadFloat( new_g_damageScale );
2528 savefile->ReadBool( bloomEnabled );
2529 savefile->ReadFloat( bloomSpeed );
2530 savefile->ReadFloat( bloomIntensity );
2536 idPlayer::PrepareForRestart
2539 void idPlayer::PrepareForRestart( void ) {
2542 forceRespawn = true;
2545 // Confirm reset hud states
2549 hud->SetStateInt( "red_flagstatus", 0 );
2550 hud->SetStateInt( "blue_flagstatus", 0 );
2554 // we will be restarting program, clear the client entities from program-related things first
2557 // the sound world is going to be cleared, don't keep references to emitters
2558 FreeSoundEmitter( false );
2566 void idPlayer::Restart( void ) {
2569 // client needs to setup the animation script object again
2570 if ( gameLocal.isClient ) {
2573 // choose a random spot and prepare the point of view in case player is left spectating
2574 assert( spectating );
2575 SpawnFromSpawnSpot();
2578 useInitialSpawns = true;
2579 UpdateSkinSetup( true );
2584 idPlayer::ServerSpectate
2587 void idPlayer::ServerSpectate( bool spectate ) {
2588 assert( !gameLocal.isClient );
2590 if ( spectating != spectate ) {
2591 Spectate( spectate );
2593 SetSpectateOrigin();
2595 if ( gameLocal.gameType == GAME_DM ) {
2596 // make sure the scores are reset so you can't exploit by spectating and entering the game back
2597 // other game types don't matter, as you either can't join back, or it's team scores
2598 gameLocal.mpGame.ClearFrags( entityNumber );
2603 SpawnFromSpawnSpot();
2606 // drop the flag if player was carrying it
2607 if ( spectate && gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() &&
2617 idPlayer::SelectInitialSpawnPoint
2619 Try to find a spawn point marked 'initial', otherwise
2620 use normal spawn selection.
2623 void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
2627 spot = gameLocal.SelectInitialSpawnPoint( this );
2629 // set the player skin from the spawn location
2630 if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
2631 spawnArgs.Set( "spawn_skin", skin );
2634 // activate the spawn locations targets
2635 spot->PostEventMS( &EV_ActivateTargets, 0, this );
2637 origin = spot->GetPhysics()->GetOrigin();
2638 origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
2639 angles = spot->GetPhysics()->GetAxis().ToAngles();
2644 idPlayer::SpawnFromSpawnSpot
2646 Chooses a spawn location and spawns the player
2649 void idPlayer::SpawnFromSpawnSpot( void ) {
2650 idVec3 spawn_origin;
2651 idAngles spawn_angles;
2653 SelectInitialSpawnPoint( spawn_origin, spawn_angles );
2654 SpawnToPoint( spawn_origin, spawn_angles );
2659 idPlayer::SpawnToPoint
2661 Called every time a client is placed fresh in the world:
2662 after the first ClientBegin, and after each respawn
2663 Initializes all non-persistant parts of playerState
2665 when called here with spectating set to true, just place yourself and init
2668 void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
2671 assert( !gameLocal.isClient );
2677 fl.noknockback = false;
2679 // stop any ragdolls being used
2682 // set back the player physics
2683 SetPhysics( &physicsObj );
2685 physicsObj.SetClipModelAxis();
2686 physicsObj.EnableClip();
2688 if ( !spectating ) {
2689 SetCombatContents( true );
2692 physicsObj.SetLinearVelocity( vec3_origin );
2694 // setup our initial view
2695 if ( !spectating ) {
2696 SetOrigin( spawn_origin );
2698 spec_origin = spawn_origin;
2699 spec_origin[ 2 ] += pm_normalheight.GetFloat();
2700 spec_origin[ 2 ] += SPECTATE_RAISE;
2701 SetOrigin( spec_origin );
2704 // if this is the first spawn of the map, we don't have a usercmd yet,
2705 // so the delta angles won't be correct. This will be fixed on the first think.
2706 viewAngles = ang_zero;
2707 SetDeltaViewAngles( ang_zero );
2708 SetViewAngles( spawn_angles );
2709 spawnAngles = spawn_angles;
2710 spawnAnglesSet = false;
2714 idealLegsYaw = 0.0f;
2715 oldViewYaw = viewAngles.yaw;
2723 if ( gameLocal.isMultiplayer ) {
2724 if ( !spectating ) {
2725 // we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
2726 if ( lastTeleFX < gameLocal.time - 1000 ) {
2727 idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
2728 lastTeleFX = gameLocal.time;
2733 AI_TELEPORT = false;
2736 // kill anything at the new position
2737 if ( !spectating ) {
2738 physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
2739 gameLocal.KillBox( this );
2742 // don't allow full run speed for a bit
2743 physicsObj.SetKnockBack( 100 );
2745 // set our respawn time and buttons so that if we're killed we don't respawn immediately
2746 minRespawnTime = gameLocal.time;
2747 maxRespawnTime = gameLocal.time;
2748 if ( !spectating ) {
2749 forceRespawn = false;
2752 privateCameraView = NULL;
2754 BecomeActive( TH_THINK );
2756 // run a client frame to drop exactly to the floor,
2757 // initialize animations and other things
2761 lastManOver = false;
2762 lastManPlayAgain = false;
2763 isTelefragged = false;
2768 idPlayer::SavePersistantInfo
2770 Saves any inventory and player stats when changing levels.
2773 void idPlayer::SavePersistantInfo( void ) {
2774 idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
2777 inventory.GetPersistantData( playerInfo );
2778 playerInfo.SetInt( "health", health );
2779 playerInfo.SetInt( "current_weapon", currentWeapon );
2784 idPlayer::RestorePersistantInfo
2786 Restores any inventory and player stats when changing levels.
2789 void idPlayer::RestorePersistantInfo( void ) {
2790 if ( gameLocal.isMultiplayer ) {
2791 gameLocal.persistentPlayerInfo[entityNumber].Clear();
2794 spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
2796 inventory.RestoreInventory( this, spawnArgs );
2797 health = spawnArgs.GetInt( "health", "100" );
2798 if ( !gameLocal.isClient ) {
2799 idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
2805 idPlayer::GetUserInfo
2808 idDict *idPlayer::GetUserInfo( void ) {
2809 return &gameLocal.userInfo[ entityNumber ];
2814 idPlayer::UpdateSkinSetup
2817 void idPlayer::UpdateSkinSetup( bool restart ) {
2819 team = ( idStr::Icmp( GetUserInfo()->GetString( "ui_team" ), "Blue" ) == 0 );
2821 if ( gameLocal.mpGame.IsGametypeTeamBased() ) { /* CTF */
2823 baseSkinName = "skins/characters/player/marine_mp_blue";
2825 baseSkinName = "skins/characters/player/marine_mp_red";
2827 if ( !gameLocal.isClient && team != latchedTeam ) {
2828 gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
2832 baseSkinName = GetUserInfo()->GetString( "ui_skin" );
2834 if ( !baseSkinName.Length() ) {
2835 baseSkinName = "skins/characters/player/marine_mp";
2837 skin = declManager->FindSkin( baseSkinName, false );
2839 // match the skin to a color band for scoreboard
2840 if ( baseSkinName.Find( "red" ) != -1 ) {
2842 } else if ( baseSkinName.Find( "green" ) != -1 ) {
2844 } else if ( baseSkinName.Find( "blue" ) != -1 ) {
2846 } else if ( baseSkinName.Find( "yellow" ) != -1 ) {
2848 } else if ( baseSkinName.Find( "grey" ) != -1 ) {
2850 } else if ( baseSkinName.Find( "purple" ) != -1 ) {
2852 } else if ( baseSkinName.Find( "orange" ) != -1 ) {
2857 colorBar = colorBarTable[ colorBarIndex ];
2858 if ( PowerUpActive( BERSERK ) ) {
2859 powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
2862 else if ( PowerUpActive( INVULNERABILITY ) ) {
2863 powerUpSkin = declManager->FindSkin( baseSkinName + "_invuln" );
2864 //} else if ( PowerUpActive( HASTE ) ) {
2865 // powerUpSkin = declManager->FindSkin( baseSkinName + "_haste" );
2872 idPlayer::BalanceTDM
2875 bool idPlayer::BalanceTDM( void ) {
2876 int i, balanceTeam, teamCount[2];
2879 teamCount[ 0 ] = teamCount[ 1 ] = 0;
2880 for( i = 0; i < gameLocal.numClients; i++ ) {
2881 ent = gameLocal.entities[ i ];
2882 if ( ent && ent->IsType( idPlayer::Type ) ) {
2883 teamCount[ static_cast< idPlayer * >( ent )->team ]++;
2887 if ( teamCount[ 0 ] < teamCount[ 1 ] ) {
2889 } else if ( teamCount[ 0 ] > teamCount[ 1 ] ) {
2892 if ( balanceTeam != -1 && team != balanceTeam ) {
2893 common->DPrintf( "team balance: forcing player %d to %s team\n", entityNumber, balanceTeam ? "blue" : "red" );
2895 GetUserInfo()->Set( "ui_team", team ? "Blue" : "Red" );
2903 idPlayer::UserInfoChanged
2906 bool idPlayer::UserInfoChanged( bool canModify ) {
2912 userInfo = GetUserInfo();
2913 showWeaponViewModel = userInfo->GetBool( "ui_showGun" );
2915 if ( !gameLocal.isMultiplayer ) {
2919 modifiedInfo = false;
2921 spec = ( idStr::Icmp( userInfo->GetString( "ui_spectate" ), "Spectate" ) == 0 );
2922 if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
2923 // never let spectators go back to game while sudden death is on
2924 if ( canModify && gameLocal.mpGame.GetGameState() == idMultiplayerGame::SUDDENDEATH && !spec && wantSpectate == true ) {
2925 userInfo->Set( "ui_spectate", "Spectate" );
2926 modifiedInfo |= true;
2928 if ( spec != wantSpectate && !spec ) {
2929 // returning from spectate, set forceRespawn so we don't get stuck in spectate forever
2930 forceRespawn = true;
2932 wantSpectate = spec;
2935 if ( canModify && spec ) {
2936 userInfo->Set( "ui_spectate", "Play" );
2937 modifiedInfo |= true;
2938 } else if ( spectating ) {
2939 // allow player to leaving spectator mode if they were in it when si_spectators got turned off
2940 forceRespawn = true;
2942 wantSpectate = false;
2945 newready = ( idStr::Icmp( userInfo->GetString( "ui_ready" ), "Ready" ) == 0 );
2946 if ( ready != newready && gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP && !wantSpectate ) {
2947 gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_07180" ), userInfo->GetString( "ui_name" ), newready ? common->GetLanguageDict()->GetString( "#str_04300" ) : common->GetLanguageDict()->GetString( "#str_04301" ) );
2950 team = ( idStr::Icmp( userInfo->GetString( "ui_team" ), "Blue" ) == 0 );
2951 // server maintains TDM balance
2952 if ( canModify && gameLocal.mpGame.IsGametypeTeamBased() && !gameLocal.mpGame.IsInGame( entityNumber ) && g_balanceTDM.GetBool() ) { /* CTF */
2953 modifiedInfo |= BalanceTDM( );
2955 UpdateSkinSetup( false );
2957 isChatting = userInfo->GetBool( "ui_chat", "0" );
2958 if ( canModify && isChatting && AI_DEAD ) {
2959 // if dead, always force chat icon off.
2961 userInfo->SetBool( "ui_chat", false );
2962 modifiedInfo |= true;
2965 return modifiedInfo;
2970 idPlayer::UpdateHudAmmo
2973 void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
2977 assert( weapon.GetEntity() );
2980 inclip = weapon.GetEntity()->AmmoInClip();
2981 ammoamount = weapon.GetEntity()->AmmoAvailable();
2984 //Hack to stop the bloodstone ammo to display when it is being activated
2985 if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() || currentWeapon == weapon_bloodstone) {
2987 if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() ) {
2989 // show infinite ammo
2990 _hud->SetStateString( "player_ammo", "" );
2991 _hud->SetStateString( "player_totalammo", "" );
2993 // show remaining ammo
2995 _hud->SetStateString( "player_totalammo", va( "%i", ammoamount ) );
2997 _hud->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip ) );
2999 _hud->SetStateString( "player_ammo", weapon.GetEntity()->ClipSize() ? va( "%i", inclip ) : "--" ); // how much in the current clip
3000 _hud->SetStateString( "player_clips", weapon.GetEntity()->ClipSize() ? va( "%i", ammoamount / weapon.GetEntity()->ClipSize() ) : "--" );
3003 _hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount ) );
3005 _hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
3009 _hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
3010 _hud->SetStateBool( "player_clip_empty", ( weapon.GetEntity()->ClipSize() ? inclip == 0 : false ) );
3011 _hud->SetStateBool( "player_clip_low", ( weapon.GetEntity()->ClipSize() ? inclip <= weapon.GetEntity()->LowAmmo() : false ) );
3014 //Hack to stop the bloodstone ammo to display when it is being activated
3015 if(currentWeapon == weapon_bloodstone) {
3016 _hud->SetStateBool( "player_ammo_empty", false );
3017 _hud->SetStateBool( "player_clip_empty", false );
3018 _hud->SetStateBool( "player_clip_low", false );
3023 //Let the HUD know the total amount of ammo regardless of the ammo required value
3024 _hud->SetStateString( "player_ammo_count", va("%i", weapon.GetEntity()->AmmoCount()));
3028 //Make sure the hud always knows how many bloodstone charges there are
3030 ammo_t ammo_i = inventory.AmmoIndexForWeaponClass( "weapon_bloodstone_passive", &ammoRequired );
3031 int bloodstoneAmmo = inventory.HasAmmo( ammo_i, ammoRequired );
3032 _hud->SetStateString("player_bloodstone_ammo", va("%i", bloodstoneAmmo));
3033 _hud->HandleNamedEvent( "bloodstoneAmmoUpdate" );
3036 _hud->HandleNamedEvent( "updateAmmo" );
3041 idPlayer::UpdateHudStats
3044 void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
3045 int staminapercentage;
3050 max_stamina = pm_stamina.GetFloat();
3051 if ( !max_stamina ) {
3052 // stamina disabled, so show full stamina bar
3053 staminapercentage = 100.0f;
3055 staminapercentage = idMath::FtoiFast( 100.0f * stamina / max_stamina );
3058 _hud->SetStateInt( "player_health", health );
3059 _hud->SetStateInt( "player_stamina", staminapercentage );
3060 _hud->SetStateInt( "player_armor", inventory.armor );
3061 _hud->SetStateInt( "player_hr", heartRate );
3063 _hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 );
3065 _hud->HandleNamedEvent( "updateArmorHealthAir" );
3068 _hud->HandleNamedEvent( "updatePowerup" );
3071 if ( healthPulse ) {
3072 _hud->HandleNamedEvent( "healthPulse" );
3073 StartSound( "snd_healthpulse", SND_CHANNEL_ITEM, 0, false, NULL );
3074 healthPulse = false;
3078 _hud->HandleNamedEvent( "healthPulse" );
3079 StartSound( "snd_healthtake", SND_CHANNEL_ITEM, 0, false, NULL );
3083 if ( inventory.ammoPulse ) {
3084 _hud->HandleNamedEvent( "ammoPulse" );
3085 inventory.ammoPulse = false;
3087 if ( inventory.weaponPulse ) {
3088 // We need to update the weapon hud manually, but not
3089 // the armor/ammo/health because they are updated every
3090 // frame no matter what
3092 _hud->HandleNamedEvent( "weaponPulse" );
3093 inventory.weaponPulse = false;
3095 if ( inventory.armorPulse ) {
3096 _hud->HandleNamedEvent( "armorPulse" );
3097 inventory.armorPulse = false;
3101 if ( gameLocal.mpGame.IsGametypeFlagBased() && _hud )
3103 _hud->SetStateInt( "red_flagstatus", gameLocal.mpGame.GetFlagStatus( 0 ) );
3104 _hud->SetStateInt( "blue_flagstatus", gameLocal.mpGame.GetFlagStatus( 1 ) );
3106 _hud->SetStateInt( "red_team_score", gameLocal.mpGame.GetFlagPoints( 0 ) );
3107 _hud->SetStateInt( "blue_team_score", gameLocal.mpGame.GetFlagPoints( 1 ) );
3109 _hud->HandleNamedEvent( "RedFlagStatusChange" );
3110 _hud->HandleNamedEvent( "BlueFlagStatusChange" );
3113 _hud->HandleNamedEvent( "selfTeam" );
3118 UpdateHudAmmo( _hud );
3123 idPlayer::UpdateHudWeapon
3126 void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
3127 idUserInterface *hud = idPlayer::hud;
3129 // if updating the hud of a followed client
3130 if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
3131 idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
3132 if ( p->spectating && p->spectator == entityNumber ) {
3142 for ( int i = 0; i < MAX_WEAPONS; i++ ) {
3143 const char *weapnum = va( "def_weapon%d", i );
3144 const char *hudWeap = va( "weapon%d", i );
3146 if ( inventory.weapons & ( 1 << i ) ) {
3147 const char *weap = spawnArgs.GetString( weapnum );
3148 if ( weap && *weap ) {
3151 if ( idealWeapon == i ) {
3155 hud->SetStateInt( hudWeap, weapstate );
3157 if ( flashWeapon ) {
3160 //Clear all hud weapon varaibles for the weapon change
3161 hud->SetStateString( "player_ammo", "" );
3162 hud->SetStateString( "player_totalammo", "" );
3163 hud->SetStateString( "player_clips", "" );
3164 hud->SetStateString( "player_allammo", "" );
3165 hud->SetStateBool( "player_ammo_empty", false );
3166 hud->SetStateBool( "player_clip_empty", false );
3167 hud->SetStateBool( "player_clip_low", false );
3168 hud->SetStateString( "player_ammo_count", "");
3171 hud->HandleNamedEvent( "weaponChange" );
3180 void idPlayer::DrawHUD( idUserInterface *_hud ) {
3182 if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !_hud || !g_showHud.GetBool() ) {
3186 UpdateHudStats( _hud );
3188 _hud->SetStateString( "weapicon", weapon.GetEntity()->Icon() );
3190 // FIXME: this is temp to allow the sound meter to show up in the hud
3191 // it should be commented out before shipping but the code can remain
3192 // for mod developers to enable for the same functionality
3193 _hud->SetStateInt( "s_debug", cvarSystem->GetCVarInteger( "s_showLevelMeter" ) );
3195 weapon.GetEntity()->UpdateGUI();
3197 _hud->Redraw( gameLocal.realClientTime );
3199 // weapon targeting crosshair
3200 if ( !GuiActive() ) {
3201 if ( cursor && weapon.GetEntity()->ShowCrosshair() ) {
3204 if ( weapon.GetEntity()->GetGrabberState() == 1 || weapon.GetEntity()->GetGrabberState() == 2 ) {
3205 cursor->SetStateString( "grabbercursor", "1" );
3206 cursor->SetStateString( "combatcursor", "0" );
3208 cursor->SetStateString( "grabbercursor", "0" );
3209 cursor->SetStateString( "combatcursor", "1" );
3213 cursor->Redraw( gameLocal.realClientTime );
3220 idPlayer::EnterCinematic
3223 void idPlayer::EnterCinematic( void ) {
3225 if ( PowerUpActive( HELLTIME ) ) {
3232 StopSound( SND_CHANNEL_PDA, false );
3234 hud->HandleNamedEvent( "radioChatterDown" );
3237 physicsObj.SetLinearVelocity( vec3_origin );
3239 SetState( "EnterCinematic" );
3242 if ( weaponEnabled && weapon.GetEntity() ) {
3243 weapon.GetEntity()->EnterCinematic();
3247 AI_BACKWARD = false;
3248 AI_STRAFE_LEFT = false;
3249 AI_STRAFE_RIGHT = false;
3251 AI_ATTACK_HELD = false;
3252 AI_WEAPON_FIRED = false;
3256 AI_ONLADDER = false;
3257 AI_DEAD = ( health <= 0 );
3260 AI_HARDLANDING = false;
3261 AI_SOFTLANDING = false;
3263 AI_TELEPORT = false;
3264 AI_TURN_LEFT = false;
3265 AI_TURN_RIGHT = false;
3270 idPlayer::ExitCinematic
3273 void idPlayer::ExitCinematic( void ) {
3276 if ( weaponEnabled && weapon.GetEntity() ) {
3277 weapon.GetEntity()->ExitCinematic();
3280 SetState( "ExitCinematic" );
3285 =====================
3286 idPlayer::UpdateConditions
3287 =====================
3289 void idPlayer::UpdateConditions( void ) {
3295 // minus the push velocity to avoid playing the walking animation and sounds when riding a mover
3296 velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
3297 fallspeed = velocity * physicsObj.GetGravityNormal();
3299 if ( influenceActive ) {
3301 AI_BACKWARD = false;
3302 AI_STRAFE_LEFT = false;
3303 AI_STRAFE_RIGHT = false;
3304 } else if ( gameLocal.time - lastDmgTime < 500 ) {
3305 forwardspeed = velocity * viewAxis[ 0 ];
3306 sidespeed = velocity * viewAxis[ 1 ];
3307 AI_FORWARD = AI_ONGROUND && ( forwardspeed > 20.01f );
3308 AI_BACKWARD = AI_ONGROUND && ( forwardspeed < -20.01f );
3309 AI_STRAFE_LEFT = AI_ONGROUND && ( sidespeed > 20.01f );
3310 AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
3311 } else if ( xyspeed > MIN_BOB_SPEED ) {
3312 AI_FORWARD = AI_ONGROUND && ( usercmd.forwardmove > 0 );
3313 AI_BACKWARD = AI_ONGROUND && ( usercmd.forwardmove < 0 );
3314 AI_STRAFE_LEFT = AI_ONGROUND && ( usercmd.rightmove < 0 );
3315 AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
3318 AI_BACKWARD = false;
3319 AI_STRAFE_LEFT = false;
3320 AI_STRAFE_RIGHT = false;
3323 AI_RUN = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
3324 AI_DEAD = ( health <= 0 );
3331 Called when a weapon fires, generates head twitches, etc
3334 void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
3338 // play the fire animation
3339 AI_WEAPON_FIRED = true;
3341 // update view feedback
3342 playerView.WeaponFireFeedback( weaponDef );
3347 idPlayer::StopFiring
3350 void idPlayer::StopFiring( void ) {
3351 AI_ATTACK_HELD = false;
3352 AI_WEAPON_FIRED = false;
3354 if ( weapon.GetEntity() ) {
3355 weapon.GetEntity()->EndAttack();
3361 idPlayer::FireWeapon
3364 void idPlayer::FireWeapon( void ) {
3368 if ( privateCameraView ) {
3372 if ( g_editEntityMode.GetInteger() ) {
3373 GetViewPos( muzzle, axis );
3374 if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
3379 if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
3380 if ( weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
3381 AI_ATTACK_HELD = true;
3382 weapon.GetEntity()->BeginAttack();
3383 if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
3385 hud->HandleNamedEvent( "soulCubeNotReady" );
3387 SelectWeapon( previousWeapon, false );
3390 if( (weapon_bloodstone >= 0) && (currentWeapon == weapon_bloodstone) && inventory.weapons & ( 1 << weapon_bloodstone_active1 ) && weapon.GetEntity()->GetStatus() == WP_READY) {
3391 // tell it to switch to the previous weapon. Only do this once to prevent
3392 // weapon toggling messing up the previous weapon
3393 if(idealWeapon == weapon_bloodstone) {
3394 if(previousWeapon == weapon_bloodstone || previousWeapon == -1) {
3397 //Since this is a toggle weapon just select itself and it will toggle to the last weapon
3398 SelectWeapon( weapon_bloodstone, false );
3412 // may want to track with with a bool as well
3413 // keep from looking up named events so often
3414 if ( objectiveUp ) {
3422 idPlayer::CacheWeapons
3425 void idPlayer::CacheWeapons( void ) {
3429 // check if we have any weapons
3430 if ( !inventory.weapons ) {
3434 for( w = 0; w < MAX_WEAPONS; w++ ) {
3435 if ( inventory.weapons & ( 1 << w ) ) {
3436 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
3438 idWeapon::CacheWeapon( weap );
3440 inventory.weapons &= ~( 1 << w );
3451 bool idPlayer::Give( const char *statname, const char *value ) {
3458 if ( !idStr::Icmp( statname, "health" ) ) {
3459 if ( health >= inventory.maxHealth ) {
3462 amount = atoi( value );
3465 if ( health > inventory.maxHealth ) {
3466 health = inventory.maxHealth;
3469 hud->HandleNamedEvent( "healthPulse" );
3473 } else if ( !idStr::Icmp( statname, "stamina" ) ) {
3474 if ( stamina >= 100 ) {
3477 stamina += atof( value );
3478 if ( stamina > 100 ) {
3482 } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
3483 heartRate += atoi( value );
3484 if ( heartRate > MAX_HEARTRATE ) {
3485 heartRate = MAX_HEARTRATE;
3488 } else if ( !idStr::Icmp( statname, "air" ) ) {
3489 if ( airTics >= pm_airTics.GetInteger() ) {
3492 airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger();
3493 if ( airTics > pm_airTics.GetInteger() ) {
3494 airTics = pm_airTics.GetInteger();
3497 } else if ( !idStr::Icmp( statname, "enviroTime" ) ) {
3498 if ( PowerUpActive( ENVIROTIME ) ) {
3499 inventory.powerupEndTime[ ENVIROTIME ] += (atof(value) * 1000);
3501 GivePowerUp( ENVIROTIME, atoi(value)*1000 );
3504 bool ret = inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
3505 if(!idStr::Icmp( statname, "ammo_bloodstone" ) ) {
3506 //int i = inventory.AmmoIndexForAmmoClass( statname );
3507 //int max = inventory.MaxAmmoForAmmoClass( this, statname );
3508 //if(hud && inventory.ammo[ i ] >= max) {
3511 //Force an update of the bloodstone ammount
3513 ammo_t ammo_i = inventory.AmmoIndexForWeaponClass( "weapon_bloodstone_passive", &ammoRequired );
3514 int bloodstoneAmmo = inventory.HasAmmo( ammo_i, ammoRequired );
3515 hud->SetStateString("player_bloodstone_ammo", va("%i", bloodstoneAmmo));
3517 hud->HandleNamedEvent("bloodstoneReady");
3518 //Make sure we unlock the ability to harvest
3519 harvest_lock = false;
3524 return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
3533 idPlayer::GiveHealthPool
3535 adds health to the player health pool
3538 void idPlayer::GiveHealthPool( float amt ) {
3546 if ( healthPool > inventory.maxHealth - health ) {
3547 healthPool = inventory.maxHealth - health;
3549 nextHealthPulse = gameLocal.time;
3557 Returns false if the item shouldn't be picked up
3560 bool idPlayer::GiveItem( idItem *item ) {
3562 const idKeyValue *arg;
3567 if ( gameLocal.isMultiplayer && spectating ) {
3571 item->GetAttributes( attr );
3574 numPickup = inventory.pickupItemNames.Num();
3575 for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
3576 arg = attr.GetKeyVal( i );
3577 if ( Give( arg->GetKey(), arg->GetValue() ) ) {
3582 arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
3584 // We need to update the weapon hud manually, but not
3585 // the armor/ammo/health because they are updated every
3586 // frame no matter what
3587 UpdateHudWeapon( false );
3588 hud->HandleNamedEvent( "weaponPulse" );
3591 // display the pickup feedback on the hud
3592 if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
3593 inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ), this ); //_D3XP
3601 idPlayer::PowerUpModifier
3604 float idPlayer::PowerUpModifier( int type ) {
3607 if ( PowerUpActive( BERSERK ) ) {
3613 case PROJECTILE_DAMAGE: {
3617 case MELEE_DAMAGE: {
3621 case MELEE_DISTANCE: {
3628 if ( gameLocal.isMultiplayer && !gameLocal.isClient ) {
3629 if ( PowerUpActive( MEGAHEALTH ) ) {
3630 if ( healthPool <= 0 ) {
3631 GiveHealthPool( 100 );
3638 /*if( PowerUpActive( HASTE ) ) {
3654 idPlayer::PowerUpActive
3657 bool idPlayer::PowerUpActive( int powerup ) const {
3658 return ( inventory.powerups & ( 1 << powerup ) ) != 0;
3663 idPlayer::GivePowerUp
3666 bool idPlayer::GivePowerUp( int powerup, int time ) {
3670 if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
3672 if ( gameLocal.isServer ) {
3674 byte msgBuf[MAX_EVENT_PARAM_SIZE];
3676 msg.Init( msgBuf, sizeof( msgBuf ) );
3677 msg.WriteShort( powerup );
3678 msg.WriteBits( 1, 1 );
3679 ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
3682 if ( powerup != MEGAHEALTH ) {
3683 inventory.GivePowerUp( this, powerup, time );
3686 const idDeclEntityDef *def = NULL;
3690 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3691 inventory.AddPickupName("#str_00100627", "", this);
3694 if(gameLocal.isMultiplayer) {
3695 if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) ) {
3696 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
3701 if ( baseSkinName.Length() ) {
3702 powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
3704 if ( !gameLocal.isClient ) {
3706 if( !gameLocal.isMultiplayer ) {
3707 // Trying it out without the health boost (1/3/05)
3708 // Give the player full health in single-player
3711 // Switch to fists in multiplayer
3720 case INVISIBILITY: {
3721 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3722 inventory.AddPickupName("#str_00100628", "", this);
3724 spawnArgs.GetString( "skin_invisibility", "", &skin );
3725 powerUpSkin = declManager->FindSkin( skin );
3726 // remove any decals from the model
3727 if ( modelDefHandle != -1 ) {
3728 gameRenderWorld->RemoveDecals( modelDefHandle );
3730 if ( weapon.GetEntity() ) {
3731 weapon.GetEntity()->UpdateSkin();
3733 /* if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
3734 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
3740 inventory.AddPickupName("#str_00100799", "", this);
3746 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3747 inventory.AddPickupName("#str_00100629", "", this);
3749 if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
3750 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
3752 def = gameLocal.FindEntityDef( "powerup_megahealth", false );
3754 health = def->dict.GetInt( "inv_health" );
3760 if ( spawnArgs.GetString( "snd_helltime_start", "", &sound ) ) {
3761 PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
3763 if ( spawnArgs.GetString( "snd_helltime_loop", "", &sound ) ) {
3764 PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_DEMONIC );
3769 // Turn on the envirosuit sound
3770 if ( gameSoundWorld ) {
3771 gameSoundWorld->SetEnviroSuit( true );
3774 // Put the helmet and lights on the player
3778 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
3781 gameLocal.SpawnEntityDef( *lightDef, &temp, false );
3783 idLight *eLight = static_cast<idLight *>(temp);
3784 eLight->GetPhysics()->SetOrigin( firstPersonViewOrigin );
3785 eLight->UpdateVisuals();
3788 enviroSuitLight = eLight;
3793 hudPowerup = ENVIROTIME;
3794 // The HUD display bar is fixed at 60 seconds
3795 hudPowerupDuration = 60000;
3798 case INVULNERABILITY: {
3799 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3800 inventory.AddPickupName("#str_00100630", "", this);
3802 if(gameLocal.isMultiplayer) {
3803 /*if ( spawnArgs.GetString( "snd_invulnerable", "", &sound ) ) {
3804 StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
3806 if ( baseSkinName.Length() ) {
3807 powerUpSkin = declManager->FindSkin( baseSkinName + "_invuln" );
3813 if(gameLocal.isMultiplayer && !gameLocal.isClient) {
3814 inventory.AddPickupName("#str_00100631", "", this);
3817 if ( baseSkinName.Length() ) {
3818 powerUpSkin = declManager->FindSkin( baseSkinName + "_haste" );
3826 hud->HandleNamedEvent( "itemPickup" );
3831 gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
3838 idPlayer::ClearPowerup
3841 void idPlayer::ClearPowerup( int i ) {
3843 if ( gameLocal.isServer ) {
3845 byte msgBuf[MAX_EVENT_PARAM_SIZE];
3847 msg.Init( msgBuf, sizeof( msgBuf ) );
3848 msg.WriteShort( i );
3849 msg.WriteBits( 0, 1 );
3850 ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
3854 inventory.powerups &= ~( 1 << i );
3855 inventory.powerupEndTime[ i ] = 0;
3858 if(gameLocal.isMultiplayer) {
3859 StopSound( SND_CHANNEL_DEMONIC, false );
3862 if(!gameLocal.isMultiplayer) {
3863 StopHealthRecharge();
3868 case INVISIBILITY: {
3869 if ( weapon.GetEntity() ) {
3870 weapon.GetEntity()->UpdateSkin();
3876 StopSound( SND_CHANNEL_DEMONIC, false );
3883 // Turn off the envirosuit sound
3884 if ( gameSoundWorld ) {
3885 gameSoundWorld->SetEnviroSuit( false );
3888 // Take off the helmet and lights
3889 if ( enviroSuitLight.IsValid() ) {
3890 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
3892 enviroSuitLight = NULL;
3895 case INVULNERABILITY: {
3896 if(gameLocal.isMultiplayer) {
3897 StopSound( SND_CHANNEL_DEMONIC, false );
3901 if(gameLocal.isMultiplayer) {
3902 StopSound( SND_CHANNEL_DEMONIC, false );
3911 idPlayer::UpdatePowerUps
3914 void idPlayer::UpdatePowerUps( void ) {
3917 if ( !gameLocal.isClient ) {
3918 for ( i = 0; i < MAX_POWERUPS; i++ ) {
3920 if ( ( inventory.powerups & ( 1 << i ) ) && inventory.powerupEndTime[i] > gameLocal.time ) {
3923 if ( enviroSuitLight.IsValid() ) {
3924 idAngles lightAng = firstPersonViewAxis.ToAngles();
3925 idVec3 lightOrg = firstPersonViewOrigin;
3926 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
3928 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
3929 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
3931 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
3932 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
3933 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
3934 lightAng.pitch += enviroAngleOffset.x;
3935 lightAng.yaw += enviroAngleOffset.y;
3936 lightAng.roll += enviroAngleOffset.z;
3938 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
3939 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
3940 enviroSuitLight.GetEntity()->UpdateVisuals();
3941 enviroSuitLight.GetEntity()->Present();
3951 if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
3958 if ( powerUpSkin ) {
3959 renderEntity.customSkin = powerUpSkin;
3961 renderEntity.customSkin = skin;
3965 if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
3966 assert( !gameLocal.isClient ); // healthPool never be set on client
3967 int amt = ( healthPool > 5 ) ? 5 : healthPool;
3969 if ( health > inventory.maxHealth ) {
3970 health = inventory.maxHealth;
3975 nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
3978 #ifndef ID_DEMO_BUILD
3979 if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
3980 assert( !gameLocal.isClient ); // healthPool never be set on client
3983 if(!PowerUpActive(INVULNERABILITY)) {
3985 health -= g_healthTakeAmt.GetInteger();
3986 if ( health < g_healthTakeLimit.GetInteger() ) {
3987 health = g_healthTakeLimit.GetInteger();
3992 nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
4000 idPlayer::ClearPowerUps
4003 void idPlayer::ClearPowerUps( void ) {
4005 for ( i = 0; i < MAX_POWERUPS; i++ ) {
4006 if ( PowerUpActive( i ) ) {
4010 inventory.ClearPowerUps();
4013 if ( gameLocal.isMultiplayer ) {
4014 if ( enviroSuitLight.IsValid() ) {
4015 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
4023 idPlayer::GiveInventoryItem
4026 bool idPlayer::GiveInventoryItem( idDict *item ) {
4027 if ( gameLocal.isMultiplayer && spectating ) {
4030 inventory.items.Append( new idDict( *item ) );
4032 const char* itemName = item->GetString( "inv_name" );
4033 if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
4034 info.name = common->GetLanguageDict()->GetString( itemName );
4036 info.name = itemName;
4038 info.icon = item->GetString( "inv_icon" );
4039 inventory.pickupItemNames.Append( info );
4041 hud->SetStateString( "itemicon", info.icon );
4042 hud->HandleNamedEvent( "invPickup" );
4045 #ifdef _D3XP //Added to support powercells
4046 if(item->GetInt("inv_powercell") && focusUI) {
4047 //Reset the powercell count
4048 int powerCellCount = 0;
4049 for ( int j = 0; j < inventory.items.Num(); j++ ) {
4050 idDict *item = inventory.items[ j ];
4051 if(item->GetInt("inv_powercell")) {
4055 focusUI->SetStateInt( "powercell_count", powerCellCount );
4062 #ifdef _D3XP //BSM: Implementing this defined function for scripted give inventory items
4065 idPlayer::GiveInventoryItem
4068 bool idPlayer::GiveInventoryItem( const char *name ) {
4071 args.Set( "classname", name );
4072 args.Set( "owner", this->name.c_str() );
4073 gameLocal.SpawnEntityDef( args);
4080 idPlayer::UpdateObjectiveInfo
4083 void idPlayer::UpdateObjectiveInfo( void ) {
4084 if ( objectiveSystem == NULL ) {
4087 objectiveSystem->SetStateString( "objective1", "" );
4088 objectiveSystem->SetStateString( "objective2", "" );
4089 objectiveSystem->SetStateString( "objective3", "" );
4090 for ( int i = 0; i < inventory.objectiveNames.Num(); i++ ) {
4091 objectiveSystem->SetStateString( va( "objective%i", i+1 ), "1" );
4092 objectiveSystem->SetStateString( va( "objectivetitle%i", i+1 ), inventory.objectiveNames[i].title.c_str() );
4093 objectiveSystem->SetStateString( va( "objectivetext%i", i+1 ), inventory.objectiveNames[i].text.c_str() );
4094 objectiveSystem->SetStateString( va( "objectiveshot%i", i+1 ), inventory.objectiveNames[i].screenshot.c_str() );
4096 objectiveSystem->StateChanged( gameLocal.time );
4101 idPlayer::GiveObjective
4104 void idPlayer::GiveObjective( const char *title, const char *text, const char *screenshot ) {
4105 idObjectiveInfo info;
4108 info.screenshot = screenshot;
4109 inventory.objectiveNames.Append( info );
4110 ShowObjective( "newObjective" );
4112 hud->HandleNamedEvent( "newObjective" );
4118 idPlayer::CompleteObjective
4121 void idPlayer::CompleteObjective( const char *title ) {
4122 int c = inventory.objectiveNames.Num();
4123 for ( int i = 0; i < c; i++ ) {
4124 if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
4125 inventory.objectiveNames.RemoveIndex( i );
4129 ShowObjective( "newObjectiveComplete" );
4132 hud->HandleNamedEvent( "newObjectiveComplete" );
4141 void idPlayer::GiveVideo( const char *videoName, idDict *item ) {
4143 if ( videoName == NULL || *videoName == NULL ) {
4147 inventory.videos.AddUnique( videoName );
4151 info.name = item->GetString( "inv_name" );
4152 info.icon = item->GetString( "inv_icon" );
4153 inventory.pickupItemNames.Append( info );
4156 hud->HandleNamedEvent( "videoPickup" );
4162 idPlayer::GiveSecurity
4165 void idPlayer::GiveSecurity( const char *security ) {
4166 GetPDA()->SetSecurity( security );
4168 hud->SetStateString( "pda_security", "1" );
4169 hud->HandleNamedEvent( "securityPickup" );
4178 void idPlayer::GiveEmail( const char *emailName ) {
4180 if ( emailName == NULL || *emailName == NULL ) {
4184 inventory.emails.AddUnique( emailName );
4185 GetPDA()->AddEmail( emailName );
4188 hud->HandleNamedEvent( "emailPickup" );
4197 void idPlayer::GivePDA( const char *pdaName, idDict *item )
4199 if ( gameLocal.isMultiplayer && spectating ) {
4204 inventory.pdaSecurity.AddUnique( item->GetString( "inv_name" ) );
4207 if ( pdaName == NULL || *pdaName == NULL ) {
4208 pdaName = "personal";
4211 const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, pdaName ) );
4213 inventory.pdas.AddUnique( pdaName );
4215 // Copy any videos over
4216 for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
4217 const idDeclVideo *video = pda->GetVideoByIndex( i );
4219 inventory.videos.AddUnique( video->GetName() );
4223 // This is kind of a hack, but it works nicely
4224 // We don't want to display the 'you got a new pda' message during a map load
4225 if ( gameLocal.GetFrameNum() > 10 ) {
4227 idStr pdaName = pda->GetPdaName();
4228 pdaName.RemoveColors();
4229 hud->SetStateString( "pda", "1" );
4230 hud->SetStateString( "pda_text", pdaName );
4231 const char *sec = pda->GetSecurity();
4232 hud->SetStateString( "pda_security", ( sec && *sec ) ? "1" : "0" );
4233 hud->HandleNamedEvent( "pdaPickup" );
4236 if ( inventory.pdas.Num() == 1 ) {
4237 GetPDA()->RemoveAddedEmailsAndVideos();
4238 if ( !objectiveSystemOpen ) {
4241 objectiveSystem->HandleNamedEvent( "showPDATip" );
4242 //ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
4245 if ( inventory.pdas.Num() > 1 && pda->GetNumVideos() > 0 && hud ) {
4246 hud->HandleNamedEvent( "videoPickup" );
4253 idPlayer::FindInventoryItem
4256 idDict *idPlayer::FindInventoryItem( const char *name ) {
4257 for ( int i = 0; i < inventory.items.Num(); i++ ) {
4258 const char *iname = inventory.items[i]->GetString( "inv_name" );
4259 if ( iname && *iname ) {
4260 if ( idStr::Icmp( name, iname ) == 0 ) {
4261 return inventory.items[i];
4270 idPlayer::RemoveInventoryItem
4273 void idPlayer::RemoveInventoryItem( const char *name ) {
4274 //Hack for localization
4275 if(!idStr::Icmp(name, "Pwr Cell")) {
4276 name = common->GetLanguageDict()->GetString( "#str_00101056" );
4278 idDict *item = FindInventoryItem(name);
4280 RemoveInventoryItem( item );
4286 idPlayer::RemoveInventoryItem
4289 void idPlayer::RemoveInventoryItem( idDict *item ) {
4290 inventory.items.Remove( item );
4292 #ifdef _D3XP //Added to support powercells
4293 if(item->GetInt("inv_powercell") && focusUI) {
4294 //Reset the powercell count
4295 int powerCellCount = 0;
4296 for ( int j = 0; j < inventory.items.Num(); j++ ) {
4297 idDict *item = inventory.items[ j ];
4298 if(item->GetInt("inv_powercell")) {
4302 focusUI->SetStateInt( "powercell_count", powerCellCount );
4314 void idPlayer::GiveItem( const char *itemname ) {
4317 args.Set( "classname", itemname );
4318 args.Set( "owner", name.c_str() );
4319 gameLocal.SpawnEntityDef( args );
4321 hud->HandleNamedEvent( "itemPickup" );
4327 idPlayer::SlotForWeapon
4330 int idPlayer::SlotForWeapon( const char *weaponName ) {
4333 for( i = 0; i < MAX_WEAPONS; i++ ) {
4334 const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
4335 if ( !idStr::Cmp( weap, weaponName ) ) {
4349 void idPlayer::Reload( void ) {
4350 if ( gameLocal.isClient ) {
4354 if ( spectating || gameLocal.inCinematic || influenceActive ) {
4358 if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
4359 weapon.GetEntity()->Reload();
4365 idPlayer::NextBestWeapon
4368 void idPlayer::NextBestWeapon( void ) {
4370 int w = MAX_WEAPONS;
4372 if ( gameLocal.isClient || !weaponEnabled ) {
4378 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4380 if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) ) {
4382 if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !(inventory.HasAmmo( weap )) ) ) {
4386 if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
4391 //Some weapons will report having ammo but the clip is empty and
4392 //will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
4393 //We need to skip these weapons because they cannot be used
4394 if(inventory.HasEmptyClipCannotRefill(weap, this)) {
4402 weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4408 idPlayer::NextWeapon
4411 void idPlayer::NextWeapon( void ) {
4416 if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
4420 if ( gameLocal.isClient ) {
4424 // check if we have any weapons
4425 if ( !inventory.weapons ) {
4432 if ( w >= MAX_WEAPONS ) {
4435 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4436 if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
4442 if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
4447 if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
4449 if ( inventory.HasAmmo( weap ) ) {
4455 if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
4457 weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4464 idPlayer::PrevWeapon
4467 void idPlayer::PrevWeapon( void ) {
4472 if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
4476 if ( gameLocal.isClient ) {
4480 // check if we have any weapons
4481 if ( !inventory.weapons ) {
4489 w = MAX_WEAPONS - 1;
4491 weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
4492 if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
4498 if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
4502 if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
4504 if ( inventory.HasAmmo( weap ) ) {
4510 if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
4512 weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
4519 idPlayer::SelectWeapon
4522 void idPlayer::SelectWeapon( int num, bool force ) {
4525 if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
4529 if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
4533 if ( gameLocal.isClient ) {
4537 if ( ( num != weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
4540 if ( hiddenWeapon && weapon.GetEntity() ) {
4541 weapon.GetEntity()->LowerWeapon();
4543 weapon.GetEntity()->RaiseWeapon();
4547 weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
4549 gameLocal.Printf( "Invalid weapon\n" );
4554 //Is the weapon a toggle weapon
4555 WeaponToggle_t* weaponToggle;
4556 if(weaponToggles.Get(va("weapontoggle%d", num), &weaponToggle)) {
4558 int weaponToggleIndex = 0;
4560 //Find the current Weapon in the list
4561 int currentIndex = -1;
4562 for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
4563 if(weaponToggle->toggleList[i] == idealWeapon) {
4568 if(currentIndex == -1) {
4569 //Didn't find the current weapon so select the first item
4570 weaponToggleIndex = 0;
4572 //Roll to the next available item in the list
4573 weaponToggleIndex = currentIndex;
4574 weaponToggleIndex++;
4575 if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
4576 weaponToggleIndex = 0;
4580 for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
4583 if(inventory.weapons & ( 1 << weaponToggle->toggleList[weaponToggleIndex])) {
4587 weaponToggleIndex++;
4588 if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
4589 weaponToggleIndex = 0;
4593 num = weaponToggle->toggleList[weaponToggleIndex];
4597 if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
4599 if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
4601 if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
4605 if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
4606 weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
4608 if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
4610 if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
4614 idealWeapon = previousWeapon;
4615 } else if ( ( weapon_pda >= 0 ) && ( num == weapon_pda ) && ( inventory.pdas.Num() == 0 ) ) {
4616 ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
4627 idPlayer::DropWeapon
4630 void idPlayer::DropWeapon( bool died ) {
4632 int inclip, ammoavailable;
4634 assert( !gameLocal.isClient );
4636 if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
4640 if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
4643 // ammoavailable is how many shots we can fire
4644 // inclip is which amount is in clip right now
4645 ammoavailable = weapon.GetEntity()->AmmoAvailable();
4646 inclip = weapon.GetEntity()->AmmoInClip();
4648 // don't drop a grenade if we have none left
4649 if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
4654 ammoavailable += inclip;
4657 // expect an ammo setup that makes sense before doing any dropping
4658 // ammoavailable is -1 for infinite ammo, and weapons like chainsaw
4659 // a bad ammo config usually indicates a bad weapon state, so we should not drop
4660 // used to be an assertion check, but it still happens in edge cases
4662 if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
4663 common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
4666 idEntity *item = NULL;
4668 // ain't gonna throw you no weapon if I'm dead
4669 item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
4671 viewAngles.ToVectors( &forward, NULL, &up );
4672 item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
4677 // set the appropriate ammo in the dropped object
4678 const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
4680 item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
4681 idStr inclipKey = keyval->GetKey();
4682 inclipKey.Insert( "inclip_", 4 );
4684 inclipKey.Insert( va("%.2d", currentWeapon), 11);
4686 item->spawnArgs.SetInt( inclipKey, inclip );
4689 // remove from our local inventory completely
4690 inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
4691 weapon.GetEntity()->ResetAmmoClip();
4693 weapon.GetEntity()->WeaponStolen();
4700 idPlayer::StealWeapon
4701 steal the target player's current weapon
4704 void idPlayer::StealWeapon( idPlayer *player ) {
4705 assert( !gameLocal.isClient );
4707 // make sure there's something to steal
4708 idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
4709 if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
4712 // steal - we need to effectively force the other player to abandon his weapon
4713 int newweap = player->currentWeapon;
4714 if ( newweap == -1 ) {
4717 // might be just dropped - check inventory
4718 if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
4721 const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
4722 assert( weapon_classname );
4723 int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
4724 int inclip = player->weapon.GetEntity()->AmmoInClip();
4727 ammoavailable += inclip;
4730 if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
4732 common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
4733 // we still steal the weapon, so let's use the default ammo levels
4735 const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
4737 const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
4739 ammoavailable = atoi( keypair->GetValue() );
4742 player->weapon.GetEntity()->WeaponStolen();
4743 player->inventory.Drop( player->spawnArgs, NULL, newweap );
4744 player->SelectWeapon( weapon_fists, false );
4745 // in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
4746 // this will ensure the firing actually stops
4747 player->weaponGone = true;
4749 // give weapon, setup the ammo count
4750 Give( "weapon", weapon_classname );
4751 ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
4752 idealWeapon = newweap;
4753 inventory.ammo[ ammo_i ] += ammoavailable;
4756 inventory.clip[ newweap ] = inclip;
4765 idUserInterface *idPlayer::ActiveGui( void ) {
4766 if ( objectiveSystemOpen ) {
4767 return objectiveSystem;
4775 idPlayer::Weapon_Combat
4778 void idPlayer::Weapon_Combat( void ) {
4779 if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
4783 weapon.GetEntity()->RaiseWeapon();
4784 if ( weapon.GetEntity()->IsReloading() ) {
4787 SetState( "ReloadWeapon" );
4794 if ( idealWeapon == weapon_soulcube && soulCubeProjectile.GetEntity() != NULL ) {
4795 idealWeapon = currentWeapon;
4798 if ( idealWeapon != currentWeapon ) {
4799 if ( weaponCatchup ) {
4800 assert( gameLocal.isClient );
4802 currentWeapon = idealWeapon;
4804 animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
4805 weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
4806 animPrefix.Strip( "weapon_" );
4808 weapon.GetEntity()->NetCatchup();
4809 const function_t *newstate = GetScriptFunction( "NetCatchup" );
4811 SetState( newstate );
4814 weaponCatchup = false;
4816 if ( weapon.GetEntity()->IsReady() ) {
4817 weapon.GetEntity()->PutAway();
4820 if ( weapon.GetEntity()->IsHolstered() ) {
4821 assert( idealWeapon >= 0 );
4822 assert( idealWeapon < MAX_WEAPONS );
4824 if ( currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", currentWeapon ) ) ) {
4825 previousWeapon = currentWeapon;
4827 currentWeapon = idealWeapon;
4829 animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
4830 weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
4831 animPrefix.Strip( "weapon_" );
4833 weapon.GetEntity()->Raise();
4837 weaponGone = false; // if you drop and re-get weap, you may miss the = false above
4838 if ( weapon.GetEntity()->IsHolstered() ) {
4839 if ( !weapon.GetEntity()->AmmoAvailable() ) {
4840 // weapons can switch automatically if they have no more ammo
4843 weapon.GetEntity()->Raise();
4844 state = GetScriptFunction( "RaiseWeapon" );
4853 AI_WEAPON_FIRED = false;
4854 if ( !influenceActive ) {
4855 if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
4857 } else if ( oldButtons & BUTTON_ATTACK ) {
4858 AI_ATTACK_HELD = false;
4859 weapon.GetEntity()->EndAttack();
4863 // update our ammo clip in our inventory
4864 if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
4865 inventory.clip[ currentWeapon ] = weapon.GetEntity()->AmmoInClip();
4866 if ( hud && ( currentWeapon == idealWeapon ) ) {
4867 UpdateHudAmmo( hud );
4874 idPlayer::Weapon_NPC
4877 void idPlayer::Weapon_NPC( void ) {
4878 if ( idealWeapon != currentWeapon ) {
4882 weapon.GetEntity()->LowerWeapon();
4884 if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
4885 buttonMask |= BUTTON_ATTACK;
4886 focusCharacter->TalkTo( this );
4892 idPlayer::LowerWeapon
4895 void idPlayer::LowerWeapon( void ) {
4896 if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
4897 weapon.GetEntity()->LowerWeapon();
4903 idPlayer::RaiseWeapon
4906 void idPlayer::RaiseWeapon( void ) {
4907 if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
4908 weapon.GetEntity()->RaiseWeapon();
4914 idPlayer::WeaponLoweringCallback
4917 void idPlayer::WeaponLoweringCallback( void ) {
4918 SetState( "LowerWeapon" );
4924 idPlayer::WeaponRisingCallback
4927 void idPlayer::WeaponRisingCallback( void ) {
4928 SetState( "RaiseWeapon" );
4934 idPlayer::Weapon_GUI
4937 void idPlayer::Weapon_GUI( void ) {
4939 if ( !objectiveSystemOpen ) {
4940 if ( idealWeapon != currentWeapon ) {
4944 weapon.GetEntity()->LowerWeapon();
4947 // disable click prediction for the GUIs. handy to check the state sync does the right thing
4948 if ( gameLocal.isClient && !net_clientPredictGUI.GetBool() ) {
4952 if ( ( oldButtons ^ usercmd.buttons ) & BUTTON_ATTACK ) {
4954 const char *command = NULL;
4955 bool updateVisuals = false;
4957 idUserInterface *ui = ActiveGui();
4959 ev = sys->GenerateMouseButtonEvent( 1, ( usercmd.buttons & BUTTON_ATTACK ) != 0 );
4960 command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
4961 if ( updateVisuals && focusGUIent && ui == focusUI ) {
4962 focusGUIent->UpdateVisuals();
4965 if ( gameLocal.isClient ) {
4966 // we predict enough, but don't want to execute commands
4969 if ( focusGUIent ) {
4970 HandleGuiCommands( focusGUIent, command );
4972 HandleGuiCommands( this, command );
4979 idPlayer::UpdateWeapon
4982 void idPlayer::UpdateWeapon( void ) {
4983 if ( health <= 0 ) {
4987 assert( !spectating );
4989 if ( gameLocal.isClient ) {
4990 // clients need to wait till the weapon and it's world model entity
4991 // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
4992 if ( !weapon.GetEntity()->IsWorldModelReady() ) {
4997 // always make sure the weapon is correctly setup before accessing it
4998 if ( !weapon.GetEntity()->IsLinked() ) {
4999 if ( idealWeapon != -1 ) {
5000 animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
5001 weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ idealWeapon ] );
5002 assert( weapon.GetEntity()->IsLinked() );
5008 if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
5012 if ( g_dragEntity.GetBool() ) {
5014 weapon.GetEntity()->LowerWeapon();
5015 dragEntity.Update( this );
5016 } else if ( ActiveGui() ) {
5017 // gui handling overrides weapon use
5019 } else if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
5025 if ( hiddenWeapon ) {
5026 weapon.GetEntity()->LowerWeapon();
5029 // update weapon state, particles, dlights, etc
5030 weapon.GetEntity()->PresentWeapon( showWeaponViewModel );
5035 idPlayer::SpectateFreeFly
5038 void idPlayer::SpectateFreeFly( bool force ) {
5041 idVec3 spawn_origin;
5042 idAngles spawn_angles;
5044 player = gameLocal.GetClientByNum( spectator );
5045 if ( force || gameLocal.time > lastSpectateChange ) {
5046 spectator = entityNumber;
5047 if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
5048 newOrig = player->GetPhysics()->GetOrigin();
5049 if ( player->physicsObj.IsCrouching() ) {
5050 newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
5052 newOrig[ 2 ] += pm_normalviewheight.GetFloat();
5054 newOrig[ 2 ] += SPECTATE_RAISE;
5055 idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
5056 idVec3 start = player->GetPhysics()->GetOrigin();
5057 start[2] += pm_spectatebbox.GetFloat() * 0.5f;
5059 // assuming spectate bbox is inside stand or crouch box
5060 gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
5061 newOrig.Lerp( start, newOrig, t.fraction );
5062 SetOrigin( newOrig );
5063 idAngles angle = player->viewAngles;
5065 SetViewAngles( angle );
5067 SelectInitialSpawnPoint( spawn_origin, spawn_angles );
5068 spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
5069 spawn_origin[ 2 ] += SPECTATE_RAISE;
5070 SetOrigin( spawn_origin );
5071 SetViewAngles( spawn_angles );
5073 lastSpectateChange = gameLocal.time + 500;
5079 idPlayer::SpectateCycle
5082 void idPlayer::SpectateCycle( void ) {
5085 if ( gameLocal.time > lastSpectateChange ) {
5086 int latchedSpectator = spectator;
5087 spectator = gameLocal.GetNextClientNum( spectator );
5088 player = gameLocal.GetClientByNum( spectator );
5089 assert( player ); // never call here when the current spectator is wrong
5090 // ignore other spectators
5091 while ( latchedSpectator != spectator && player->spectating ) {
5092 spectator = gameLocal.GetNextClientNum( spectator );
5093 player = gameLocal.GetClientByNum( spectator );
5095 lastSpectateChange = gameLocal.time + 500;
5101 idPlayer::UpdateSpectating
5104 void idPlayer::UpdateSpectating( void ) {
5105 assert( spectating );
5106 assert( !gameLocal.isClient );
5107 assert( IsHidden() );
5109 if ( !gameLocal.isMultiplayer ) {
5112 player = gameLocal.GetClientByNum( spectator );
5113 if ( !player || ( player->spectating && player != this ) ) {
5114 SpectateFreeFly( true );
5115 } else if ( usercmd.upmove > 0 ) {
5116 SpectateFreeFly( false );
5117 } else if ( usercmd.buttons & BUTTON_ATTACK ) {
5124 idPlayer::HandleSingleGuiCommand
5127 bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
5130 if ( !src->ReadToken( &token ) ) {
5134 if ( token == ";" ) {
5138 if ( token.Icmp( "addhealth" ) == 0 ) {
5139 if ( entityGui && health < 100 ) {
5140 int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
5141 int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
5143 entityGui->spawnArgs.SetInt( "gui_parm1", _health );
5144 if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
5145 entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
5148 if ( health > 100 ) {
5155 if ( token.Icmp( "ready" ) == 0 ) {
5156 PerformImpulse( IMPULSE_17 );
5160 if ( token.Icmp( "updatepda" ) == 0 ) {
5161 UpdatePDAInfo( true );
5165 if ( token.Icmp( "updatepda2" ) == 0 ) {
5166 UpdatePDAInfo( false );
5170 if ( token.Icmp( "stoppdavideo" ) == 0 ) {
5171 if ( objectiveSystem && objectiveSystemOpen && pdaVideoWave.Length() > 0 ) {
5172 StopSound( SND_CHANNEL_PDA, false );
5177 if ( token.Icmp( "close" ) == 0 ) {
5178 if ( objectiveSystem && objectiveSystemOpen ) {
5183 if ( token.Icmp( "playpdavideo" ) == 0 ) {
5184 if ( objectiveSystem && objectiveSystemOpen && pdaVideo.Length() > 0 ) {
5185 const idMaterial *mat = declManager->FindMaterial( pdaVideo );
5187 int c = mat->GetNumStages();
5188 for ( int i = 0; i < c; i++ ) {
5189 const shaderStage_t *stage = mat->GetStage(i);
5190 if ( stage && stage->texture.cinematic ) {
5191 stage->texture.cinematic->ResetTime( gameLocal.time );
5194 if ( pdaVideoWave.Length() ) {
5195 const idSoundShader *shader = declManager->FindSound( pdaVideoWave );
5196 StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, NULL );
5202 if ( token.Icmp( "playpdaaudio" ) == 0 ) {
5203 if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
5204 const idSoundShader *shader = declManager->FindSound( pdaAudio );
5206 StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, &ms );
5208 CancelEvents( &EV_Player_StopAudioLog );
5209 PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
5214 if ( token.Icmp( "stoppdaaudio" ) == 0 ) {
5215 if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
5216 // idSoundShader *shader = declManager->FindSound( pdaAudio );
5218 StopSound( SND_CHANNEL_PDA, false );
5223 src->UnreadToken( &token );
5232 bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
5235 if ( gameLocal.isClient ) {
5239 other = gameLocal.entities[ collision.c.entityNum ];
5241 other->Signal( SIG_TOUCH );
5242 if ( !spectating ) {
5243 if ( other->RespondsTo( EV_Touch ) ) {
5244 other->ProcessEvent( &EV_Touch, this, &collision );
5247 if ( other->RespondsTo( EV_SpectatorTouch ) ) {
5248 other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
5258 idPlayer::UpdateLocation
5260 Searches nearby locations
5263 void idPlayer::UpdateLocation( void ) {
5265 idLocationEntity *locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
5266 if ( locationEntity ) {
5267 hud->SetStateString( "location", locationEntity->GetLocation() );
5269 hud->SetStateString( "location", common->GetLanguageDict()->GetString( "#str_02911" ) );
5276 idPlayer::ClearFocus
5278 Clears the focus cursor
5281 void idPlayer::ClearFocus( void ) {
5282 focusCharacter = NULL;
5285 focusVehicle = NULL;
5291 idPlayer::UpdateFocus
5293 Searches nearby entities for interactive guis, possibly making one of them
5294 the focus and sending it a mouse move event
5297 void idPlayer::UpdateFocus( void ) {
5298 idClipModel *clipModelList[ MAX_GENTITIES ];
5300 int listedClipModels;
5303 idUserInterface *oldUI;
5306 idAFEntity_Vehicle *oldVehicle;
5310 const char *command;
5313 const idKeyValue *kv;
5315 idUserInterface *ui;
5317 if ( gameLocal.inCinematic ) {
5321 // only update the focus character when attack button isn't pressed so players
5322 // can still chainsaw NPC's
5323 if ( gameLocal.isMultiplayer || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
5329 oldFocus = focusGUIent;
5331 oldChar = focusCharacter;
5332 oldTalkCursor = talkCursor;
5333 oldVehicle = focusVehicle;
5335 if ( focusTime <= gameLocal.time ) {
5339 // don't let spectators interact with GUIs
5344 start = GetEyePosition();
5345 end = start + viewAngles.ToForward() * 80.0f;
5347 // player identification -> names to the hud
5348 if ( gameLocal.isMultiplayer && entityNumber == gameLocal.localClientNum ) {
5349 idVec3 end = start + viewAngles.ToForward() * 768.0f;
5350 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
5352 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
5353 iclient = trace.c.entityNum;
5355 if ( MPAim != iclient ) {
5358 lastMPAimTime = gameLocal.realClientTime;
5362 idBounds bounds( start );
5363 bounds.AddPoint( end );
5365 listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
5367 // no pretense at sorting here, just assume that there will only be one active
5368 // gui within range along the trace
5369 for ( i = 0; i < listedClipModels; i++ ) {
5370 clip = clipModelList[ i ];
5371 ent = clip->GetEntity();
5373 if ( ent->IsHidden() ) {
5378 if ( ent->IsType( idAFAttachment::Type ) ) {
5379 idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
5380 if ( body && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
5381 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5382 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5384 focusCharacter = static_cast<idAI *>( body );
5386 focusTime = gameLocal.time + FOCUS_TIME;
5393 if ( ent->IsType( idAI::Type ) ) {
5394 if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
5395 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5396 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5398 focusCharacter = static_cast<idAI *>( ent );
5400 focusTime = gameLocal.time + FOCUS_TIME;
5407 if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
5408 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
5409 if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
5411 focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
5412 focusTime = gameLocal.time + FOCUS_TIME;
5419 if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
5423 if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
5424 // don't allow guis on pickup items focus
5428 pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
5431 renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
5432 if ( !focusGUIrenderEntity ) {
5436 if ( pt.guiId == 1 ) {
5437 ui = focusGUIrenderEntity->gui[ 0 ];
5438 } else if ( pt.guiId == 2 ) {
5439 ui = focusGUIrenderEntity->gui[ 1 ];
5441 ui = focusGUIrenderEntity->gui[ 2 ];
5452 if ( oldFocus != ent ) {
5454 // going to see if we have anything in inventory a gui might be interested in
5455 // need to enumerate inventory items
5456 focusUI->SetStateInt( "inv_count", inventory.items.Num() );
5457 for ( j = 0; j < inventory.items.Num(); j++ ) {
5458 idDict *item = inventory.items[ j ];
5459 const char *iname = item->GetString( "inv_name" );
5460 const char *iicon = item->GetString( "inv_icon" );
5461 const char *itext = item->GetString( "inv_text" );
5463 focusUI->SetStateString( va( "inv_name_%i", j), iname );
5464 focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
5465 focusUI->SetStateString( va( "inv_text_%i", j), itext );
5466 kv = item->MatchPrefix("inv_id", NULL);
5468 focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
5470 focusUI->SetStateInt( iname, 1 );
5474 for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
5475 const char *p = inventory.pdaSecurity[ j ];
5477 focusUI->SetStateInt( p, 1 );
5481 #ifdef _D3XP //BSM: Added for powercells
5482 int powerCellCount = 0;
5483 for ( j = 0; j < inventory.items.Num(); j++ ) {
5484 idDict *item = inventory.items[ j ];
5485 if(item->GetInt("inv_powercell")) {
5489 focusUI->SetStateInt( "powercell_count", powerCellCount );
5492 int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
5493 focusUI->SetStateString( "player_health", va("%i", health ) );
5494 focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
5495 focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
5497 kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
5499 focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
5500 kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
5504 // clamp the mouse to the corner
5505 ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
5506 command = focusUI->HandleEvent( &ev, gameLocal.time );
5507 HandleGuiCommands( focusGUIent, command );
5509 // move to an absolute position
5510 ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
5511 command = focusUI->HandleEvent( &ev, gameLocal.time );
5512 HandleGuiCommands( focusGUIent, command );
5513 focusTime = gameLocal.time + FOCUS_GUI_TIME;
5518 if ( focusGUIent && focusUI ) {
5519 if ( !oldFocus || oldFocus != focusGUIent ) {
5520 command = focusUI->Activate( true, gameLocal.time );
5521 HandleGuiCommands( focusGUIent, command );
5522 StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
5526 } else if ( oldFocus && oldUI ) {
5527 command = oldUI->Activate( false, gameLocal.time );
5528 HandleGuiCommands( oldFocus, command );
5529 StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
5532 if ( cursor && ( oldTalkCursor != talkCursor ) ) {
5533 cursor->SetStateInt( "talkcursor", talkCursor );
5536 if ( oldChar != focusCharacter && hud ) {
5537 if ( focusCharacter ) {
5538 hud->SetStateString( "npc", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
5540 //Use to code to update the npc action string to fix bug 1159
5541 hud->SetStateString( "npc_action", common->GetLanguageDict()->GetString( "#str_02036" ));
5543 hud->HandleNamedEvent( "showNPC" );
5547 hud->SetStateString( "npc", "" );
5549 hud->SetStateString( "npc_action", "" );
5551 hud->HandleNamedEvent( "hideNPC" );
5560 Check for hard landings that generate sound events
5563 void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
5564 idVec3 origin, velocity;
5565 idVec3 gravityVector, gravityNormal;
5567 float hardDelta, fatalDelta;
5572 waterLevel_t waterLevel;
5575 AI_SOFTLANDING = false;
5576 AI_HARDLANDING = false;
5578 // if the player is not on the ground
5579 if ( !physicsObj.HasGroundContacts() ) {
5583 gravityNormal = physicsObj.GetGravityNormal();
5585 // if the player wasn't going down
5586 if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
5590 waterLevel = physicsObj.GetWaterLevel();
5592 // never take falling damage if completely underwater
5593 if ( waterLevel == WATERLEVEL_HEAD ) {
5597 // no falling damage if touching a nodamage surface
5599 for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
5600 const contactInfo_t &contact = physicsObj.GetContact( i );
5601 if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
5603 StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
5608 origin = GetPhysics()->GetOrigin();
5609 gravityVector = physicsObj.GetGravity();
5611 // calculate the exact velocity on landing
5612 dist = ( origin - oldOrigin ) * -gravityNormal;
5613 vel = oldVelocity * -gravityNormal;
5614 acc = -gravityVector.Length();
5620 den = b * b - 4.0f * a * c;
5624 t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
5626 delta = vel + t * acc;
5627 delta = delta * delta * 0.0001;
5629 // reduce falling damage if there is standing water
5630 if ( waterLevel == WATERLEVEL_WAIST ) {
5633 if ( waterLevel == WATERLEVEL_FEET ) {
5637 if ( delta < 1.0f ) {
5641 // allow falling a bit further for multiplayer
5642 if ( gameLocal.isMultiplayer ) {
5650 if ( delta > fatalDelta ) {
5651 AI_HARDLANDING = true;
5653 landTime = gameLocal.time;
5655 pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
5656 Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
5658 } else if ( delta > hardDelta ) {
5659 AI_HARDLANDING = true;
5661 landTime = gameLocal.time;
5663 pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
5664 Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
5666 } else if ( delta > 30 ) {
5667 AI_HARDLANDING = true;
5669 landTime = gameLocal.time;
5671 pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
5672 Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
5674 } else if ( delta > 7 ) {
5675 AI_SOFTLANDING = true;
5677 landTime = gameLocal.time;
5678 } else if ( delta > 3 ) {
5688 void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
5691 idVec3 vel, gravityDir, velocity;
5699 // calculate speed and cycle to be used for
5700 // all cyclic walking effects
5702 velocity = physicsObj.GetLinearVelocity() - pushVelocity;
5704 gravityDir = physicsObj.GetGravityNormal();
5705 vel = velocity - ( velocity * gravityDir ) * gravityDir;
5706 xyspeed = vel.LengthFast();
5708 // do not evaluate the bob for other clients
5709 // when doing a spectate follow, don't do any weapon bobbing
5710 if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
5711 viewBobAngles.Zero();
5716 if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( gameLocal.isMultiplayer && spectating ) ) {
5721 } else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
5722 // start at beginning of cycle again
5727 if ( physicsObj.IsCrouching() ) {
5728 bobmove = pm_crouchbob.GetFloat();
5729 // ducked characters never play footsteps
5731 // vary the bobbing based on the speed of the player
5732 bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
5735 // check for footstep / splash sounds
5737 bobCycle = (int)( old + bobmove * gameLocal.msec ) & 255;
5738 bobFoot = ( bobCycle & 128 ) >> 7;
5739 bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
5742 // calculate angles for view bobbing
5743 viewBobAngles.Zero();
5745 viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
5747 // add angles based on velocity
5748 delta = velocity * viewaxis[0];
5749 viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
5751 delta = velocity * viewaxis[1];
5752 viewBobAngles.roll -= delta * pm_runroll.GetFloat();
5754 // add angles based on bob
5755 // make sure the bob is visible even at low speeds
5756 speed = xyspeed > 200 ? xyspeed : 200;
5758 delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
5759 if ( physicsObj.IsCrouching() ) {
5760 delta *= 3; // crouching
5762 viewBobAngles.pitch += delta;
5763 delta = bobfracsin * pm_bobroll.GetFloat() * speed;
5764 if ( physicsObj.IsCrouching() ) {
5765 delta *= 3; // crouching accentuates roll
5767 if ( bobFoot & 1 ) {
5770 viewBobAngles.roll += delta;
5772 // calculate position for view bobbing
5775 if ( physicsObj.HasSteppedUp() ) {
5777 // check for stepping up before a previous step is completed
5778 deltaTime = gameLocal.time - stepUpTime;
5779 if ( deltaTime < STEPUP_TIME ) {
5780 stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
5782 stepUpDelta = physicsObj.GetStepUp();
5784 if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
5785 stepUpDelta = 2.0f * pm_stepsize.GetFloat();
5787 stepUpTime = gameLocal.time;
5790 idVec3 gravity = physicsObj.GetGravityNormal();
5792 // if the player stepped up recently
5793 deltaTime = gameLocal.time - stepUpTime;
5794 if ( deltaTime < STEPUP_TIME ) {
5795 viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
5798 // add bob height after any movement smoothing
5799 bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
5806 delta = gameLocal.time - landTime;
5807 if ( delta < LAND_DEFLECT_TIME ) {
5808 f = delta / LAND_DEFLECT_TIME;
5809 viewBob -= gravity * ( landChange * f );
5810 } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
5811 delta -= LAND_DEFLECT_TIME;
5812 f = 1.0 - ( delta / LAND_RETURN_TIME );
5813 viewBob -= gravity * ( landChange * f );
5819 idPlayer::UpdateDeltaViewAngles
5822 void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
5823 // set the delta angle
5825 for( int i = 0; i < 3; i++ ) {
5826 delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
5828 SetDeltaViewAngles( delta );
5833 idPlayer::SetViewAngles
5836 void idPlayer::SetViewAngles( const idAngles &angles ) {
5837 UpdateDeltaViewAngles( angles );
5838 viewAngles = angles;
5843 idPlayer::UpdateViewAngles
5846 void idPlayer::UpdateViewAngles( void ) {
5850 if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
5851 // no view changes at all, but we still want to update the deltas or else when
5852 // we get out of this mode, our view will snap to a kind of random angle
5853 UpdateDeltaViewAngles( viewAngles );
5858 if ( health <= 0 ) {
5859 if ( pm_thirdPersonDeath.GetBool() ) {
5860 viewAngles.roll = 0.0f;
5861 viewAngles.pitch = 30.0f;
5863 viewAngles.roll = 40.0f;
5864 viewAngles.pitch = -15.0f;
5869 // circularly clamp the angles with deltas
5870 for ( i = 0; i < 3; i++ ) {
5871 cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
5872 if ( influenceActive == INFLUENCE_LEVEL3 ) {
5873 viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
5875 viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] );
5878 if ( !centerView.IsDone( gameLocal.time ) ) {
5879 viewAngles.pitch = centerView.GetCurrentValue(gameLocal.time);
5884 if ( viewAngles.pitch > 89.0f ) {
5885 // don't let the player look down more than 89 degrees while noclipping
5886 viewAngles.pitch = 89.0f;
5887 } else if ( viewAngles.pitch < -89.0f ) {
5888 // don't let the player look up more than 89 degrees while noclipping
5889 viewAngles.pitch = -89.0f;
5892 } else if ( mountedObject ) {
5893 int yaw_min, yaw_max, varc;
5895 mountedObject->GetAngleRestrictions( yaw_min, yaw_max, varc );
5897 if ( yaw_min < yaw_max ) {
5898 viewAngles.yaw = idMath::ClampFloat( yaw_min, yaw_max, viewAngles.yaw );
5900 if ( viewAngles.yaw < 0 ) {
5901 viewAngles.yaw = idMath::ClampFloat( -180.f, yaw_max, viewAngles.yaw );
5903 viewAngles.yaw = idMath::ClampFloat( yaw_min, 180.f, viewAngles.yaw );
5906 viewAngles.pitch = idMath::ClampFloat( -varc, varc, viewAngles.pitch );
5909 if ( viewAngles.pitch > pm_maxviewpitch.GetFloat() ) {
5910 // don't let the player look down enough to see the shadow of his (non-existant) feet
5911 viewAngles.pitch = pm_maxviewpitch.GetFloat();
5912 } else if ( viewAngles.pitch < pm_minviewpitch.GetFloat() ) {
5913 // don't let the player look up more than 89 degrees
5914 viewAngles.pitch = pm_minviewpitch.GetFloat();
5918 UpdateDeltaViewAngles( viewAngles );
5920 // orient the model towards the direction we're looking
5921 SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
5923 // save in the log for analyzing weapon angle offsets
5924 loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
5929 idPlayer::AdjustHeartRate
5931 Player heartrate works as follows
5933 DEF_HEARTRATE is resting heartrate
5935 Taking damage when health is above 75 adjusts heart rate by 1 beat per second
5936 Taking damage when health is below 75 adjusts heart rate by 5 beats per second
5937 Maximum heartrate from damage is MAX_HEARTRATE
5939 Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
5941 Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
5943 All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
5944 Once it starts falling it always tries to get to DEF_HEARTRATE
5946 The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling
5949 Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is
5950 scaled linearly based on the actual rate
5952 Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
5953 it is audible or -10db and scales to 8db on the last few beats
5956 void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
5958 if ( heartInfo.GetEndValue() == target ) {
5962 if ( AI_DEAD && !force ) {
5966 lastHeartAdjust = gameLocal.time;
5968 heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
5973 idPlayer::GetBaseHeartRate
5976 int idPlayer::GetBaseHeartRate( void ) {
5977 int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
5978 int rate = idMath::FtoiFast( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
5979 int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
5980 rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
5986 idPlayer::SetCurrentHeartRate
5989 void idPlayer::SetCurrentHeartRate( void ) {
5991 int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
5993 if ( PowerUpActive( ADRENALINE )) {
5996 heartRate = idMath::FtoiFast( heartInfo.GetCurrentValue( gameLocal.time ) );
5997 int currentRate = GetBaseHeartRate();
5998 if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
5999 AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
6003 int bps = idMath::FtoiFast( 60.0f / heartRate * 1000.0f );
6004 if ( gameLocal.time - lastHeartBeat > bps ) {
6005 int dmgVol = DMG_VOLUME;
6006 int deathVol = DEATH_VOLUME;
6007 int zeroVol = ZERO_VOLUME;
6009 if ( heartRate > BASE_HEARTRATE && health > 0 ) {
6010 pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
6011 pct *= ((float)dmgVol - (float)zeroVol);
6012 } else if ( health <= 0 ) {
6013 pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
6016 } else if (pct < 0.0f) {
6019 pct *= ((float)deathVol - (float)zeroVol);
6022 pct += (float)zeroVol;
6024 if ( pct != zeroVol ) {
6025 StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
6026 // modify just this channel to a custom volume
6027 soundShaderParms_t parms;
6028 memset( &parms, 0, sizeof( parms ) );
6030 refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
6033 lastHeartBeat = gameLocal.time;
6042 void idPlayer::UpdateAir( void ) {
6043 if ( health <= 0 ) {
6047 // see if the player is connected to the info_vacuum
6048 bool newAirless = false;
6050 if ( gameLocal.vacuumAreaNum != -1 ) {
6051 int num = GetNumPVSAreas();
6055 // if the player box spans multiple areas, get the area from the origin point instead,
6056 // otherwise a rotating player box may poke into an outside area
6058 const int *pvsAreas = GetPVSAreas();
6059 areaNum = pvsAreas[0];
6061 areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
6063 newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
6068 if ( PowerUpActive( ENVIROTIME ) ) {
6075 StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
6076 StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
6078 hud->HandleNamedEvent( "noAir" );
6082 if ( airTics < 0 ) {
6085 const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
6086 int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
6087 if ( gameLocal.time > lastAirDamage + dmgTiming ) {
6088 Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
6089 lastAirDamage = gameLocal.time;
6095 StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
6096 StopSound( SND_CHANNEL_BODY2, false );
6098 hud->HandleNamedEvent( "Air" );
6101 airTics+=2; // regain twice as fast as lose
6102 if ( airTics > pm_airTics.GetInteger() ) {
6103 airTics = pm_airTics.GetInteger();
6107 airless = newAirless;
6110 hud->SetStateInt( "player_air", 100 * airTics / pm_airTics.GetInteger() );
6114 void idPlayer::UpdatePowerupHud() {
6116 if ( health <= 0 ) {
6120 if(lastHudPowerup != hudPowerup) {
6122 if(hudPowerup == -1) {
6123 //The powerup hud should be turned off
6125 hud->HandleNamedEvent( "noPowerup" );
6128 //Turn the pwoerup hud on
6130 hud->HandleNamedEvent( "Powerup" );
6134 lastHudPowerup = hudPowerup;
6137 if(hudPowerup != -1) {
6138 if(PowerUpActive(hudPowerup)) {
6139 int remaining = inventory.powerupEndTime[ hudPowerup ] - gameLocal.time;
6140 int filledbar = idMath::ClampInt( 0, hudPowerupDuration, remaining );
6143 hud->SetStateInt( "player_powerup", 100 * filledbar / hudPowerupDuration );
6144 hud->SetStateInt( "player_poweruptime", remaining / 1000 );
6152 idPlayer::AddGuiPDAData
6155 int idPlayer::AddGuiPDAData( const declType_t dataType, const char *listName, const idDeclPDA *src, idUserInterface *gui ) {
6158 if ( dataType == DECL_EMAIL ) {
6159 c = src->GetNumEmails();
6160 for ( i = 0; i < c; i++ ) {
6161 const idDeclEmail *email = src->GetEmailByIndex( i );
6162 if ( email == NULL ) {
6163 work = va( "-\tEmail %d not found\t-", i );
6165 work = email->GetFrom();
6167 work += email->GetSubject();
6169 work += email->GetDate();
6171 gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6174 } else if ( dataType == DECL_AUDIO ) {
6175 c = src->GetNumAudios();
6176 for ( i = 0; i < c; i++ ) {
6177 const idDeclAudio *audio = src->GetAudioByIndex( i );
6178 if ( audio == NULL ) {
6179 work = va( "Audio Log %d not found", i );
6181 work = audio->GetAudioName();
6183 gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6186 } else if ( dataType == DECL_VIDEO ) {
6187 c = inventory.videos.Num();
6188 for ( i = 0; i < c; i++ ) {
6189 const idDeclVideo *video = GetVideo( i );
6190 if ( video == NULL ) {
6191 work = va( "Video CD %s not found", inventory.videos[i].c_str() );
6193 work = video->GetVideoName();
6195 gui->SetStateString( va( "%s_item_%i", listName, i ), work );
6207 const idDeclPDA *idPlayer::GetPDA( void ) const {
6208 if ( inventory.pdas.Num() ) {
6209 return static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[ 0 ] ) );
6221 const idDeclVideo *idPlayer::GetVideo( int index ) {
6222 if ( index >= 0 && index < inventory.videos.Num() ) {
6223 return static_cast< const idDeclVideo* >( declManager->FindType( DECL_VIDEO, inventory.videos[index], false ) );
6231 idPlayer::UpdatePDAInfo
6234 void idPlayer::UpdatePDAInfo( bool updatePDASel ) {
6237 if ( objectiveSystem == NULL ) {
6243 int currentPDA = objectiveSystem->State().GetInt( "listPDA_sel_0", "0" );
6244 if ( currentPDA == -1 ) {
6248 if ( updatePDASel ) {
6249 objectiveSystem->SetStateInt( "listPDAVideo_sel_0", 0 );
6250 objectiveSystem->SetStateInt( "listPDAEmail_sel_0", 0 );
6251 objectiveSystem->SetStateInt( "listPDAAudio_sel_0", 0 );
6254 if ( currentPDA > 0 ) {
6255 currentPDA = inventory.pdas.Num() - currentPDA;
6258 // Mark in the bit array that this pda has been read
6259 if ( currentPDA < 128 ) {
6260 inventory.pdasViewed[currentPDA >> 5] |= 1 << (currentPDA & 31);
6266 idStr name, data, preview, info, wave;
6267 for ( j = 0; j < MAX_PDAS; j++ ) {
6268 objectiveSystem->SetStateString( va( "listPDA_item_%i", j ), "" );
6270 for ( j = 0; j < MAX_PDA_ITEMS; j++ ) {
6271 objectiveSystem->SetStateString( va( "listPDAVideo_item_%i", j ), "" );
6272 objectiveSystem->SetStateString( va( "listPDAAudio_item_%i", j ), "" );
6273 objectiveSystem->SetStateString( va( "listPDAEmail_item_%i", j ), "" );
6274 objectiveSystem->SetStateString( va( "listPDASecurity_item_%i", j ), "" );
6276 for ( j = 0; j < inventory.pdas.Num(); j++ ) {
6278 const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[j], false ) );
6280 if ( pda == NULL ) {
6284 int index = inventory.pdas.Num() - j;
6286 // Special case for the first PDA
6290 if ( j != currentPDA && j < 128 && inventory.pdasViewed[j >> 5] & (1 << (j & 31)) ) {
6291 // This pda has been read already, mark in gray
6292 objectiveSystem->SetStateString( va( "listPDA_item_%i", index), va(S_COLOR_GRAY "%s", pda->GetPdaName()) );
6294 // This pda has not been read yet
6295 objectiveSystem->SetStateString( va( "listPDA_item_%i", index), pda->GetPdaName() );
6298 const char *security = pda->GetSecurity();
6299 if ( j == currentPDA || (currentPDA == 0 && security && *security ) ) {
6300 if ( *security == NULL ) {
6301 security = common->GetLanguageDict()->GetString( "#str_00066" );
6303 objectiveSystem->SetStateString( "PDASecurityClearance", security );
6306 if ( j == currentPDA ) {
6308 objectiveSystem->SetStateString( "pda_icon", pda->GetIcon() );
6309 objectiveSystem->SetStateString( "pda_id", pda->GetID() );
6310 objectiveSystem->SetStateString( "pda_title", pda->GetTitle() );
6313 // Selected, personal pda
6315 if ( updatePDASel || !inventory.pdaOpened ) {
6316 objectiveSystem->HandleNamedEvent( "playerPDAActive" );
6317 objectiveSystem->SetStateString( "pda_personal", "1" );
6318 inventory.pdaOpened = true;
6320 objectiveSystem->SetStateString( "pda_location", hud->State().GetString("location") );
6321 objectiveSystem->SetStateString( "pda_name", cvarSystem->GetCVarString( "ui_name") );
6322 AddGuiPDAData( DECL_VIDEO, "listPDAVideo", pda, objectiveSystem );
6323 sel = objectiveSystem->State().GetInt( "listPDAVideo_sel_0", "0" );
6324 const idDeclVideo *vid = NULL;
6325 if ( sel >= 0 && sel < inventory.videos.Num() ) {
6326 vid = static_cast< const idDeclVideo * >( declManager->FindType( DECL_VIDEO, inventory.videos[ sel ], false ) );
6329 pdaVideo = vid->GetRoq();
6330 pdaVideoWave = vid->GetWave();
6331 objectiveSystem->SetStateString( "PDAVideoTitle", vid->GetVideoName() );
6332 objectiveSystem->SetStateString( "PDAVideoVid", vid->GetRoq() );
6333 objectiveSystem->SetStateString( "PDAVideoIcon", vid->GetPreview() );
6334 objectiveSystem->SetStateString( "PDAVideoInfo", vid->GetInfo() );
6336 //FIXME: need to precache these in the player def
6337 objectiveSystem->SetStateString( "PDAVideoVid", "sound/vo/video/welcome.tga" );
6338 objectiveSystem->SetStateString( "PDAVideoIcon", "sound/vo/video/welcome.tga" );
6339 objectiveSystem->SetStateString( "PDAVideoTitle", "" );
6340 objectiveSystem->SetStateString( "PDAVideoInfo", "" );
6343 // Selected, non-personal pda
6345 if ( updatePDASel ) {
6346 objectiveSystem->HandleNamedEvent( "playerPDANotActive" );
6347 objectiveSystem->SetStateString( "pda_personal", "0" );
6348 inventory.pdaOpened = true;
6350 objectiveSystem->SetStateString( "pda_location", pda->GetPost() );
6351 objectiveSystem->SetStateString( "pda_name", pda->GetFullName() );
6352 int audioCount = AddGuiPDAData( DECL_AUDIO, "listPDAAudio", pda, objectiveSystem );
6353 objectiveSystem->SetStateInt( "audioLogCount", audioCount );
6354 sel = objectiveSystem->State().GetInt( "listPDAAudio_sel_0", "0" );
6355 const idDeclAudio *aud = NULL;
6357 aud = pda->GetAudioByIndex( sel );
6360 pdaAudio = aud->GetWave();
6361 objectiveSystem->SetStateString( "PDAAudioTitle", aud->GetAudioName() );
6362 objectiveSystem->SetStateString( "PDAAudioIcon", aud->GetPreview() );
6363 objectiveSystem->SetStateString( "PDAAudioInfo", aud->GetInfo() );
6365 objectiveSystem->SetStateString( "PDAAudioIcon", "sound/vo/video/welcome.tga" );
6366 objectiveSystem->SetStateString( "PDAAutioTitle", "" );
6367 objectiveSystem->SetStateString( "PDAAudioInfo", "" );
6373 int numEmails = pda->GetNumEmails();
6374 if ( numEmails > 0 ) {
6375 AddGuiPDAData( DECL_EMAIL, "listPDAEmail", pda, objectiveSystem );
6376 sel = objectiveSystem->State().GetInt( "listPDAEmail_sel_0", "-1" );
6377 if ( sel >= 0 && sel < numEmails ) {
6378 const idDeclEmail *email = pda->GetEmailByIndex( sel );
6379 name = email->GetSubject();
6380 data = email->GetBody();
6383 objectiveSystem->SetStateString( "PDAEmailTitle", name );
6384 objectiveSystem->SetStateString( "PDAEmailText", data );
6387 if ( objectiveSystem->State().GetInt( "listPDA_sel_0", "-1" ) == -1 ) {
6388 objectiveSystem->SetStateInt( "listPDA_sel_0", 0 );
6390 objectiveSystem->StateChanged( gameLocal.time );
6398 void idPlayer::TogglePDA( void ) {
6399 if ( objectiveSystem == NULL ) {
6403 if ( inventory.pdas.Num() == 0 ) {
6404 ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
6410 if ( !objectiveSystemOpen ) {
6411 int j, c = inventory.items.Num();
6412 objectiveSystem->SetStateInt( "inv_count", c );
6413 for ( j = 0; j < MAX_INVENTORY_ITEMS; j++ ) {
6414 objectiveSystem->SetStateString( va( "inv_name_%i", j ), "" );
6415 objectiveSystem->SetStateString( va( "inv_icon_%i", j ), "" );
6416 objectiveSystem->SetStateString( va( "inv_text_%i", j ), "" );
6418 for ( j = 0; j < c; j++ ) {
6419 idDict *item = inventory.items[j];
6420 if ( !item->GetBool( "inv_pda" ) ) {
6421 const char *iname = item->GetString( "inv_name" );
6422 const char *iicon = item->GetString( "inv_icon" );
6423 const char *itext = item->GetString( "inv_text" );
6424 objectiveSystem->SetStateString( va( "inv_name_%i", j ), iname );
6425 objectiveSystem->SetStateString( va( "inv_icon_%i", j ), iicon );
6426 objectiveSystem->SetStateString( va( "inv_text_%i", j ), itext );
6427 const idKeyValue *kv = item->MatchPrefix( "inv_id", NULL );
6429 objectiveSystem->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
6434 for ( j = 0; j < MAX_WEAPONS; j++ ) {
6435 const char *weapnum = va( "def_weapon%d", j );
6436 const char *hudWeap = va( "weapon%d", j );
6438 if ( inventory.weapons & ( 1 << j ) ) {
6439 const char *weap = spawnArgs.GetString( weapnum );
6440 if ( weap && *weap ) {
6444 objectiveSystem->SetStateInt( hudWeap, weapstate );
6447 objectiveSystem->SetStateInt( "listPDA_sel_0", inventory.selPDA );
6448 objectiveSystem->SetStateInt( "listPDAVideo_sel_0", inventory.selVideo );
6449 objectiveSystem->SetStateInt( "listPDAAudio_sel_0", inventory.selAudio );
6450 objectiveSystem->SetStateInt( "listPDAEmail_sel_0", inventory.selEMail );
6451 UpdatePDAInfo( false );
6452 UpdateObjectiveInfo();
6453 objectiveSystem->Activate( true, gameLocal.time );
6454 hud->HandleNamedEvent( "pdaPickupHide" );
6455 hud->HandleNamedEvent( "videoPickupHide" );
6457 inventory.selPDA = objectiveSystem->State().GetInt( "listPDA_sel_0" );
6458 inventory.selVideo = objectiveSystem->State().GetInt( "listPDAVideo_sel_0" );
6459 inventory.selAudio = objectiveSystem->State().GetInt( "listPDAAudio_sel_0" );
6460 inventory.selEMail = objectiveSystem->State().GetInt( "listPDAEmail_sel_0" );
6461 objectiveSystem->Activate( false, gameLocal.time );
6463 objectiveSystemOpen ^= 1;
6468 idPlayer::ToggleScoreboard
6471 void idPlayer::ToggleScoreboard( void ) {
6472 scoreBoardOpen ^= 1;
6480 void idPlayer::Spectate( bool spectate ) {
6482 byte msgBuf[MAX_EVENT_PARAM_SIZE];
6484 // track invisible player bug
6485 // all hiding and showing should be performed through Spectate calls
6486 // except for the private camera view, which is used for teleports
6487 assert( ( teleportEntity.GetEntity() != NULL ) || ( IsHidden() == spectating ) );
6489 if ( spectating == spectate ) {
6493 spectating = spectate;
6495 if ( gameLocal.isServer ) {
6496 msg.Init( msgBuf, sizeof( msgBuf ) );
6497 msg.WriteBits( spectating, 1 );
6498 ServerSendEvent( EVENT_SPECTATE, &msg, false, -1 );
6502 // join the spectators
6504 spectator = this->entityNumber;
6507 SetPhysics( &physicsObj );
6508 physicsObj.DisableClip();
6510 Event_DisableWeapon();
6512 hud->HandleNamedEvent( "aim_clear" );
6516 // put everything back together again
6517 currentWeapon = -1; // to make sure the def will be loaded if necessary
6519 Event_EnableWeapon();
6526 idPlayer::SetClipModel
6529 void idPlayer::SetClipModel( void ) {
6533 bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
6535 bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
6536 bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
6538 // the origin of the clip model needs to be set before calling SetClipModel
6539 // otherwise our physics object's current origin value gets reset to 0
6540 idClipModel *newClip;
6541 if ( pm_usecylinder.GetBool() ) {
6542 newClip = new idClipModel( idTraceModel( bounds, 8 ) );
6543 newClip->Translate( physicsObj.PlayerGetOrigin() );
6544 physicsObj.SetClipModel( newClip, 1.0f );
6546 newClip = new idClipModel( idTraceModel( bounds ) );
6547 newClip->Translate( physicsObj.PlayerGetOrigin() );
6548 physicsObj.SetClipModel( newClip, 1.0f );
6554 idPlayer::UseVehicle
6557 void idPlayer::UseVehicle( void ) {
6562 if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
6564 static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
6566 start = GetEyePosition();
6567 end = start + viewAngles.ToForward() * 80.0f;
6568 gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
6569 if ( trace.fraction < 1.0f ) {
6570 ent = gameLocal.entities[ trace.c.entityNum ];
6571 if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
6573 static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
6581 idPlayer::PerformImpulse
6584 void idPlayer::PerformImpulse( int impulse ) {
6586 if ( gameLocal.isClient ) {
6588 byte msgBuf[MAX_EVENT_PARAM_SIZE];
6590 assert( entityNumber == gameLocal.localClientNum );
6591 msg.Init( msgBuf, sizeof( msgBuf ) );
6593 msg.WriteBits( impulse, 6 );
6594 ClientSendEvent( EVENT_IMPULSE, &msg );
6597 if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 ) {
6598 SelectWeapon( impulse, false );
6616 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6617 gameLocal.mpGame.ToggleReady();
6622 centerView.Init(gameLocal.time, 200, viewAngles.pitch, 0);
6626 // when we're not in single player, IMPULSE_19 is used for showScores
6627 // otherwise it opens the pda
6628 if ( !gameLocal.isMultiplayer ) {
6629 if ( objectiveSystemOpen ) {
6631 } else if ( weapon_pda >= 0 ) {
6632 SelectWeapon( weapon_pda, true );
6638 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6639 gameLocal.mpGame.ToggleTeam();
6644 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6645 gameLocal.mpGame.ToggleSpectate();
6651 if ( gameLocal.isServer && gameLocal.mpGame.IsGametypeFlagBased() && (gameLocal.serverInfo.GetInt( "si_midnight" ) == 2) ) {
6652 if ( enviroSuitLight.IsValid() ) {
6653 enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
6654 enviroSuitLight = NULL;
6656 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
6658 idEntity *temp = static_cast<idEntity *>(enviroSuitLight.GetEntity());
6659 idAngles lightAng = firstPersonViewAxis.ToAngles();
6660 idVec3 lightOrg = firstPersonViewOrigin;
6662 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
6663 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
6665 gameLocal.SpawnEntityDef( *lightDef, &temp, false );
6666 enviroSuitLight = static_cast<idLight *>(temp);
6668 enviroSuitLight.GetEntity()->fl.networkSync = true;
6670 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
6671 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
6672 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
6673 lightAng.pitch += enviroAngleOffset.x;
6674 lightAng.yaw += enviroAngleOffset.y;
6675 lightAng.roll += enviroAngleOffset.z;
6677 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
6678 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
6680 enviroSuitLight.GetEntity()->UpdateVisuals();
6681 enviroSuitLight.GetEntity()->Present();
6689 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6690 gameLocal.mpGame.CastVote( gameLocal.localClientNum, true );
6695 if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
6696 gameLocal.mpGame.CastVote( gameLocal.localClientNum, false );
6705 //Hack so the chainsaw will work in MP
6707 SelectWeapon(18, false);
6714 bool idPlayer::HandleESC( void ) {
6715 if ( gameLocal.inCinematic ) {
6716 return SkipCinematic();
6719 if ( objectiveSystemOpen ) {
6727 bool idPlayer::SkipCinematic( void ) {
6728 StartSound( "snd_skipcinematic", SND_CHANNEL_ANY, 0, false, NULL );
6729 return gameLocal.SkipCinematic();
6734 idPlayer::EvaluateControls
6737 void idPlayer::EvaluateControls( void ) {
6738 // check for respawning
6739 if ( health <= 0 ) {
6740 if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
6741 forceRespawn = true;
6742 } else if ( gameLocal.time > maxRespawnTime ) {
6743 forceRespawn = true;
6747 // in MP, idMultiplayerGame decides spawns
6748 if ( forceRespawn && !gameLocal.isMultiplayer && !g_testDeath.GetBool() ) {
6749 // in single player, we let the session handle restarting the level or loading a game
6750 gameLocal.sessionCommand = "died";
6753 if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
6754 PerformImpulse( usercmd.impulse );
6757 scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
6759 oldFlags = usercmd.flags;
6763 // update the viewangles
6769 idPlayer::AdjustSpeed
6772 void idPlayer::AdjustSpeed( void ) {
6777 speed = pm_spectatespeed.GetFloat();
6779 } else if ( noclip ) {
6780 speed = pm_noclipspeed.GetFloat();
6782 } else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && ( usercmd.upmove >= 0 ) ) {
6783 if ( !gameLocal.isMultiplayer && !physicsObj.IsCrouching() && !PowerUpActive( ADRENALINE ) ) {
6784 stamina -= MS2SEC( gameLocal.msec );
6786 if ( stamina < 0 ) {
6789 if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
6791 } else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
6794 bobFrac = stamina / pm_staminathreshold.GetFloat();
6796 speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
6798 rate = pm_staminarate.GetFloat();
6800 // increase 25% faster when not moving
6801 if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( usercmd.upmove == 0 ) ) ) {
6805 stamina += rate * MS2SEC( gameLocal.msec );
6806 if ( stamina > pm_stamina.GetFloat() ) {
6807 stamina = pm_stamina.GetFloat();
6809 speed = pm_walkspeed.GetFloat();
6813 speed *= PowerUpModifier(SPEED);
6815 if ( influenceActive == INFLUENCE_LEVEL3 ) {
6819 physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
6824 idPlayer::AdjustBodyAngles
6827 void idPlayer::AdjustBodyAngles( void ) {
6843 if ( !physicsObj.HasGroundContacts() ) {
6844 idealLegsYaw = 0.0f;
6846 } else if ( usercmd.forwardmove < 0 ) {
6847 idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
6848 legsForward = false;
6849 } else if ( usercmd.forwardmove > 0 ) {
6850 idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
6852 } else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
6853 if ( !legsForward ) {
6854 idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
6856 idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
6858 } else if ( usercmd.rightmove != 0 ) {
6859 idealLegsYaw = 0.0f;
6863 diff = idMath::Fabs( idealLegsYaw - legsYaw );
6864 idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
6865 if ( diff < 0.1f ) {
6866 legsYaw = idealLegsYaw;
6871 if ( !physicsObj.IsCrouching() ) {
6875 oldViewYaw = viewAngles.yaw;
6877 AI_TURN_LEFT = false;
6878 AI_TURN_RIGHT = false;
6879 if ( idealLegsYaw < -45.0f ) {
6881 AI_TURN_RIGHT = true;
6883 } else if ( idealLegsYaw > 45.0f ) {
6885 AI_TURN_LEFT = true;
6890 legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
6892 legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
6893 animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
6895 // calculate the blending between down, straight, and up
6896 frac = viewAngles.pitch / 90.0f;
6897 if ( frac > 0.0f ) {
6899 forwardBlend = 1.0f - frac;
6903 forwardBlend = 1.0f + frac;
6907 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
6908 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
6909 animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
6911 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
6912 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
6913 animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
6918 idPlayer::InitAASLocation
6921 void idPlayer::InitAASLocation( void ) {
6929 GetFloorPos( 64.0f, origin );
6931 num = gameLocal.NumAAS();
6932 aasLocation.SetGranularity( 1 );
6933 aasLocation.SetNum( num );
6934 for( i = 0; i < aasLocation.Num(); i++ ) {
6935 aasLocation[ i ].areaNum = 0;
6936 aasLocation[ i ].pos = origin;
6937 aas = gameLocal.GetAAS( i );
6938 if ( aas && aas->GetSettings() ) {
6939 size = aas->GetSettings()->boundingBoxes[0][1];
6944 aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
6951 idPlayer::SetAASLocation
6954 void idPlayer::SetAASLocation( void ) {
6962 if ( !GetFloorPos( 64.0f, origin ) ) {
6966 for( i = 0; i < aasLocation.Num(); i++ ) {
6967 aas = gameLocal.GetAAS( i );
6972 size = aas->GetSettings()->boundingBoxes[0][1];
6977 areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
6979 aasLocation[ i ].pos = origin;
6980 aasLocation[ i ].areaNum = areaNum;
6987 idPlayer::GetAASLocation
6990 void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
6993 if ( aas != NULL ) {
6994 for( i = 0; i < aasLocation.Num(); i++ ) {
6995 if ( aas == gameLocal.GetAAS( i ) ) {
6996 areaNum = aasLocation[ i ].areaNum;
6997 pos = aasLocation[ i ].pos;
7004 pos = physicsObj.GetOrigin();
7012 void idPlayer::Move( void ) {
7016 idVec3 pushVelocity;
7018 // save old origin and velocity for crashlanding
7019 oldOrigin = physicsObj.GetOrigin();
7020 oldVelocity = physicsObj.GetLinearVelocity();
7021 pushVelocity = physicsObj.GetPushedLinearVelocity();
7023 // set physics variables
7024 physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
7025 physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
7028 physicsObj.SetContents( 0 );
7029 physicsObj.SetMovementType( PM_NOCLIP );
7030 } else if ( spectating ) {
7031 physicsObj.SetContents( 0 );
7032 physicsObj.SetMovementType( PM_SPECTATOR );
7033 } else if ( health <= 0 ) {
7034 physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
7035 physicsObj.SetMovementType( PM_DEAD );
7036 } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
7037 physicsObj.SetContents( CONTENTS_BODY );
7038 physicsObj.SetMovementType( PM_FREEZE );
7040 } else if ( mountedObject ) {
7041 physicsObj.SetContents( 0 );
7042 physicsObj.SetMovementType( PM_FREEZE );
7045 physicsObj.SetContents( CONTENTS_BODY );
7046 physicsObj.SetMovementType( PM_NORMAL );
7050 physicsObj.SetClipMask( MASK_DEADSOLID );
7051 } else if ( health <= 0 ) {
7052 physicsObj.SetClipMask( MASK_DEADSOLID );
7054 physicsObj.SetClipMask( MASK_PLAYERSOLID );
7057 physicsObj.SetDebugLevel( g_debugMove.GetBool() );
7058 physicsObj.SetPlayerInput( usercmd, viewAngles );
7060 // FIXME: physics gets disabled somehow
7061 BecomeActive( TH_PHYSICS );
7064 // update our last valid AAS location for the AI
7068 newEyeOffset = 0.0f;
7069 } else if ( health <= 0 ) {
7070 newEyeOffset = pm_deadviewheight.GetFloat();
7071 } else if ( physicsObj.IsCrouching() ) {
7072 newEyeOffset = pm_crouchviewheight.GetFloat();
7073 } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
7074 newEyeOffset = 0.0f;
7076 newEyeOffset = pm_normalviewheight.GetFloat();
7079 if ( EyeHeight() != newEyeOffset ) {
7081 SetEyeHeight( newEyeOffset );
7083 // smooth out duck height changes
7084 SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
7088 if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
7090 AI_ONGROUND = ( influenceActive == INFLUENCE_LEVEL2 );
7091 AI_ONLADDER = false;
7094 AI_CROUCH = physicsObj.IsCrouching();
7095 AI_ONGROUND = physicsObj.HasGroundContacts();
7096 AI_ONLADDER = physicsObj.OnLadder();
7097 AI_JUMP = physicsObj.HasJumped();
7099 // check if we're standing on top of a monster and give a push if we are
7100 idEntity *groundEnt = physicsObj.GetGroundEntity();
7101 if ( groundEnt && groundEnt->IsType( idAI::Type ) ) {
7102 idVec3 vel = physicsObj.GetLinearVelocity();
7103 if ( vel.ToVec2().LengthSqr() < 0.1f ) {
7104 vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
7105 vel.ToVec2().NormalizeFast();
7106 vel.ToVec2() *= pm_walkspeed.GetFloat();
7108 // give em a push in the direction they're going
7111 physicsObj.SetLinearVelocity( vel );
7116 // bounce the view weapon
7117 loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7118 currentLoggedAccel++;
7119 acc->time = gameLocal.time;
7121 acc->dir[0] = acc->dir[1] = 0;
7124 if ( AI_ONLADDER ) {
7125 int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
7126 int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
7128 if ( old_rung != new_rung ) {
7129 StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
7133 BobCycle( pushVelocity );
7134 CrashLand( oldOrigin, oldVelocity );
7142 void idPlayer::UpdateHud( void ) {
7149 if ( entityNumber != gameLocal.localClientNum ) {
7153 int c = inventory.pickupItemNames.Num();
7155 if ( gameLocal.time > inventory.nextItemPickup ) {
7156 if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
7157 inventory.nextItemNum = 1;
7163 if(gameLocal.isMultiplayer) {
7167 for ( i = 0; i < count, i < c; i++ ) { //_D3XP
7168 hud->SetStateString( va( "itemtext%i", inventory.nextItemNum ), inventory.pickupItemNames[0].name );
7169 hud->SetStateString( va( "itemicon%i", inventory.nextItemNum ), inventory.pickupItemNames[0].icon );
7170 hud->HandleNamedEvent( va( "itemPickup%i", inventory.nextItemNum++ ) );
7171 inventory.pickupItemNames.RemoveIndex( 0 );
7172 if (inventory.nextItemNum == 1 ) {
7173 inventory.onePickupTime = gameLocal.time;
7174 } else if ( inventory.nextItemNum > count ) { //_D3XP
7175 inventory.nextItemNum = 1;
7176 inventory.nextItemPickup = inventory.onePickupTime + 2000;
7178 inventory.nextItemPickup = gameLocal.time + 400;
7184 if ( gameLocal.realClientTime == lastMPAimTime ) {
7185 if ( MPAim != -1 && gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
7186 && gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
7187 && static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
7188 aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
7189 hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
7190 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
7191 hud->HandleNamedEvent( "aim_flash" );
7192 MPAimHighlight = true;
7193 MPAimFadeTime = 0; // no fade till loosing focus
7194 } else if ( MPAimHighlight ) {
7195 hud->HandleNamedEvent( "aim_fade" );
7196 MPAimFadeTime = gameLocal.realClientTime;
7197 MPAimHighlight = false;
7200 if ( MPAimFadeTime ) {
7201 assert( !MPAimHighlight );
7202 if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
7207 hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
7208 if ( numProjectilesFired ) {
7209 hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
7211 hud->SetStateString( "projectilepct", "Hit % 0.0" );
7214 if ( isLagged && gameLocal.isMultiplayer && gameLocal.localClientNum == entityNumber ) {
7215 hud->SetStateString( "hudLag", "1" );
7217 hud->SetStateString( "hudLag", "0" );
7223 idPlayer::UpdateDeathSkin
7226 void idPlayer::UpdateDeathSkin( bool state_hitch ) {
7227 if ( !( gameLocal.isMultiplayer || g_testDeath.GetBool() ) ) {
7230 if ( health <= 0 ) {
7231 if ( !doingDeathSkin ) {
7232 deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
7233 doingDeathSkin = true;
7234 renderEntity.noShadow = true;
7235 if ( state_hitch ) {
7236 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
7238 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
7243 // wait a bit before switching off the content
7244 if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
7245 SetCombatContents( false );
7246 deathClearContentsTime = 0;
7249 renderEntity.noShadow = false;
7250 renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
7252 doingDeathSkin = false;
7258 idPlayer::StartFxOnBone
7261 void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
7264 jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
7266 if ( jointHandle == INVALID_JOINT ) {
7267 gameLocal.Printf( "Cannot find bone %s\n", bone );
7271 if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
7272 offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
7273 axis = axis * GetPhysics()->GetAxis();
7276 idEntityFx::StartFx( fx, &offset, &axis, this, true );
7283 Called every tic for each player
7286 void idPlayer::Think( void ) {
7287 renderEntity_t *headRenderEnt;
7289 UpdatePlayerIcons();
7291 // latch button actions
7292 oldButtons = usercmd.buttons;
7295 usercmd_t oldCmd = usercmd;
7296 usercmd = gameLocal.usercmds[ entityNumber ];
7297 buttonMask &= usercmd.buttons;
7298 usercmd.buttons &= ~buttonMask;
7300 if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
7304 // clear the ik before we do anything else so the skeleton doesn't get updated twice
7305 walkIK.ClearJointMods();
7307 // if this is the very first frame of the map, set the delta view angles
7308 // based on the usercmd angles
7309 if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
7310 spawnAnglesSet = true;
7311 SetViewAngles( spawnAngles );
7312 oldFlags = usercmd.flags;
7316 if ( mountedObject ) {
7317 usercmd.forwardmove = 0;
7318 usercmd.rightmove = 0;
7323 if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
7324 if ( objectiveSystemOpen && AI_PAIN ) {
7327 usercmd.forwardmove = 0;
7328 usercmd.rightmove = 0;
7332 // log movement changes for weapon bobbing effects
7333 if ( usercmd.forwardmove != oldCmd.forwardmove ) {
7334 loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7335 currentLoggedAccel++;
7336 acc->time = gameLocal.time;
7337 acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
7338 acc->dir[1] = acc->dir[2] = 0;
7341 if ( usercmd.rightmove != oldCmd.rightmove ) {
7342 loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
7343 currentLoggedAccel++;
7344 acc->time = gameLocal.time;
7345 acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
7346 acc->dir[0] = acc->dir[2] = 0;
7349 // freelook centering
7350 if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_MLOOK ) {
7351 centerView.Init( gameLocal.time, 200, viewAngles.pitch, 0 );
7355 if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
7356 if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
7357 zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
7359 zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
7363 // if we have an active gui, we will unrotate the view angles as
7364 // we turn the mouse movements into gui events
7365 idUserInterface *gui = ActiveGui();
7366 if ( gui && gui != focusUI ) {
7367 RouteGuiMouse( gui );
7370 // set the push velocity on the weapon before running the physics
7371 if ( weapon.GetEntity() ) {
7372 weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
7377 if ( !af.IsActive() ) {
7379 CopyJointsFromBodyToHead();
7384 if ( !g_stopTime.GetBool() ) {
7386 if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
7390 // not done on clients for various reasons. don't do it on server and save the sound channel for other things
7391 if ( !gameLocal.isMultiplayer ) {
7392 SetCurrentHeartRate();
7394 float scale = new_g_damageScale;
7396 float scale = g_damageScale.GetFloat();
7398 if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
7399 if ( scale < 1.0f ) {
7402 if ( scale > 1.0f ) {
7406 new_g_damageScale = scale;
7408 g_damageScale.SetFloat( scale );
7413 // update GUIs, Items, and character interactions
7418 // update player script
7421 // service animations
7422 if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
7428 // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
7432 // calculate the exact bobbed view position, which is used to
7433 // position the view weapon, among other things
7434 CalculateFirstPersonView();
7436 // this may use firstPersonView, or a thirdPeroson / camera view
7437 CalculateRenderView();
7439 inventory.UpdateArmor();
7443 } else if ( health > 0 ) {
7457 UpdateDeathSkin( false );
7459 if ( gameLocal.isMultiplayer ) {
7463 if ( enviroSuitLight.IsValid() ) {
7464 idAngles lightAng = firstPersonViewAxis.ToAngles();
7465 idVec3 lightOrg = firstPersonViewOrigin;
7466 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
7468 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
7469 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
7471 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
7472 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
7473 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
7474 lightAng.pitch += enviroAngleOffset.x;
7475 lightAng.yaw += enviroAngleOffset.y;
7476 lightAng.roll += enviroAngleOffset.z;
7478 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
7479 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
7480 enviroSuitLight.GetEntity()->UpdateVisuals();
7481 enviroSuitLight.GetEntity()->Present();
7486 if ( head.GetEntity() ) {
7487 headRenderEnt = head.GetEntity()->GetRenderEntity();
7489 headRenderEnt = NULL;
7492 if ( headRenderEnt ) {
7493 if ( influenceSkin ) {
7494 headRenderEnt->customSkin = influenceSkin;
7496 headRenderEnt->customSkin = NULL;
7500 if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
7501 renderEntity.suppressShadowInViewID = 0;
7502 if ( headRenderEnt ) {
7503 headRenderEnt->suppressShadowInViewID = 0;
7506 renderEntity.suppressShadowInViewID = entityNumber+1;
7507 if ( headRenderEnt ) {
7508 headRenderEnt->suppressShadowInViewID = entityNumber+1;
7511 // never cast shadows from our first-person muzzle flashes
7512 renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
7513 if ( headRenderEnt ) {
7514 headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
7517 if ( !g_stopTime.GetBool() ) {
7522 UpdateDamageEffects();
7526 playerView.CalculateShake();
7529 if ( !( thinkFlags & TH_THINK ) ) {
7530 gameLocal.Printf( "player %d not thinking?\n", entityNumber );
7533 if ( g_showEnemies.GetBool() ) {
7536 for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
7537 gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
7538 gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
7541 gameLocal.Printf( "%d: enemies\n", num );
7545 inventory.RechargeAmmo(this);
7547 if(healthRecharge) {
7548 int elapsed = gameLocal.time - lastHealthRechargeTime;
7549 if(elapsed >= rechargeSpeed) {
7550 int intervals = (gameLocal.time - lastHealthRechargeTime)/rechargeSpeed;
7551 Give("health", va("%d", intervals));
7552 lastHealthRechargeTime += intervals*rechargeSpeed;
7556 // determine if portal sky is in pvs
7557 gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( gameLocal.GetPlayerPVS(), GetPhysics()->GetOrigin() );
7564 idPlayer::StartHealthRecharge
7567 void idPlayer::StartHealthRecharge(int speed) {
7568 lastHealthRechargeTime = gameLocal.time;
7569 healthRecharge = true;
7570 rechargeSpeed = speed;
7575 idPlayer::StopHealthRecharge
7578 void idPlayer::StopHealthRecharge() {
7579 healthRecharge = false;
7584 idPlayer::GetCurrentWeapon
7587 idStr idPlayer::GetCurrentWeapon() {
7590 if ( currentWeapon >= 0 ) {
7591 weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
7603 bool idPlayer::CanGive( const char *statname, const char *value ) {
7608 if ( !idStr::Icmp( statname, "health" ) ) {
7609 if ( health >= inventory.maxHealth ) {
7613 } else if ( !idStr::Icmp( statname, "stamina" ) ) {
7614 if ( stamina >= 100 ) {
7619 } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
7622 } else if ( !idStr::Icmp( statname, "air" ) ) {
7623 if ( airTics >= pm_airTics.GetInteger() ) {
7628 return inventory.CanGive( this, spawnArgs, statname, value, &idealWeapon );
7636 idPlayer::StopHelltime
7638 provides a quick non-ramping way of stopping helltime
7641 void idPlayer::StopHelltime( bool quick ) {
7642 if ( !PowerUpActive( HELLTIME ) ) {
7646 // take away the powerups
7647 if ( PowerUpActive( INVULNERABILITY ) ) {
7648 ClearPowerup( INVULNERABILITY );
7651 if ( PowerUpActive( BERSERK ) ) {
7652 ClearPowerup( BERSERK );
7655 if ( PowerUpActive( HELLTIME ) ) {
7656 ClearPowerup( HELLTIME );
7659 // stop the looping sound
7660 StopSound( SND_CHANNEL_DEMONIC, false );
7662 // reset the game vars
7664 gameLocal.QuickSlowmoReset();
7670 idPlayer::Event_ToggleBloom
7673 void idPlayer::Event_ToggleBloom( int on ) {
7675 bloomEnabled = true;
7678 bloomEnabled = false;
7684 idPlayer::Event_SetBloomParms
7687 void idPlayer::Event_SetBloomParms( float speed, float intensity ) {
7689 bloomIntensity = intensity;
7694 idPlayer::PlayHelltimeStopSound
7697 void idPlayer::PlayHelltimeStopSound() {
7700 if ( spawnArgs.GetString( "snd_helltime_stop", "", &sound ) ) {
7701 PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
7708 idPlayer::RouteGuiMouse
7711 void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
7713 const char *command;
7715 if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
7716 ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
7717 command = gui->HandleEvent( &ev, gameLocal.time );
7718 oldMouseX = usercmd.mx;
7719 oldMouseY = usercmd.my;
7725 idPlayer::LookAtKiller
7728 void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
7731 if ( attacker && attacker != this ) {
7732 dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
7733 } else if ( inflictor && inflictor != this ) {
7734 dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
7736 dir = viewAxis[ 0 ];
7739 idAngles ang( 0, dir.ToYaw(), 0 );
7740 SetViewAngles( ang );
7748 void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
7750 SpectateFreeFly( false );
7751 } else if ( health > 0 ) {
7754 ServerSpectate( true );
7755 forceRespawn = true;
7757 Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
7758 if ( delayRespawn ) {
7759 forceRespawn = false;
7760 int delay = spawnArgs.GetFloat( "respawn_delay" );
7761 minRespawnTime = gameLocal.time + SEC2MS( delay );
7762 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7773 void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
7776 assert( !gameLocal.isClient );
7778 // stop taking knockback once dead
7779 fl.noknockback = true;
7780 if ( health < -999 ) {
7789 heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
7790 AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
7792 if ( !g_testDeath.GetBool() ) {
7793 playerView.Fade( colorBlack, 12000 );
7797 SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
7798 SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
7801 animator.ClearAllJoints();
7803 if ( StartRagdoll() ) {
7804 pm_modelView.SetInteger( 0 );
7805 minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
7806 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7808 // don't allow respawn until the death anim is done
7809 // g_forcerespawn may force spawning at some later time
7810 delay = spawnArgs.GetFloat( "respawn_delay" );
7811 minRespawnTime = gameLocal.time + SEC2MS( delay );
7812 maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
7815 physicsObj.SetMovementType( PM_DEAD );
7816 StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
7817 StopSound( SND_CHANNEL_BODY2, false );
7819 fl.takedamage = true; // can still be gibbed
7821 // get rid of weapon
7822 weapon.GetEntity()->OwnerDied();
7824 // drop the weapon as an item
7828 // drop the flag if player was carrying it
7829 if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() &&
7836 if ( !g_testDeath.GetBool() ) {
7837 LookAtKiller( inflictor, attacker );
7840 if ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) {
7841 idPlayer *killer = NULL;
7842 // no gibbing in MP. Event_Gib will early out in MP
7843 if ( attacker->IsType( idPlayer::Type ) ) {
7844 killer = static_cast<idPlayer*>(attacker);
7845 if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
7848 gibsLaunched = false;
7851 gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
7853 physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
7864 =====================
7865 idPlayer::GetAIAimTargets
7867 Returns positions for the AI to aim at.
7868 =====================
7870 void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
7875 origin = lastSightPos - physicsObj.GetOrigin();
7877 GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
7878 headPos = offset + origin;
7880 GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
7881 chestPos = offset + origin;
7886 idPlayer::DamageFeedback
7888 callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
7891 void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
7892 assert( !gameLocal.isClient );
7893 damage *= PowerUpModifier( BERSERK );
7894 if ( damage && ( victim != this ) && ( victim->IsType( idActor::Type ) || victim->IsType( idDamagable::Type ) ) ) {
7896 idPlayer *victimPlayer = NULL;
7898 /* No damage feedback sound for hitting friendlies in CTF */
7899 if ( victim->IsType( idPlayer::Type ) ) {
7900 victimPlayer = static_cast<idPlayer*>(victim);
7903 if ( gameLocal.mpGame.IsGametypeFlagBased() && victimPlayer && this->team == victimPlayer->team ) {
7904 /* Do nothing ... */
7907 SetLastHitTime( gameLocal.time );
7914 idPlayer::CalcDamagePoints
7916 Calculates how many health and armor points will be inflicted, but
7917 doesn't actually do anything with them. This is used to tell when an attack
7918 would have killed the player, possibly allowing a "saving throw"
7921 void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
7922 const float damageScale, const int location, int *health, int *armor ) {
7926 damageDef->GetInt( "damage", "20", damage );
7927 damage = GetDamageForLocation( damage, location );
7929 idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
7930 if ( !gameLocal.isMultiplayer ) {
7931 if ( inflictor != gameLocal.world ) {
7932 switch ( g_skill.GetInteger() ) {
7951 damage *= damageScale;
7953 // always give half damage if hurting self
7954 if ( attacker == this ) {
7955 if ( gameLocal.isMultiplayer ) {
7956 // only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
7957 damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
7959 damage *= damageDef->GetFloat( "selfDamageScale", "1" );
7963 // check for completely getting out of the damage
7964 if ( !damageDef->GetBool( "noGod" ) ) {
7965 // check for godmode
7970 //Invulnerability is just like god mode
7971 if( PowerUpActive( INVULNERABILITY ) ) {
7977 // inform the attacker that they hit someone
7978 attacker->DamageFeedback( this, inflictor, damage );
7980 // save some from armor
7981 if ( !damageDef->GetBool( "noArmor" ) ) {
7982 float armor_protection;
7984 armor_protection = ( gameLocal.isMultiplayer ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
7986 armorSave = ceil( damage * armor_protection );
7987 if ( armorSave >= inventory.armor ) {
7988 armorSave = inventory.armor;
7993 } else if ( armorSave >= damage ) {
7994 armorSave = damage - 1;
7997 damage -= armorSave;
8003 // check for team damage
8004 if ( gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
8005 && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
8006 && !damageDef->GetBool( "noTeam" )
8008 && player != this // you get self damage no matter what
8009 && player->team == team ) {
8021 this entity that is being damaged
8022 inflictor entity that is causing the damage
8023 attacker entity that caused the inflictor to damage targ
8024 example: this=monster, inflictor=rocket, attacker=player
8026 dir direction of the attack for knockback in global space
8028 damageDef an idDict with all the options for damage effects
8030 inflictor, attacker, dir, and point can be NULL for environmental effects
8033 void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
8034 const char *damageDefName, const float damageScale, const int location ) {
8040 idVec3 localDamageVector;
8041 float attackerPushScale;
8043 SetTimeState ts( timeGroup );
8046 // damage is only processed on server
8047 if ( gameLocal.isClient ) {
8051 if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
8056 inflictor = gameLocal.world;
8059 attacker = gameLocal.world;
8062 if ( attacker->IsType( idAI::Type ) ) {
8064 if ( PowerUpActive( BERSERK ) ) {
8068 // don't take damage from monsters during influences
8069 if ( influenceActive != 0 ) {
8074 const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
8076 gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
8080 if ( damageDef->dict.GetBool( "ignore_player" ) ) {
8084 CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
8086 // determine knockback
8087 damageDef->dict.GetInt( "knockback", "20", knockback );
8090 idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
8092 if ( gameLocal.mpGame.IsGametypeTeamBased()
8093 && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
8094 && !damageDef->dict.GetBool( "noTeam" )
8096 && player != this // you get self damage no matter what
8097 && player->team == team ) {
8102 if ( knockback != 0 && !fl.noknockback ) {
8103 if ( attacker == this ) {
8104 damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
8106 attackerPushScale = 1.0f;
8111 kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
8112 physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
8114 // set the timer so that the player can't cancel out the movement immediately
8115 physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
8119 // give feedback on the player view and audibly when armor is helping
8121 inventory.armor -= armorSave;
8123 if ( gameLocal.time > lastArmorPulse + 200 ) {
8124 StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
8126 lastArmorPulse = gameLocal.time;
8129 if ( damageDef->dict.GetBool( "burn" ) ) {
8130 StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
8131 } else if ( damageDef->dict.GetBool( "no_air" ) ) {
8132 if ( !armorSave && health > 0 ) {
8133 StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
8137 if ( g_debugDamage.GetInteger() ) {
8138 gameLocal.Printf( "client:%i health:%i damage:%i armor:%i\n",
8139 entityNumber, health, damage, armorSave );
8142 // move the world direction vector to local coordinates
8144 damage_from.Normalize();
8146 viewAxis.ProjectVector( damage_from, localDamageVector );
8148 // add to the damage inflicted on a player this frame
8149 // the total will be turned into screen blends and view angle kicks
8150 // at the end of the frame
8152 playerView.DamageImpulse( localDamageVector, &damageDef->dict );
8158 if ( !gameLocal.isMultiplayer ) {
8160 float scale = new_g_damageScale;
8162 float scale = g_damageScale.GetFloat();
8164 if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
8165 if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
8168 new_g_damageScale = scale;
8170 g_damageScale.SetFloat( scale );
8175 if ( scale > 0.0f ) {
8184 int oldHealth = health;
8187 if ( health <= 0 ) {
8189 if ( health < -999 ) {
8193 isTelefragged = damageDef->dict.GetBool( "telefrag" );
8195 lastDmgTime = gameLocal.time;
8196 Killed( inflictor, attacker, damage, dir, location );
8202 // let the anim script know we took damage
8203 AI_PAIN = Pain( inflictor, attacker, damage, dir, location );
8204 if ( !g_testDeath.GetBool() ) {
8205 lastDmgTime = gameLocal.time;
8209 // don't accumulate impulses
8210 if ( af.IsLoaded() ) {
8214 // physics is turned off by calling af.Rest()
8215 BecomeActive( TH_PHYSICS );
8219 lastDamageDef = damageDef->Index();
8220 lastDamageDir = damage_from;
8221 lastDamageLocation = location;
8229 void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
8232 if ( weapon.GetEntity() ) {
8233 weapon.GetEntity()->LowerWeapon();
8236 SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
8237 if ( !gameLocal.isMultiplayer && GetFloorPos( 16.0f, org ) ) {
8241 // clear the ik heights so model doesn't appear in the wrong place
8244 GetPhysics()->SetLinearVelocity( vec3_origin );
8246 SetViewAngles( angles );
8249 idealLegsYaw = 0.0f;
8250 oldViewYaw = viewAngles.yaw;
8252 if ( gameLocal.isMultiplayer ) {
8253 playerView.Flash( colorWhite, 140 );
8258 teleportEntity = destination;
8260 if ( !gameLocal.isClient && !noclip ) {
8261 if ( gameLocal.isMultiplayer ) {
8262 // kill anything at the new position or mark for kill depending on immediate or delayed teleport
8263 gameLocal.KillBox( this, destination != NULL );
8265 // kill anything at the new position
8266 gameLocal.KillBox( this, true );
8271 if ( PowerUpActive( HELLTIME ) ) {
8278 ====================
8279 idPlayer::SetPrivateCameraView
8280 ====================
8282 void idPlayer::SetPrivateCameraView( idCamera *camView ) {
8283 privateCameraView = camView;
8288 if ( !spectating ) {
8295 ====================
8296 idPlayer::DefaultFov
8298 Returns the base FOV
8299 ====================
8301 float idPlayer::DefaultFov( void ) const {
8304 fov = g_fov.GetFloat();
8305 if ( gameLocal.isMultiplayer ) {
8306 if ( fov < 90.0f ) {
8308 } else if ( fov > 110.0f ) {
8317 ====================
8320 Fixed fov at intermissions, otherwise account for fov variable and zooms.
8321 ====================
8323 float idPlayer::CalcFov( bool honorZoom ) {
8327 return DefaultFov() + 10.0f + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
8330 if ( influenceFov ) {
8331 return influenceFov;
8334 if ( zoomFov.IsDone( gameLocal.time ) ) {
8335 fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->GetZoomFov() : DefaultFov();
8337 fov = zoomFov.GetCurrentValue( gameLocal.time );
8340 // bound normal viewsize
8343 } else if ( fov > 179 ) {
8352 idPlayer::GunTurningOffset
8354 generate a rotational offset for the gun based on the view angle
8355 history in loggedViewAngles
8358 idAngles idPlayer::GunTurningOffset( void ) {
8363 if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
8367 idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
8370 int weaponAngleOffsetAverages;
8371 float weaponAngleOffsetScale, weaponAngleOffsetMax;
8373 weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
8377 // calcualte this so the wrap arounds work properly
8378 for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
8379 idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
8381 idAngles delta = a2 - current;
8383 if ( delta[1] > 180 ) {
8385 } else if ( delta[1] < -180 ) {
8389 av += delta * ( 1.0f / weaponAngleOffsetAverages );
8392 a = ( av - current ) * weaponAngleOffsetScale;
8394 for ( int i = 0 ; i < 3 ; i++ ) {
8395 if ( a[i] < -weaponAngleOffsetMax ) {
8396 a[i] = -weaponAngleOffsetMax;
8397 } else if ( a[i] > weaponAngleOffsetMax ) {
8398 a[i] = weaponAngleOffsetMax;
8407 idPlayer::GunAcceleratingOffset
8409 generate a positional offset for the gun based on the movement
8410 history in loggedAccelerations
8413 idVec3 idPlayer::GunAcceleratingOffset( void ) {
8416 float weaponOffsetTime, weaponOffsetScale;
8420 weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
8422 int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
8426 for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
8427 loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
8430 float t = gameLocal.time - acc->time;
8431 if ( t >= weaponOffsetTime ) {
8432 break; // remainder are too old to care about
8435 f = t / weaponOffsetTime;
8436 f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
8437 ofs += f * weaponOffsetScale * acc->dir;
8445 idPlayer::CalculateViewWeaponPos
8447 Calculate the bobbing position of the view weapon
8450 void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
8456 // CalculateRenderView must have been called first
8457 const idVec3 &viewOrigin = firstPersonViewOrigin;
8458 const idMat3 &viewAxis = firstPersonViewAxis;
8460 // these cvars are just for hand tweaking before moving a value to the weapon def
8461 idVec3 gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
8463 // as the player changes direction, the gun will take a small lag
8464 idVec3 gunOfs = GunAcceleratingOffset();
8465 origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
8467 // on odd legs, invert some angles
8468 if ( bobCycle & 128 ) {
8474 // gun angles from bobbing
8475 angles.roll = scale * bobfracsin * 0.005f;
8476 angles.yaw = scale * bobfracsin * 0.01f;
8477 angles.pitch = xyspeed * bobfracsin * 0.005f;
8479 // gun angles from turning
8480 if ( gameLocal.isMultiplayer ) {
8481 idAngles offset = GunTurningOffset();
8482 offset *= g_mpWeaponAngleScale.GetFloat();
8485 angles += GunTurningOffset();
8488 idVec3 gravity = physicsObj.GetGravityNormal();
8490 // drop the weapon when landing after a jump / fall
8491 delta = gameLocal.time - landTime;
8492 if ( delta < LAND_DEFLECT_TIME ) {
8493 origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
8494 } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
8495 origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
8498 // speed sensitive idle drift
8499 scale = xyspeed + 40.0f;
8500 fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
8501 angles.roll += fracsin;
8502 angles.yaw += fracsin;
8503 angles.pitch += fracsin;
8505 axis = angles.ToMat3() * viewAxis;
8510 idPlayer::OffsetThirdPersonView
8513 void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
8519 float forwardScale, sideScale;
8525 angles = viewAngles;
8526 GetViewPos( origin, axis );
8529 angles.pitch = 0.0f;
8532 if ( angles.pitch > 45.0f ) {
8533 angles.pitch = 45.0f; // don't go too far overhead
8536 focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
8537 focusPoint.z += height;
8539 view.z += 8 + height;
8541 angles.pitch *= 0.5f;
8542 renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
8544 idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
8545 view -= range * forwardScale * renderView->viewaxis[ 0 ];
8546 view += range * sideScale * renderView->viewaxis[ 1 ];
8549 // trace a ray from the origin to the viewpoint to make sure the view isn't
8550 // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
8551 bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
8552 gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
8553 if ( trace.fraction != 1.0f ) {
8554 view = trace.endpos;
8555 view.z += ( 1.0f - trace.fraction ) * 32.0f;
8557 // try another trace to this position, because a tunnel may have the ceiling
8558 // close enough that this is poking out
8559 gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
8560 view = trace.endpos;
8564 // select pitch to look at focus point from vieword
8566 focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
8567 if ( focusDist < 1.0f ) {
8568 focusDist = 1.0f; // should never happen
8571 angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) );
8572 angles.yaw -= angle;
8574 renderView->vieworg = view;
8575 renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
8576 renderView->viewID = 0;
8581 idPlayer::GetEyePosition
8584 idVec3 idPlayer::GetEyePosition( void ) const {
8587 // use the smoothed origin if spectating another player in multiplayer
8588 if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
8589 org = smoothedOrigin;
8591 org = GetPhysics()->GetOrigin();
8593 return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
8598 idPlayer::GetViewPos
8601 void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
8604 // if dead, fix the angle and don't add any kick
8605 if ( health <= 0 ) {
8606 angles.yaw = viewAngles.yaw;
8609 axis = angles.ToMat3();
8610 origin = GetEyePosition();
8612 origin = GetEyePosition() + viewBob;
8613 angles = viewAngles + viewBobAngles + playerView.AngleOffset();
8615 axis = angles.ToMat3() * physicsObj.GetGravityAxis();
8617 // adjust the origin based on the camera nodal distance (eye distance from neck)
8618 origin += physicsObj.GetGravityNormal() * g_viewNodalZ.GetFloat();
8619 origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
8625 idPlayer::CalculateFirstPersonView
8628 void idPlayer::CalculateFirstPersonView( void ) {
8629 if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
8630 // Displays the view from the point of view of the "camera" joint in the player model
8636 ang = viewBobAngles + playerView.AngleOffset();
8637 ang.yaw += viewAxis[ 0 ].ToYaw();
8639 jointHandle_t joint = animator.GetJointHandle( "camera" );
8640 animator.GetJointTransform( joint, gameLocal.time, origin, axis );
8641 firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
8642 firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
8644 // offset for local bobbing and kicks
8645 GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
8647 // shakefrom sound stuff only happens in first person
8648 firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
8655 idPlayer::GetRenderView
8657 Returns the renderView that was calculated for this tic
8660 renderView_t *idPlayer::GetRenderView( void ) {
8666 idPlayer::CalculateRenderView
8668 create the renderView for the current tic
8671 void idPlayer::CalculateRenderView( void ) {
8675 if ( !renderView ) {
8676 renderView = new renderView_t;
8678 memset( renderView, 0, sizeof( *renderView ) );
8680 // copy global shader parms
8681 for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
8682 renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
8684 renderView->globalMaterial = gameLocal.GetGlobalMaterial();
8687 renderView->time = gameLocal.slow.time;
8690 // calculate size of 3D view
8693 renderView->width = SCREEN_WIDTH;
8694 renderView->height = SCREEN_HEIGHT;
8695 renderView->viewID = 0;
8697 // check if we should be drawing from a camera's POV
8698 if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
8699 // get origin, axis, and fov
8700 if ( privateCameraView ) {
8701 privateCameraView->GetViewParms( renderView );
8703 gameLocal.GetCamera()->GetViewParms( renderView );
8706 if ( g_stopTime.GetBool() ) {
8707 renderView->vieworg = firstPersonViewOrigin;
8708 renderView->viewaxis = firstPersonViewAxis;
8710 if ( !pm_thirdPerson.GetBool() ) {
8711 // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
8712 // allow the right player view weapons
8713 renderView->viewID = entityNumber + 1;
8715 } else if ( pm_thirdPerson.GetBool() ) {
8716 OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
8717 } else if ( pm_thirdPersonDeath.GetBool() ) {
8718 range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
8719 OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
8721 renderView->vieworg = firstPersonViewOrigin;
8722 renderView->viewaxis = firstPersonViewAxis;
8724 // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
8725 // allow the right player view weapons
8726 renderView->viewID = entityNumber + 1;
8730 gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
8733 if ( renderView->fov_y == 0 ) {
8734 common->Error( "renderView->fov_y == 0" );
8737 if ( g_showviewpos.GetBool() ) {
8738 gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
8747 void idPlayer::AddAIKill( void ) {
8754 if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
8761 ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
8762 max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
8763 if ( inventory.ammo[ ammo_souls ] < max_souls ) {
8764 inventory.ammo[ ammo_souls ]++;
8765 if ( inventory.ammo[ ammo_souls ] >= max_souls ) {
8766 hud->HandleNamedEvent( "soulCubeReady" );
8767 StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
8775 idPlayer::SetSoulCubeProjectile
8778 void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
8779 soulCubeProjectile = projectile;
8784 idPlayer::AddProjectilesFired
8787 void idPlayer::AddProjectilesFired( int count ) {
8788 numProjectilesFired += count;
8793 idPlayer::AddProjectileHites
8796 void idPlayer::AddProjectileHits( int count ) {
8797 numProjectileHits += count;
8802 idPlayer::SetLastHitTime
8805 void idPlayer::SetLastHitTime( int time ) {
8806 idPlayer *aimed = NULL;
8808 if ( time && lastHitTime != time ) {
8813 // level start and inits
8816 if ( gameLocal.isMultiplayer && ( time - lastSndHitTime ) > 10 ) {
8817 lastSndHitTime = time;
8818 StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
8821 cursor->HandleNamedEvent( "hitTime" );
8824 if ( MPAim != -1 ) {
8825 if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
8826 aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
8829 // full highlight, no fade till loosing aim
8830 hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
8832 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
8834 hud->HandleNamedEvent( "aim_flash" );
8835 MPAimHighlight = true;
8837 } else if ( lastMPAim != -1 ) {
8838 if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
8839 aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
8842 // start fading right away
8843 hud->SetStateString( "aim_text", gameLocal.userInfo[ lastMPAim ].GetString( "ui_name" ) );
8845 hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
8847 hud->HandleNamedEvent( "aim_flash" );
8848 hud->HandleNamedEvent( "aim_fade" );
8849 MPAimHighlight = false;
8850 MPAimFadeTime = gameLocal.realClientTime;
8857 idPlayer::SetInfluenceLevel
8860 void idPlayer::SetInfluenceLevel( int level ) {
8861 if ( level != influenceActive ) {
8863 for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
8864 if ( ent->IsType( idProjectile::Type ) ) {
8865 // remove all projectiles
8866 ent->PostEventMS( &EV_Remove, 0 );
8869 if ( weaponEnabled && weapon.GetEntity() ) {
8870 weapon.GetEntity()->EnterCinematic();
8873 physicsObj.SetLinearVelocity( vec3_origin );
8874 if ( weaponEnabled && weapon.GetEntity() ) {
8875 weapon.GetEntity()->ExitCinematic();
8878 influenceActive = level;
8884 idPlayer::SetInfluenceView
8887 void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
8888 influenceMaterial = NULL;
8889 influenceEntity = NULL;
8890 influenceSkin = NULL;
8891 if ( mtr && *mtr ) {
8892 influenceMaterial = declManager->FindMaterial( mtr );
8894 if ( skinname && *skinname ) {
8895 influenceSkin = declManager->FindSkin( skinname );
8896 if ( head.GetEntity() ) {
8897 head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
8901 influenceRadius = radius;
8902 if ( radius > 0.0f ) {
8903 influenceEntity = ent;
8909 idPlayer::SetInfluenceFov
8912 void idPlayer::SetInfluenceFov( float fov ) {
8921 bool idPlayer::OnLadder( void ) const {
8922 return physicsObj.OnLadder();
8927 idPlayer::Event_GetButtons
8930 void idPlayer::Event_GetButtons( void ) {
8931 idThread::ReturnInt( usercmd.buttons );
8936 idPlayer::Event_GetMove
8939 void idPlayer::Event_GetMove( void ) {
8940 idVec3 move( usercmd.forwardmove, usercmd.rightmove, usercmd.upmove );
8941 idThread::ReturnVector( move );
8946 idPlayer::Event_GetViewAngles
8949 void idPlayer::Event_GetViewAngles( void ) {
8950 idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
8955 idPlayer::Event_StopFxFov
8958 void idPlayer::Event_StopFxFov( void ) {
8964 idPlayer::StartFxFov
8967 void idPlayer::StartFxFov( float duration ) {
8969 PostEventSec( &EV_Player_StopFxFov, duration );
8974 idPlayer::Event_EnableWeapon
8977 void idPlayer::Event_EnableWeapon( void ) {
8978 hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
8979 weaponEnabled = true;
8980 if ( weapon.GetEntity() ) {
8981 weapon.GetEntity()->ExitCinematic();
8987 idPlayer::Event_DisableWeapon
8990 void idPlayer::Event_DisableWeapon( void ) {
8991 hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
8992 weaponEnabled = false;
8993 if ( weapon.GetEntity() ) {
8994 weapon.GetEntity()->EnterCinematic();
9001 idPlayer::Event_GiveInventoryItem
9004 void idPlayer::Event_GiveInventoryItem( const char* name ) {
9005 GiveInventoryItem(name);
9010 idPlayer::Event_RemoveInventoryItem
9013 void idPlayer::Event_RemoveInventoryItem( const char* name ) {
9014 RemoveInventoryItem(name);
9019 idPlayer::Event_GetIdealWeapon
9022 void idPlayer::Event_GetIdealWeapon( void ) {
9025 if ( idealWeapon >= 0 ) {
9026 weapon = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
9027 idThread::ReturnString( weapon );
9029 idThread::ReturnString( "" );
9035 idPlayer::Event_SetPowerupTime
9038 void idPlayer::Event_SetPowerupTime( int powerup, int time ) {
9040 GivePowerUp( powerup, time );
9042 ClearPowerup( powerup );
9048 idPlayer::Event_IsPowerupActive
9051 void idPlayer::Event_IsPowerupActive( int powerup ) {
9052 idThread::ReturnInt(this->PowerUpActive(powerup) ? 1 : 0);
9057 idPlayer::Event_StartWarp
9060 void idPlayer::Event_StartWarp() {
9061 playerView.AddWarp( idVec3( 0, 0, 0 ), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100, 1000 );
9066 idPlayer::Event_StopHelltime
9069 void idPlayer::Event_StopHelltime( int mode ) {
9071 StopHelltime( true );
9074 StopHelltime( false );
9080 idPlayer::Event_WeaponAvailable
9083 void idPlayer::Event_WeaponAvailable( const char* name ) {
9085 idThread::ReturnInt( WeaponAvailable(name) ? 1 : 0 );
9088 bool idPlayer::WeaponAvailable( const char* name ) {
9089 for( int i = 0; i < MAX_WEAPONS; i++ ) {
9090 if ( inventory.weapons & ( 1 << i ) ) {
9091 const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
9092 if ( !idStr::Cmp( weap, name ) ) {
9104 idPlayer::Event_GetCurrentWeapon
9107 void idPlayer::Event_GetCurrentWeapon( void ) {
9110 if ( currentWeapon >= 0 ) {
9111 weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
9112 idThread::ReturnString( weapon );
9114 idThread::ReturnString( "" );
9120 idPlayer::Event_GetPreviousWeapon
9123 void idPlayer::Event_GetPreviousWeapon( void ) {
9126 if ( previousWeapon >= 0 ) {
9127 int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
9128 weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
9129 idThread::ReturnString( weapon );
9131 idThread::ReturnString( spawnArgs.GetString( "def_weapon0" ) );
9137 idPlayer::Event_SelectWeapon
9140 void idPlayer::Event_SelectWeapon( const char *weaponName ) {
9144 if ( gameLocal.isClient ) {
9145 gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
9149 if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
9150 idealWeapon = weapon_fists;
9151 weapon.GetEntity()->HideWeapon();
9156 for( i = 0; i < MAX_WEAPONS; i++ ) {
9157 if ( inventory.weapons & ( 1 << i ) ) {
9158 const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
9159 if ( !idStr::Cmp( weap, weaponName ) ) {
9166 if ( weaponNum < 0 ) {
9167 gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
9171 hiddenWeapon = false;
9172 idealWeapon = weaponNum;
9179 idPlayer::Event_GetWeaponEntity
9182 void idPlayer::Event_GetWeaponEntity( void ) {
9183 idThread::ReturnEntity( weapon.GetEntity() );
9188 idPlayer::Event_OpenPDA
9191 void idPlayer::Event_OpenPDA( void ) {
9192 if ( !gameLocal.isMultiplayer ) {
9199 idPlayer::Event_InPDA
9202 void idPlayer::Event_InPDA( void ) {
9203 idThread::ReturnInt( objectiveSystemOpen );
9208 idPlayer::TeleportDeath
9211 void idPlayer::TeleportDeath( int killer ) {
9212 teleportKiller = killer;
9217 idPlayer::Event_ExitTeleporter
9220 void idPlayer::Event_ExitTeleporter( void ) {
9225 exitEnt = teleportEntity.GetEntity();
9227 common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
9231 pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
9233 if ( gameLocal.isServer ) {
9234 ServerSendEvent( EVENT_EXIT_TELEPORTER, NULL, false, -1 );
9237 SetPrivateCameraView( NULL );
9238 // setup origin and push according to the exit target
9239 SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
9240 SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
9241 physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
9242 physicsObj.ClearPushedVelocity();
9244 playerView.Flash( colorWhite, 120 );
9246 // clear the ik heights so model doesn't appear in the wrong place
9251 StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
9253 if ( teleportKiller != -1 ) {
9254 // we got killed while being teleported
9255 Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
9256 teleportKiller = -1;
9258 // kill anything that would have waited at teleport exit
9259 gameLocal.KillBox( this );
9261 teleportEntity = NULL;
9266 idPlayer::ClientPredictionThink
9269 void idPlayer::ClientPredictionThink( void ) {
9270 renderEntity_t *headRenderEnt;
9272 oldFlags = usercmd.flags;
9273 oldButtons = usercmd.buttons;
9275 usercmd = gameLocal.usercmds[ entityNumber ];
9277 if ( entityNumber != gameLocal.localClientNum ) {
9278 // ignore attack button of other clients. that's no good for predictions
9279 usercmd.buttons &= ~BUTTON_ATTACK;
9282 buttonMask &= usercmd.buttons;
9283 usercmd.buttons &= ~buttonMask;
9286 if ( mountedObject ) {
9287 usercmd.forwardmove = 0;
9288 usercmd.rightmove = 0;
9293 if ( objectiveSystemOpen ) {
9294 usercmd.forwardmove = 0;
9295 usercmd.rightmove = 0;
9299 // clear the ik before we do anything else so the skeleton doesn't get updated twice
9300 walkIK.ClearJointMods();
9302 if ( gameLocal.isNewFrame ) {
9303 if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
9304 PerformImpulse( usercmd.impulse );
9308 scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
9314 // update the smoothed view angles
9315 if ( gameLocal.framenum >= smoothedFrame && entityNumber != gameLocal.localClientNum ) {
9316 idAngles anglesDiff = viewAngles - smoothedAngles;
9317 anglesDiff.Normalize180();
9318 if ( idMath::Fabs( anglesDiff.yaw ) < 90.0f && idMath::Fabs( anglesDiff.pitch ) < 90.0f ) {
9319 // smoothen by pushing back to the previous angles
9320 viewAngles -= gameLocal.clientSmoothing * anglesDiff;
9321 viewAngles.Normalize180();
9323 smoothedAngles = viewAngles;
9325 smoothedOriginUpdated = false;
9327 if ( !af.IsActive() ) {
9332 // don't allow client to move when lagged
9336 // update GUIs, Items, and character interactions
9339 // service animations
9340 if ( !spectating && !af.IsActive() ) {
9346 // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
9349 // calculate the exact bobbed view position, which is used to
9350 // position the view weapon, among other things
9351 CalculateFirstPersonView();
9353 // this may use firstPersonView, or a thirdPerson / camera view
9354 CalculateRenderView();
9356 if ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
9362 if ( gameLocal.isNewFrame ) {
9366 UpdateDeathSkin( false );
9368 if ( head.GetEntity() ) {
9369 headRenderEnt = head.GetEntity()->GetRenderEntity();
9371 headRenderEnt = NULL;
9374 if ( headRenderEnt ) {
9375 if ( influenceSkin ) {
9376 headRenderEnt->customSkin = influenceSkin;
9378 headRenderEnt->customSkin = NULL;
9382 if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
9383 renderEntity.suppressShadowInViewID = 0;
9384 if ( headRenderEnt ) {
9385 headRenderEnt->suppressShadowInViewID = 0;
9388 renderEntity.suppressShadowInViewID = entityNumber+1;
9389 if ( headRenderEnt ) {
9390 headRenderEnt->suppressShadowInViewID = entityNumber+1;
9393 // never cast shadows from our first-person muzzle flashes
9394 renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
9395 if ( headRenderEnt ) {
9396 headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
9399 if ( !gameLocal.inCinematic ) {
9404 if ( enviroSuitLight.IsValid() ) {
9405 idAngles lightAng = firstPersonViewAxis.ToAngles();
9406 idVec3 lightOrg = firstPersonViewOrigin;
9407 const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
9409 idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
9410 idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
9412 lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
9413 lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
9414 lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
9415 lightAng.pitch += enviroAngleOffset.x;
9416 lightAng.yaw += enviroAngleOffset.y;
9417 lightAng.roll += enviroAngleOffset.z;
9419 enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
9420 enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
9421 enviroSuitLight.GetEntity()->UpdateVisuals();
9422 enviroSuitLight.GetEntity()->Present();
9426 if ( gameLocal.isMultiplayer ) {
9432 UpdateDamageEffects();
9436 if ( gameLocal.isNewFrame && entityNumber == gameLocal.localClientNum ) {
9437 playerView.CalculateShake();
9441 // determine if portal sky is in pvs
9442 pvsHandle_t clientPVS = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
9443 gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( clientPVS, GetPhysics()->GetOrigin() );
9444 gameLocal.pvs.FreeCurrentPVS( clientPVS );
9450 idPlayer::GetPhysicsToVisualTransform
9453 bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
9454 if ( af.IsActive() ) {
9455 af.GetPhysicsToVisualTransform( origin, axis );
9459 // smoothen the rendered origin and angles of other clients
9460 // smooth self origin if snapshots are telling us prediction is off
9461 if ( gameLocal.isClient && gameLocal.framenum >= smoothedFrame && ( entityNumber != gameLocal.localClientNum || selfSmooth ) ) {
9462 // render origin and axis
9463 idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
9464 idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
9466 // update the smoothed origin
9467 if ( !smoothedOriginUpdated ) {
9468 idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
9469 if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
9470 // smoothen by pushing back to the previous position
9472 assert( entityNumber == gameLocal.localClientNum );
9473 renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
9475 renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
9478 smoothedOrigin = renderOrigin;
9480 smoothedFrame = gameLocal.framenum;
9481 smoothedOriginUpdated = true;
9484 axis = idAngles( 0.0f, smoothedAngles.yaw, 0.0f ).ToMat3();
9485 origin = ( smoothedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
9490 origin = modelOffset;
9497 idPlayer::GetPhysicsToSoundTransform
9500 bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
9503 if ( privateCameraView ) {
9504 camera = privateCameraView;
9506 camera = gameLocal.GetCamera();
9512 memset( &view, 0, sizeof( view ) );
9513 camera->GetViewParms( &view );
9514 origin = view.vieworg;
9515 axis = view.viewaxis;
9518 return idActor::GetPhysicsToSoundTransform( origin, axis );
9524 idPlayer::WriteToSnapshot
9527 void idPlayer::WriteToSnapshot( idBitMsgDelta &msg ) const {
9528 physicsObj.WriteToSnapshot( msg );
9529 WriteBindToSnapshot( msg );
9530 msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
9531 msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
9532 msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
9533 msg.WriteShort( health );
9534 msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, lastDamageDef ), gameLocal.entityDefBits );
9535 msg.WriteDir( lastDamageDir, 9 );
9536 msg.WriteShort( lastDamageLocation );
9537 msg.WriteBits( idealWeapon, idMath::BitsForInteger( MAX_WEAPONS ) );
9538 msg.WriteBits( inventory.weapons, MAX_WEAPONS );
9539 msg.WriteBits( weapon.GetSpawnId(), 32 );
9540 msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
9541 msg.WriteBits( lastHitToggle, 1 );
9542 msg.WriteBits( weaponGone, 1 );
9543 msg.WriteBits( isLagged, 1 );
9544 msg.WriteBits( isChatting, 1 );
9546 /* Needed for the scoreboard */
9547 msg.WriteBits( carryingFlag, 1 );
9550 msg.WriteBits( enviroSuitLight.GetSpawnId(), 32 );
9556 idPlayer::ReadFromSnapshot
9559 void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
9560 int i, oldHealth, newIdealWeapon, weaponSpawnId;
9561 bool newHitToggle, stateHitch;
9563 if ( snapshotSequence - lastSnapshotSequence > 1 ) {
9568 lastSnapshotSequence = snapshotSequence;
9572 physicsObj.ReadFromSnapshot( msg );
9573 ReadBindFromSnapshot( msg );
9574 deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
9575 deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
9576 deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
9577 health = msg.ReadShort();
9578 lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( gameLocal.entityDefBits ) );
9579 lastDamageDir = msg.ReadDir( 9 );
9580 lastDamageLocation = msg.ReadShort();
9581 newIdealWeapon = msg.ReadBits( idMath::BitsForInteger( MAX_WEAPONS ) );
9582 inventory.weapons = msg.ReadBits( MAX_WEAPONS );
9583 weaponSpawnId = msg.ReadBits( 32 );
9584 spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
9585 newHitToggle = msg.ReadBits( 1 ) != 0;
9586 weaponGone = msg.ReadBits( 1 ) != 0;
9587 isLagged = msg.ReadBits( 1 ) != 0;
9588 isChatting = msg.ReadBits( 1 ) != 0;
9590 carryingFlag = msg.ReadBits( 1 ) != 0;
9594 enviroSpawnId = msg.ReadBits( 32 );
9595 enviroSuitLight.SetSpawnId( enviroSpawnId );
9598 // no msg reading below this
9600 if ( weapon.SetSpawnId( weaponSpawnId ) ) {
9601 if ( weapon.GetEntity() ) {
9602 // maintain ownership locally
9603 weapon.GetEntity()->SetOwner( this );
9607 // if not a local client assume the client has all ammo types
9608 if ( entityNumber != gameLocal.localClientNum ) {
9609 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9610 inventory.ammo[ i ] = 999;
9614 if ( oldHealth > 0 && health <= 0 ) {
9616 // so we just hide and don't show a death skin
9617 UpdateDeathSkin( true );
9622 SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
9623 SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
9625 animator.ClearAllJoints();
9626 if ( entityNumber == gameLocal.localClientNum ) {
9627 playerView.Fade( colorBlack, 12000 );
9630 physicsObj.SetMovementType( PM_DEAD );
9631 if ( !stateHitch ) {
9632 StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
9634 if ( weapon.GetEntity() ) {
9635 weapon.GetEntity()->OwnerDied();
9637 } else if ( oldHealth <= 0 && health > 0 ) {
9641 SetPhysics( &physicsObj );
9642 physicsObj.EnableClip();
9643 SetCombatContents( true );
9644 } else if ( health < oldHealth && health > 0 ) {
9646 lastDmgTime = gameLocal.time;
9649 const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
9651 playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
9652 AI_PAIN = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
9653 lastDmgTime = gameLocal.time;
9655 common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
9658 } else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !stateHitch ) {
9659 // just pulse, for any health raise
9664 // If the player is alive, restore proper physics object
9665 if ( health > 0 && IsActiveAF() ) {
9667 SetPhysics( &physicsObj );
9668 physicsObj.EnableClip();
9669 SetCombatContents( true );
9673 if ( idealWeapon != newIdealWeapon ) {
9675 weaponCatchup = true;
9677 idealWeapon = newIdealWeapon;
9681 if ( lastHitToggle != newHitToggle ) {
9682 SetLastHitTime( gameLocal.realClientTime );
9685 if ( msg.HasChanged() ) {
9692 idPlayer::WritePlayerStateToSnapshot
9695 void idPlayer::WritePlayerStateToSnapshot( idBitMsgDelta &msg ) const {
9698 msg.WriteByte( bobCycle );
9699 msg.WriteLong( stepUpTime );
9700 msg.WriteFloat( stepUpDelta );
9702 msg.WriteLong( inventory.weapons );
9704 msg.WriteShort( inventory.weapons );
9706 msg.WriteByte( inventory.armor );
9708 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9709 msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
9711 for( i = 0; i < MAX_WEAPONS; i++ ) {
9712 msg.WriteBits( inventory.clip[i], ASYNC_PLAYER_INV_CLIP_BITS );
9718 idPlayer::ReadPlayerStateFromSnapshot
9721 void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
9724 bobCycle = msg.ReadByte();
9725 stepUpTime = msg.ReadLong();
9726 stepUpDelta = msg.ReadFloat();
9728 inventory.weapons = msg.ReadLong();
9730 inventory.weapons = msg.ReadShort();
9732 inventory.armor = msg.ReadByte();
9734 for( i = 0; i < AMMO_NUMTYPES; i++ ) {
9735 ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
9736 if ( gameLocal.time >= inventory.ammoPredictTime ) {
9737 inventory.ammo[ i ] = ammo;
9740 for( i = 0; i < MAX_WEAPONS; i++ ) {
9741 inventory.clip[i] = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
9747 idPlayer::ServerReceiveEvent
9750 bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
9752 if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
9756 // client->server events
9758 case EVENT_IMPULSE: {
9759 PerformImpulse( msg.ReadBits( 6 ) );
9770 idPlayer::ClientReceiveEvent
9773 bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
9778 case EVENT_EXIT_TELEPORTER:
9779 Event_ExitTeleporter();
9781 case EVENT_ABORT_TELEPORTER:
9782 SetPrivateCameraView( NULL );
9784 case EVENT_POWERUP: {
9785 powerup = msg.ReadShort();
9786 start = msg.ReadBits( 1 ) != 0;
9788 GivePowerUp( powerup, 0 );
9790 ClearPowerup( powerup );
9795 case EVENT_PICKUPNAME: {
9796 char buf[MAX_EVENT_PARAM_SIZE];
9797 msg.ReadString(buf, MAX_EVENT_PARAM_SIZE);
9798 inventory.AddPickupName(buf, "", this); //_D3XP
9802 case EVENT_SPECTATE: {
9803 bool spectate = ( msg.ReadBits( 1 ) != 0 );
9804 Spectate( spectate );
9807 case EVENT_ADD_DAMAGE_EFFECT: {
9809 // if we're spectating, ignore
9810 // happens if the event and the spectate change are written on the server during the same frame (fraglimit)
9813 return idActor::ClientReceiveEvent( event, time, msg );
9816 return idActor::ClientReceiveEvent( event, time, msg );
9827 void idPlayer::Hide( void ) {
9831 weap = weapon.GetEntity();
9833 weap->HideWorldModel();
9842 void idPlayer::Show( void ) {
9846 weap = weapon.GetEntity();
9848 weap->ShowWorldModel();
9854 idPlayer::StartAudioLog
9857 void idPlayer::StartAudioLog( void ) {
9859 hud->HandleNamedEvent( "audioLogUp" );
9865 idPlayer::StopAudioLog
9868 void idPlayer::StopAudioLog( void ) {
9870 hud->HandleNamedEvent( "audioLogDown" );
9879 void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
9883 hud->SetStateString( "tip", tip );
9884 hud->SetStateString( "tiptitle", title );
9885 hud->HandleNamedEvent( "tipWindowUp" );
9887 PostEventSec( &EV_Player_HideTip, 5.0f );
9897 void idPlayer::HideTip( void ) {
9898 hud->HandleNamedEvent( "tipWindowDown" );
9904 idPlayer::Event_HideTip
9907 void idPlayer::Event_HideTip( void ) {
9913 idPlayer::ShowObjective
9916 void idPlayer::ShowObjective( const char *obj ) {
9917 hud->HandleNamedEvent( obj );
9923 idPlayer::HideObjective
9926 void idPlayer::HideObjective( void ) {
9927 hud->HandleNamedEvent( "closeObjective" );
9928 objectiveUp = false;
9933 idPlayer::Event_StopAudioLog
9936 void idPlayer::Event_StopAudioLog( void ) {
9942 idPlayer::SetSpectateOrigin
9945 void idPlayer::SetSpectateOrigin( void ) {
9948 neworig = GetPhysics()->GetOrigin();
9949 neworig[ 2 ] += EyeHeight();
9951 SetOrigin( neworig );
9956 idPlayer::RemoveWeapon
9959 void idPlayer::RemoveWeapon( const char *weap ) {
9960 if ( weap && *weap ) {
9961 inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
9967 idPlayer::CanShowWeaponViewmodel
9970 bool idPlayer::CanShowWeaponViewmodel( void ) const {
9971 return showWeaponViewModel;
9976 idPlayer::SetLevelTrigger
9979 void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
9980 if ( levelName && *levelName && triggerName && *triggerName ) {
9981 idLevelTriggerInfo lti;
9982 lti.levelName = levelName;
9983 lti.triggerName = triggerName;
9984 inventory.levelTriggers.Append( lti );
9990 idPlayer::Event_LevelTrigger
9993 void idPlayer::Event_LevelTrigger( void ) {
9994 idStr mapName = gameLocal.GetMapName();
9995 mapName.StripPath();
9996 mapName.StripFileExtension();
9997 for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
9998 if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
9999 idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
10001 ent->PostEventMS( &EV_Activate, 1, this );
10009 idPlayer::Event_Gibbed
10012 void idPlayer::Event_Gibbed( void ) {
10018 idPlayer::UpdatePlayerIcons
10021 void idPlayer::UpdatePlayerIcons( void ) {
10022 int time = networkSystem->ServerGetClientTimeSinceLastPacket( entityNumber );
10023 if ( time > cvarSystem->GetCVarInteger( "net_clientMaxPrediction" ) ) {
10028 // TODO: chatting, PDA, etc?
10033 idPlayer::DrawPlayerIcons
10036 void idPlayer::DrawPlayerIcons( void ) {
10037 if ( !NeedsIcon() ) {
10038 playerIcon.FreeIcon();
10043 // Never draw icons for hidden players.
10044 if ( this->IsHidden() )
10048 playerIcon.Draw( this, headJoint );
10053 idPlayer::HidePlayerIcons
10056 void idPlayer::HidePlayerIcons( void ) {
10057 playerIcon.FreeIcon();
10062 idPlayer::NeedsIcon
10065 bool idPlayer::NeedsIcon( void ) {
10066 // local clients don't render their own icons... they're only info for other clients
10068 // always draw icons in CTF games
10069 return entityNumber != gameLocal.localClientNum && ( ( g_CTFArrows.GetBool() && gameLocal.mpGame.IsGametypeFlagBased() && !IsHidden() && !AI_DEAD ) || ( isLagged || isChatting ) );
10071 return entityNumber != gameLocal.localClientNum && ( isLagged || isChatting );
10078 idPlayer::DropFlag()
10081 void idPlayer::DropFlag( void ) {
10082 if ( !carryingFlag || !gameLocal.isMultiplayer || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
10085 idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - this->latchedTeam );
10087 idItemTeam * item = static_cast<idItemTeam*>(entity);
10089 if ( item->carried && !item->dropped ) {
10090 item->Drop( health <= 0 );
10091 carryingFlag = false;
10097 void idPlayer::ReturnFlag() {
10099 if ( !carryingFlag || !gameLocal.isMultiplayer || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
10102 idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - this->latchedTeam );
10104 idItemTeam * item = static_cast<idItemTeam*>(entity);
10106 if ( item->carried && !item->dropped ) {
10108 carryingFlag = false;
10113 void idPlayer::FreeModelDef( void ) {
10114 idAFEntity_Base::FreeModelDef();
10115 if ( gameLocal.isMultiplayer && gameLocal.mpGame.IsGametypeFlagBased() )
10116 playerIcon.FreeIcon();