quake4-sdk/source/mpgame/Player.cpp

14157 lines
377 KiB
C++
Raw Normal View History

2007-06-15 00:00:00 +00:00
// RAVEN BEGIN
// bdube: note that this file is no longer merged with Doom3 updates
//
// MERGE_DATE 09/30/2004
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#include "ai/AI.h"
#include "ai/AI_Manager.h"
#include "Weapon.h"
#include "Projectile.h"
#include "vehicle/Vehicle.h"
#include "client/ClientModel.h"
#include "ai/AAS_tactical.h"
#include "Healing_Station.h"
#include "ai/AI_Medic.h"
// RAVEN BEGIN
// nrausch: support for turning the weapon change ui on and off
#ifdef _XENON
#include "../ui/Window.h"
// nrausch: support for direct button input
#include "../sys/xenon/xen_input.h"
#endif
// RAVEN END
idCVar net_predictionErrorDecay( "net_predictionErrorDecay", "112", CVAR_FLOAT | CVAR_GAME | CVAR_NOCHEAT, "time in milliseconds it takes to fade away prediction errors", 0.0f, 200.0f );
idCVar net_showPredictionError( "net_showPredictionError", "-1", CVAR_INTEGER | CVAR_GAME | CVAR_NOCHEAT, "show prediction errors for the given client", -1, MAX_CLIENTS );
/*
===============================================================================
Player control.
This object handles all player movement and world interaction.
===============================================================================
*/
#ifdef _XENON
bool g_ObjectiveSystemOpen = false;
#endif
// distance between ladder rungs (actually is half that distance, but this sounds better)
const int LADDER_RUNG_DISTANCE = 32;
// amount of health per dose from the health station
const int HEALTH_PER_DOSE = 10;
// time before a weapon dropped to the floor disappears
const int WEAPON_DROP_TIME = 20 * 1000;
// time before a next or prev weapon switch happens
const int WEAPON_SWITCH_DELAY = 150;
const float PLAYER_ITEM_DROP_SPEED = 100.0f;
// how many units to raise spectator above default view height so it's in the head of someone
const int SPECTATE_RAISE = 25;
const int HEALTH_PULSE = 1000; // Regen rate and heal leak rate (for health > 100)
const int ARMOR_PULSE = 1000; // armor ticking down due to being higher than maxarmor
const int AMMO_REGEN_PULSE = 1000; // ammo regen in Arena CTF
const int POWERUP_BLINKS = 5; // Number of times the powerup wear off sound plays
const int POWERUP_BLINK_TIME = 1000; // Time between powerup wear off sounds
const float MIN_BOB_SPEED = 5.0f; // minimum speed to bob and play run/walk animations at
const int MAX_RESPAWN_TIME = 10000;
const int RAGDOLL_DEATH_TIME = 3000;
#ifdef _XENON
const int RAGDOLL_DEATH_TIME_XEN_SP = 1000;
const int MAX_RESPAWN_TIME_XEN_SP = 3000;
#endif
const int STEPUP_TIME = 200;
const int MAX_INVENTORY_ITEMS = 20;
const int ARENA_POWERUP_MASK = ( 1 << POWERUP_AMMOREGEN ) | ( 1 << POWERUP_GUARD ) | ( 1 << POWERUP_DOUBLER ) | ( 1 << POWERUP_SCOUT );
//const idEventDef EV_Player_HideDatabaseEntry ( "<hidedatabaseentry>", NULL );
const idEventDef EV_Player_ZoomIn ( "<zoomin>" );
const idEventDef EV_Player_ZoomOut ( "<zoomout>" );
const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
const idEventDef EV_Player_SetViewAngles( "setViewAngles", "v" );
const idEventDef EV_Player_StopFxFov( "stopFxFov" );
const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
const idEventDef EV_Player_HideTip( "hideTip" );
const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
const idEventDef EV_Player_GetViewPos("getViewPos", NULL, 'v');
const idEventDef EV_Player_FinishHearingLoss ( "<finishHearingLoss>", "f" );
const idEventDef EV_Player_GetAmmoData( "getAmmoData", "s", 'v');
const idEventDef EV_Player_RefillAmmo( "refillAmmo" );
const idEventDef EV_Player_SetExtraProjPassEntity( "setExtraProjPassEntity", "E" );
const idEventDef EV_Player_SetArmor( "setArmor", "f" );
const idEventDef EV_Player_DamageEffect( "damageEffect", "sE" );
const idEventDef EV_Player_AllowFallDamage( "allowFallDamage", "d" );
// mekberg: allow enabling/disabling of objectives
const idEventDef EV_Player_EnableObjectives( "enableObjectives" );
const idEventDef EV_Player_DisableObjectives( "disableObjectives" );
// mekberg: don't suppress showing of new objectives anymore
const idEventDef EV_Player_AllowNewObjectives( "<allownewobjectives>" );
// RAVEN END
CLASS_DECLARATION( idActor, idPlayer )
// EVENT( EV_Player_HideDatabaseEntry, idPlayer::Event_HideDatabaseEntry )
EVENT( EV_Player_ZoomIn, idPlayer::Event_ZoomIn )
EVENT( EV_Player_ZoomOut, idPlayer::Event_ZoomOut )
EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
EVENT( EV_Player_SetViewAngles, idPlayer::Event_SetViewAngles )
EVENT( EV_Player_StopFxFov, idPlayer::Event_StopFxFov )
EVENT( EV_Player_EnableWeapon, idPlayer::Event_EnableWeapon )
EVENT( EV_Player_DisableWeapon, idPlayer::Event_DisableWeapon )
EVENT( EV_Player_GetCurrentWeapon, idPlayer::Event_GetCurrentWeapon )
EVENT( EV_Player_GetPreviousWeapon, idPlayer::Event_GetPreviousWeapon )
EVENT( EV_Player_SelectWeapon, idPlayer::Event_SelectWeapon )
EVENT( EV_Player_GetWeaponEntity, idPlayer::Event_GetWeaponEntity )
EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
EVENT( EV_Player_GetViewPos, idPlayer::Event_GetViewPos )
EVENT( EV_Player_FinishHearingLoss, idPlayer::Event_FinishHearingLoss )
EVENT( EV_Player_GetAmmoData, idPlayer::Event_GetAmmoData )
EVENT( EV_Player_RefillAmmo, idPlayer::Event_RefillAmmo )
EVENT( EV_Player_AllowFallDamage, idPlayer::Event_AllowFallDamage )
// mekberg: allow enabling/disabling of objectives
EVENT ( EV_Player_EnableObjectives, idPlayer::Event_EnableObjectives )
EVENT ( EV_Player_DisableObjectives, idPlayer::Event_DisableObjectives )
// mekberg: don't suppress showing of new objectives anymore
EVENT ( EV_Player_AllowNewObjectives, idPlayer::Event_AllowNewObjectives )
// RAVEN END
EVENT( AI_EnableTarget, idPlayer::Event_EnableTarget )
EVENT( AI_DisableTarget, idPlayer::Event_DisableTarget )
EVENT( EV_ApplyImpulse, idPlayer::Event_ApplyImpulse )
// RAVEN BEGIN
// mekberg: sethealth on player.
EVENT( AI_SetHealth, idPlayer::Event_SetHealth )
//MCG: setArmor
EVENT( EV_Player_SetArmor, idPlayer::Event_SetArmor )
// RAVEN END;
EVENT( EV_Player_SetExtraProjPassEntity,idPlayer::Event_SetExtraProjPassEntity )
//MCG: direct damage
EVENT( EV_Player_DamageEffect, idPlayer::Event_DamageEffect )
END_CLASS
// RAVEN BEGIN
// asalmon: Xenon weapon combo system
#ifdef _XENON
nextWeaponCombo_t weaponComboChart[12] = {
// up, down, left, right
{1,3,2,4}, // 0: empty slot (select none)
{10,0,1,1},
{2,2,5,0},
{0,7,3,3},
{4,4,0,9},
{5,5,6,2},
{6,6,6,5},
{3,7,7,7},
{8,8,9,8},
{9,9,4,8},
{10,1,10,10},
{0,0,0,0}
};
#endif
// RAVEN END
const idVec4 marineHitscanTint( 0.69f, 1.0f, 0.4f, 1.0f );
const idVec4 stroggHitscanTint( 1.0f, 0.5f, 0.0f, 1.0f );
const idVec4 defaultHitscanTint( 0.4f, 1.0f, 0.4f, 1.0f );
/*
==============
idInventory::Clear
==============
*/
void idInventory::Clear( void ) {
maxHealth = 0;
weapons = 0;
carryOverWeapons = 0;
powerups = 0;
armor = 0;
maxarmor = 0;
secretAreasDiscovered = 0;
memset( ammo, 0, sizeof( ammo ) );
ClearPowerUps();
memset( weaponMods, 0, sizeof(weaponMods) );
// 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
memset( clip, -1, sizeof( clip ) );
items.DeleteContents( true );
pdas.Clear();
videos.Clear();
levelTriggers.Clear();
nextItemPickup = 0;
nextItemNum = 1;
onePickupTime = 0;
objectiveNames.Clear();
ammoPredictTime = 0;
lastGiveTime = 0;
memset( ammoRegenStep, -1, sizeof( int ) * MAX_WEAPONS );
memset( ammoIndices, -1, sizeof( int ) * MAX_WEAPONS );
memset( startingAmmo, -1, sizeof( int ) * MAX_WEAPONS );
memset( ammoRegenTime, -1, sizeof( int ) * MAX_WEAPONS );
}
/*
==============
idInventory::GivePowerUp
==============
*/
void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
powerups |= 1 << powerup;
powerupEndTime[ powerup ] = msec == -1 ? -1 : (gameLocal.time + msec);
}
/*
==============
idInventory::ClearPowerUps
==============
*/
void idInventory::ClearPowerUps( void ) {
int i;
for ( i = 0; i < POWERUP_MAX; i++ ) {
powerupEndTime[ i ] = 0;
}
powerups = 0;
}
/*
==============
idInventory::GetPersistantData
==============
*/
void idInventory::GetPersistantData( idDict &dict ) {
int i;
int num;
idDict *item;
idStr key;
const idKeyValue *kv;
const char *name;
// armor
dict.SetInt( "armor", armor );
// ammo
for( i = 0; i < MAX_AMMOTYPES; i++ ) {
name = rvWeapon::GetAmmoNameForIndex( i );
if ( name ) {
dict.SetInt( name, ammo[ i ] );
}
}
// items
num = 0;
for( i = 0; i < items.Num(); i++ ) {
item = items[ i ];
// copy all keys with "inv_"
kv = item->MatchPrefix( "inv_" );
if ( kv ) {
while( kv ) {
sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
dict.Set( key, kv->GetValue() );
kv = item->MatchPrefix( "inv_", kv );
}
num++;
}
}
dict.SetInt( "items", num );
// weapons
dict.SetInt( "weapon_bits", weapons );
// weapon mods
for ( i = 0; i < MAX_WEAPONS; i++ ) {
dict.SetInt( va( "weapon_mods_%i", i ), weaponMods[ i ] );
}
dict.SetInt( "levelTriggers", levelTriggers.Num() );
for ( i = 0; i < levelTriggers.Num(); i++ ) {
sprintf( key, "levelTrigger_Level_%i", i );
dict.Set( key, levelTriggers[i].levelName );
sprintf( key, "levelTrigger_Trigger_%i", i );
dict.Set( key, levelTriggers[i].triggerName );
}
}
/*
==============
idInventory::RestoreInventory
==============
*/
void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
int i;
int num;
idDict *item;
idStr key;
idStr itemname;
const idKeyValue *kv;
const char *name;
//We might not need to clear it out.
//Clear();
// health/armor
maxHealth = dict.GetInt( "maxhealth", "100" );
armor = dict.GetInt( "armor", "50" );
maxarmor = dict.GetInt( "maxarmor", "100" );
// ammo
for( i = 0; i < MAX_AMMOTYPES; i++ ) {
name = rvWeapon::GetAmmoNameForIndex ( i );
if ( name ) {
ammo[ i ] = dict.GetInt( name );
}
}
// items
num = dict.GetInt( "items" );
items.SetNum( num );
for( i = 0; i < num; i++ ) {
item = new idDict();
items[ i ] = item;
sprintf( itemname, "item_%i ", i );
kv = dict.MatchPrefix( itemname );
while( kv ) {
key = kv->GetKey();
key.Strip( itemname );
item->Set( key, kv->GetValue() );
kv = dict.MatchPrefix( itemname, kv );
}
}
// weapons are stored as a number for persistant data, but as strings in the entityDef
weapons = dict.GetInt( "weapon_bits", "0" );
// RAVEN BEGIN
// mekberg: removed nightmare weapon check.
Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
// RAVEN END
// weapon mods
for ( i = 0; i < MAX_WEAPONS; i++ ) {
weaponMods[ i ] = dict.GetInt( va( "weapon_mods_%i", i ) );
}
// forcefully invalidate the weapon
owner->GiveWeaponMods( 0 );
num = dict.GetInt( "levelTriggers" );
for ( i = 0; i < num; i++ ) {
sprintf( itemname, "levelTrigger_Level_%i", i );
idLevelTriggerInfo lti;
lti.levelName = dict.GetString( itemname );
sprintf( itemname, "levelTrigger_Trigger_%i", i );
lti.triggerName = dict.GetString( itemname );
levelTriggers.Append( lti );
}
}
/*
==============
idInventory::Save
==============
*/
void idInventory::Save( idSaveGame *savefile ) const {
int i;
savefile->WriteInt( maxHealth );
savefile->WriteInt( weapons );
savefile->WriteInt( powerups );
savefile->WriteInt( armor );
savefile->WriteInt( maxarmor );
for( i = 0; i < MAX_AMMO; i++ ) {
savefile->WriteInt( ammo[ i ] );
}
for( i = 0; i < MAX_WEAPONS; i++ ) {
savefile->WriteInt( clip[ i ] );
savefile->WriteInt( weaponMods[i] );
}
for( i = 0; i < POWERUP_MAX; i++ ) {
savefile->WriteInt( powerupEndTime[ i ] );
}
savefile->WriteInt( ammoPredictTime );
savefile->WriteInt( lastGiveTime );
// Save Items
savefile->WriteInt( items.Num() );
for( i = 0; i < items.Num(); i++ ) {
savefile->WriteDict( items[ i ] );
}
// TOSAVE: idStrList pdas;
// TOSAVE: idStrList pdaSecurity;
// TOSAVE: idStrList videos;
// Save level triggers
savefile->WriteInt( levelTriggers.Num() );
for ( i = 0; i < levelTriggers.Num(); i++ ) {
savefile->WriteString( levelTriggers[i].levelName );
savefile->WriteString( levelTriggers[i].triggerName );
}
savefile->WriteInt( nextItemPickup );
savefile->WriteInt( nextItemNum );
savefile->WriteInt( onePickupTime );
// Save pick up item names
savefile->WriteInt( pickupItemNames.Num() );
for( i = 0; i < pickupItemNames.Num(); i++ ) {
savefile->WriteString( pickupItemNames[ i ].name );
savefile->WriteString( pickupItemNames[ i ].icon );
}
// Save objectives
savefile->WriteInt( objectiveNames.Num() );
for( i = 0; i < objectiveNames.Num(); i++ ) {
savefile->WriteString( objectiveNames[i].screenshot );
savefile->WriteString( objectiveNames[i].text );
savefile->WriteString( objectiveNames[i].title );
}
/*
// Save database
savefile->WriteInt ( database.Num() );
for ( i = 0; i < database.Num(); i ++ ) {
savefile->WriteString ( database[i].title );
savefile->WriteString ( database[i].text );
savefile->WriteString ( database[i].image );
savefile->WriteString ( database[i].filter );
}
*/
savefile->WriteInt( secretAreasDiscovered );
savefile->WriteSyncId();
}
/*
==============
idInventory::Restore
==============
*/
void idInventory::Restore( idRestoreGame *savefile ) {
int i, num;
savefile->ReadInt( maxHealth );
savefile->ReadInt( weapons );
savefile->ReadInt( powerups );
savefile->ReadInt( armor );
savefile->ReadInt( maxarmor );
for( i = 0; i < MAX_AMMO; i++ ) {
savefile->ReadInt( ammo[ i ] );
}
for( i = 0; i < MAX_WEAPONS; i++ ) {
savefile->ReadInt( clip[ i ] );
savefile->ReadInt( weaponMods[i] );
}
for( i = 0; i < POWERUP_MAX; i++ ) {
savefile->ReadInt( powerupEndTime[ i ] );
}
savefile->ReadInt( ammoPredictTime );
savefile->ReadInt( lastGiveTime );
// Load Items
savefile->ReadInt( num );
for( i = 0; i < num; i++ ) {
idDict *itemdict = new idDict;
savefile->ReadDict( itemdict );
items.Append( itemdict );
}
// TORESTORE: idStrList pdas;
// TORESTORE: idStrList pdaSecurity;
// TORESTORE: idStrList videos;
// Load level triggers
savefile->ReadInt( num );
for ( i = 0; i < num; i++ ) {
idLevelTriggerInfo lti;
savefile->ReadString( lti.levelName );
savefile->ReadString( lti.triggerName );
levelTriggers.Append( lti );
}
savefile->ReadInt( nextItemPickup );
savefile->ReadInt( nextItemNum );
savefile->ReadInt( onePickupTime );
// Load pickup items
savefile->ReadInt( num );
for ( i = 0; i < num; i++ ) {
idItemInfo itemInfo;
savefile->ReadString( itemInfo.name );
savefile->ReadString( itemInfo.icon );
pickupItemNames.Append( itemInfo );
}
// Load objectives
savefile->ReadInt( num );
for( i = 0; i < num; i++ ) {
idObjectiveInfo obj;
savefile->ReadString( obj.screenshot );
savefile->ReadString( obj.text );
savefile->ReadString( obj.title );
objectiveNames.Append( obj );
}
/*
// Load database
savefile->ReadInt ( num );
for ( i = 0; i < num; i++ ) {
rvDatabaseEntry entry;
savefile->ReadString ( entry.title );
savefile->ReadString ( entry.text );
savefile->ReadString ( entry.image );
savefile->ReadString ( entry.filter );
database.Append ( entry );
}
*/
savefile->ReadInt( secretAreasDiscovered );
savefile->ReadSyncId( "idInventory::Restore" );
}
/*
==============
idInventory::AmmoIndexForAmmoClass
==============
*/
int idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
return rvWeapon::GetAmmoIndexForName( ammo_classname );
}
/*
==============
idInventory::MaxAmmoForAmmoClass
==============
*/
int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
}
/*
==============
idInventory::AmmoIndexForWeaponClass
==============
*/
int idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !decl ) {
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
}
if ( ammoRequired ) {
*ammoRequired = decl->dict.GetInt( "ammoRequired" );
}
return AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
}
/*
==============
idInventory::AmmoClassForWeaponClass
==============
*/
const char * idInventory::AmmoClassForWeaponClass( const char *weapon_classname ) {
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !decl ) {
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
}
return decl->dict.GetString( "ammoType" );
}
// RAVEN BEGIN
// mekberg: if the player can pick up ammo at this time
/*
==============
idInventory::DetermineAmmoAvailability
==============
*/
bool idInventory::DetermineAmmoAvailability( idPlayer* owner, const char *ammoName, int ammoIndex, int ammoAmount, int ammoMax ) {
const idDeclEntityDef *weaponDecl = NULL;
const idDict* weaponDict = NULL;
const char* mod = NULL;
const idDict* modDict = NULL;
int weaponIndex = -1;
int clipSize = 0;
int modClipSize = 0;
int difference = 0;
idStr realAmmoName( ammoName );
// Early out
if ( ammo[ ammoIndex ] == ammoMax ) {
return false;
}
// Make sure the clip info is updated.
if ( owner->weapon ) {
clip[ owner->GetCurrentWeapon( ) ] = owner->weapon->AmmoInClip( );
}
if ( !idStr::Icmpn( ammoName, "start_ammo_", 11 ) ) {
realAmmoName.StripLeading( "start_" );
}
// Find the entityDef for the weapon that uses this ammo.
for ( int i = 0; i < MAX_WEAPONS; i++ ) {
if ( ! ( weapons & ( 1 << i ) ) ) {
continue;
}
weaponDecl = owner->GetWeaponDef( i );
if ( !weaponDecl ) {
continue;
}
if ( !idStr::Icmp ( weaponDecl->dict.GetString( "ammoType" ), realAmmoName.c_str() ) ) {
weaponDict = &( weaponDecl->dict );
weaponIndex = i;
break;
}
}
// If we didn't find one.
if ( weaponIndex == -1 ) {
// If we are picking up ammo and we aren't currently full.
if ( ammoAmount && ammo[ ammoIndex ] != ammoMax ) {
ammo[ ammoIndex ] += ammoAmount;
if ( ammo[ ammoIndex ] > ammoMax ) {
ammo[ ammoIndex ] = ammoMax;
}
return true;
}
return false;
}
clipSize = weaponDict->GetInt( "clipSize", "0" );
// Find the weaponmods for this weapon and see if we have any clipsize mods.
for ( int m = 0; m < MAX_WEAPONMODS; m ++ ) {
if ( ! ( weaponMods[ weaponIndex ] & ( 1 << m ) ) ) {
continue;
}
mod = weaponDict->GetString ( va ( "def_mod%d" , m + 1 ) );
if ( !mod || !*mod ) {
break;
}
modDict = gameLocal.FindEntityDefDict ( mod, false );
modClipSize = modDict->GetInt( "clipSize", "0" );
if ( modClipSize > clipSize ) {
clipSize = modClipSize;
}
}
// Don't bother with these checks if we don't have a clipsize
if ( clipSize ) {
difference = ( ammoMax - clipSize ) - ( ammo[ ammoIndex ] - clip[ weaponIndex ] );
if ( difference ) {
if ( ammoAmount > difference ) {
ammo[ ammoIndex ] += difference;
} else {
ammo[ ammoIndex ] += ammoAmount;
}
return true;
} else {
return false;
}
} else if ( ( ammo[ ammoIndex ] + ammoAmount ) > ammoMax ) {
ammo[ ammoIndex ] = ammoMax;
} else {
ammo[ ammoIndex ] += ammoAmount;
}
return true;
}
// RAVEN END
/*
==============
idInventory::AmmoIndexForWeaponIndex
==============
*/
int idInventory::AmmoIndexForWeaponIndex( int weaponIndex ) {
if( ammoIndices[ weaponIndex ] == -1 ) {
const idDict* playerDict = gameLocal.FindEntityDefDict( "player_marine", false );
if( !playerDict ) {
gameLocal.Error( "idInventory::AmmoIndexForWeaponIndex() - Can't find player def\n" );
return -1;
}
ammoIndices[ weaponIndex ] = AmmoIndexForWeaponClass( playerDict->GetString( va( "def_weapon%d", weaponIndex ) ) );
}
return ammoIndices[ weaponIndex ];
}
/*
==============
idInventory::StartingAmmoForWeaponIndex
==============
*/
int idInventory::StartingAmmoForWeaponIndex( int weaponIndex ) {
if( startingAmmo[ weaponIndex ] == -1 ) {
const idDict* playerDict = gameLocal.FindEntityDefDict( "player_marine", false );
if( !playerDict ) {
gameLocal.Error( "idInventory::StartingAmmoForWeaponIndex() - Can't find player def\n" );
return -1;
}
const idDict* weaponDict = gameLocal.FindEntityDefDict( playerDict->GetString( va( "def_weapon%d", weaponIndex ) ), false );
if( !weaponDict ) {
gameLocal.Warning( "idInventory::StartingAmmoForWeaponIndex() - Unknown weapon '%d'\n", weaponIndex );
return -1;
}
const idKeyValue* kv = weaponDict->MatchPrefix( "inv_start_ammo" );
if( kv == NULL ) {
startingAmmo[ weaponIndex ] = 1;
} else {
startingAmmo[ weaponIndex ] = atoi( kv->GetValue() );
kv = weaponDict->MatchPrefix( "inv_start_ammo", kv );
if( kv != NULL ) {
gameLocal.Error( "idInventory::StartingAmmoForWeaponIndex() - Weapon dict for player's def_weapon%d has multiple inv_start_ammo entries\n", weaponIndex );
return -1;
}
}
}
return startingAmmo[ weaponIndex ];
}
/*
==============
idInventory::AmmoRegenStepForWeaponIndex
==============
*/
int idInventory::AmmoRegenStepForWeaponIndex( int weaponIndex ) {
if( ammoRegenStep[ weaponIndex ] == -1 ) {
const idDict* playerDict = gameLocal.FindEntityDefDict( "player_marine", false );
if( !playerDict ) {
gameLocal.Error( "idInventory::AmmoRegenStepForWeaponIndex() - Can't find player def\n" );
return -1;
}
const idDict* weaponDict = gameLocal.FindEntityDefDict( playerDict->GetString( va( "def_weapon%d", weaponIndex ) ), false );
if( !weaponDict ) {
gameLocal.Warning( "idInventory::AmmoRegenStepForWeaponIndex() - Unknown weapon '%d'\n", weaponIndex );
return -1;
}
ammoRegenStep[ weaponIndex ] = weaponDict->GetInt( "ammoRegenStep", "1" );
}
return ammoRegenStep[ weaponIndex ];
}
/*
==============
idInventory::AmmoRegenTimeForAmmoIndex
==============
*/
int idInventory::AmmoRegenTimeForWeaponIndex( int weaponIndex ) {
if ( ammoRegenTime[ weaponIndex ] == -1 ) {
const idDict* playerDict = gameLocal.FindEntityDefDict( "player_marine", false );
if( !playerDict ) {
gameLocal.Error( "idInventory::AmmoRegenTimeForWeaponIndex() - Can't find player def\n" );
return -1;
}
const idDict* weaponDict = gameLocal.FindEntityDefDict( playerDict->GetString( va( "def_weapon%d", weaponIndex ) ), false );
if( !weaponDict ) {
gameLocal.Warning( "idInventory::AmmoRegenTimeForWeaponIndex() - Unknown weapon '%d'\n", weaponIndex );
return -1;
}
ammoRegenTime[ weaponIndex ] = weaponDict->GetInt( "ammoRegenTime", "1" );
}
return ammoRegenTime[ weaponIndex ];
}
/*
==============
idInventory::Give
If checkOnly is true, check only for possibility of adding to inventory, don't actually add
==============
*/
bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud, bool dropped, bool checkOnly ) {
int i;
const char *pos;
const char *end;
int len;
idStr weaponString;
int max;
int amount;
if ( owner->IsFakeClient() ) {
return false;
}
if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
i = AmmoIndexForAmmoClass( statname );
max = MaxAmmoForAmmoClass( owner, statname );
amount = atoi( value );
// RAVEN BEGIN
// mekberg: check max ammo vs clipsize when picking up ammo
if ( !gameLocal.IsMultiplayer ( ) ) {
return DetermineAmmoAvailability ( owner, statname, i, amount, max );
} else if ( ammo[ i ] >= max ) {
return false;
}
if ( amount && !checkOnly ) {
ammo[ i ] += amount;
if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
ammo[ i ] = max;
}
}
// RAVEN END
} else if ( !idStr::Icmpn( statname, "start_ammo_", 11 ) ) {
// starting ammo gives only if current ammo is below it
idStr ammoname( statname );
ammoname.StripLeading( "start_" );
i = AmmoIndexForAmmoClass( ammoname.c_str() );
max = MaxAmmoForAmmoClass( owner, ammoname.c_str() );
amount = atoi( value );
// RAVEN BEGIN
// mekberg: check max ammo vs clipsize when picking up ammo
if ( !gameLocal.IsMultiplayer ( ) ) {
return DetermineAmmoAvailability ( owner, statname, i, amount, max );
} else if ( amount ) {
if ( ammo[ i ] >= amount ) {
amount = 1;
} else {
amount = amount - ammo[ i ];
}
}
if ( amount && !checkOnly ) {
ammo[ i ] += amount;
if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
ammo[ i ] = max;
}
}
// RAVEN END
} else if ( !idStr::Icmp( statname, "armor" ) ) {
if ( armor >= maxarmor * 2 ) {
return false;
}
} else if ( !idStr::Icmp( statname, "health" ) ) {
if ( owner->health >= maxHealth ) {
return false;
}
} else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
i = owner->SlotForWeapon ( statname + 7 );
if ( i != -1 && !checkOnly ) {
// set, don't add. not going over the clip size limit.
clip[ i ] = atoi( value );
}
} else if ( !idStr::Icmp( statname, "quad" ) && !checkOnly ) {
GivePowerUp( owner, POWERUP_QUADDAMAGE, SEC2MS( atof( value ) ) );
} else if ( !idStr::Icmp( statname, "regen" ) && !checkOnly ) {
GivePowerUp( owner, POWERUP_REGENERATION, SEC2MS( atof( value ) ) );
} else if ( !idStr::Icmp( statname, "haste" ) && !checkOnly ) {
GivePowerUp( owner, POWERUP_HASTE, SEC2MS( atof( value ) ) );
} else if( !idStr::Icmp( statname, "ammoregen" ) && !checkOnly ) {
GivePowerUp( owner, POWERUP_AMMOREGEN, -1 );
} else if ( !idStr::Icmp( statname, "weapon" ) ) {
bool tookWeapon = false;
for( pos = value; pos != NULL; pos = end ) {
end = strchr( pos, ',' );
if ( end ) {
len = end - pos;
end++;
} else {
len = strlen( pos );
}
idStr weaponName( pos, 0, len );
// find the number of the matching weapon names
i = owner->SlotForWeapon ( weaponName );
if ( i == -1 ) {
gameLocal.Warning( "Unknown weapon '%s'", weaponName.c_str() );
return false;
}
if ( gameLocal.isMultiplayer
&& ( weapons & ( 1 << i ) ) ) {
//already have this weapon
if ( !dropped ) {
//a placed weapon item
if ( gameLocal.IsWeaponsStayOn() ) {
//don't pick up weapons at all if you already have them...
continue;
}
}
// don't pickup "no ammo" weapon types twice
// not for singleplayer.. there is only one case in the game where you can get a no ammo
// weapon when you might already have it, in that case it is more consistent to pick it up
// cache the media for this weapon
const idDict* dict;
dict = &owner->GetWeaponDef ( i )->dict;
if ( dict && !dict->GetInt( "ammoRequired" ) ) {
continue;
}
}
if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) ) {
if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
if ( ( owner->GetUserInfo()->GetBool( "ui_autoSwitch" )
|| ( gameLocal.isMultiplayer && gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) )
&& idealWeapon && !checkOnly )
{
// client prediction should not get here
assert( !gameLocal.isClient );
*idealWeapon = i;
}
if ( owner->hud && updateHud && lastGiveTime + 1000 < gameLocal.time && !checkOnly ) {
owner->hud->SetStateInt( "newWeapon", i );
owner->hud->HandleNamedEvent( "newWeapon" );
lastGiveTime = gameLocal.time;
}
if ( !checkOnly ) {
weapons |= ( 1 << i );
}
tookWeapon = true;
}
}
}
return tookWeapon;
} else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
// ignore these as they're handled elsewhere
return false;
} else {
// unknown item
gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
return false;
}
return true;
}
/*
===============
idInventoy::Drop
===============
*/
void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
// remove the weapon bit
// also remove the ammo associated with the weapon as we pushed it in the item
assert( weapon_index != -1 || weapon_classname );
if ( weapon_index == -1 ) {
for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
break;
}
}
if ( weapon_index >= MAX_WEAPONS ) {
gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
}
} else if ( !weapon_classname ) {
weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
}
weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
int ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
if ( ammo_i ) {
clip[ weapon_index ] = -1;
ammo[ ammo_i ] = 0;
}
weaponMods[weapon_index] = 0;
}
/*
===============
idInventory::HasAmmo
===============
*/
int idInventory::HasAmmo( int index, int amount ) {
if ( ( index == 0 ) || !amount ) {
// always allow weapons that don't use ammo to fire
return -1;
}
// check if we have infinite ammo
if ( ammo[ index ] < 0 ) {
return -1;
}
// return how many shots we can fire
return ammo[ index ] / amount;
}
/*
===============
idInventory::HasAmmo
===============
*/
int idInventory::HasAmmo( const char *weapon_classname ) {
int ammoRequired;
int index;
index = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
return HasAmmo( index, ammoRequired );
}
/*
===============
idInventory::UseAmmo
===============
*/
bool idInventory::UseAmmo( int index, int amount ) {
if ( !HasAmmo( index, amount ) ) {
return false;
}
// take an ammo away if not infinite
if ( ammo[ index ] >= 0 ) {
ammo[ index ] -= amount;
ammoPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots
}
return true;
}
/*
==============
idPlayer::idPlayer
==============
*/
idPlayer::idPlayer() {
memset( &usercmd, 0, sizeof( usercmd ) );
alreadyDidTeamAnnouncerSound = false;
noclip = false;
godmode = false;
undying = g_forceUndying.GetBool() ? !gameLocal.isMultiplayer : false;
spawnAnglesSet = false;
spawnAngles = ang_zero;
viewAngles = ang_zero;
deltaViewAngles = ang_zero;
cmdAngles = ang_zero;
demoViewAngleTime = 0;
demoViewAngles = ang_zero;
oldButtons = 0;
buttonMask = 0;
oldFlags = 0;
lastHitFrame = 0;
lastHitArmor = false;
lastSavingThrowTime = 0;
weapon = NULL;
hud = NULL;
mphud = NULL;
objectiveSystem = NULL;
objectiveSystemOpen = false;
showNewObjectives = false;
#ifdef _XENON
g_ObjectiveSystemOpen = false;
#endif
objectiveButtonReleased = false;
cinematicHud = NULL;
overlayHud = NULL;
overlayHudTime = 0;
lastDmgTime = 0;
deathClearContentsTime = 0;
nextHealthPulse = 0;
scoreBoardOpen = false;
forceScoreBoard = false;
forceScoreBoardTime = 0;
forceRespawn = false;
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
allowedToRespawn = true;
// squirrel: Mode-agnostic buymenus
inBuyZone = false;
inBuyZonePrev = false;
// RITUAL END
spectating = false;
spectator = 0;
forcedReady = false;
wantSpectate = false;
minRespawnTime = 0;
maxRespawnTime = 0;
firstPersonViewOrigin = vec3_zero;
firstPersonViewAxis = mat3_identity;
hipJoint = INVALID_JOINT;
chestJoint = INVALID_JOINT;
headJoint = INVALID_JOINT;
neckLeanJoint = INVALID_JOINT;
bobFoot = 0;
bobFrac = 0.0f;
bobfracsin = 0.0f;
bobCycle = 0;
xyspeed = 0.0f;
stepUpTime = 0;
stepUpDelta = 0.0f;
idealLegsYaw = 0.0f;
legsYaw = 0.0f;
legsForward = true;
oldViewYaw = 0.0f;
viewBobAngles = ang_zero;
viewBob = vec3_zero;
landChange = 0;
landTime = 0;
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
carryOverCurrentWeapon = -1;
// RITUAL END
currentWeapon = -1;
idealWeapon = -1;
previousWeapon = -1;
weaponSwitchTime = 0;
weaponEnabled = true;
showWeaponViewModel = true;
oldInventoryWeapons = 0;
// RAVEN BEGIN
// mekberg: allow disabling of objectives during non-cinematic time periods
objectivesEnabled = true;
// RAVEN END
skin = NULL;
weaponViewSkin = NULL;
headSkin = NULL;
powerUpSkin = NULL;
numProjectilesFired = 0;
numProjectileHits = 0;
airless = false;
airTics = 0;
lastAirDamage = 0;
gibDeath = false;
gibsLaunched = false;
gibDir = vec3_zero;
centerView.Init( 0, 0, 0, 0 );
fxFov = false;
influenceFov = 0;
influenceActive = 0;
influenceRadius = 0.0f;
influenceEntity = NULL;
influenceMaterial = NULL;
influenceSkin = NULL;
privateCameraView = NULL;
memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
memset( loggedAccel, 0, sizeof( loggedAccel ) );
currentLoggedAccel = 0;
focusTime = 0;
focusUI = NULL;
focusEnt = NULL;
focusType = FOCUS_NONE;
focusBrackets = NULL;
focusBracketsTime = 0;
talkingNPC = NULL;
cursor = NULL;
talkCursor = 0;
oldMouseX = 0;
oldMouseY = 0;
lastDamageDef = 0;
lastDamageDir = vec3_zero;
lastDamageLocation = 0;
predictedFrame = 0;
predictedOrigin = vec3_zero;
predictedAngles = ang_zero;
predictedUpdated = false;
predictionOriginError = vec3_zero;
predictionAnglesError = ang_zero;
predictionErrorTime = 0;
fl.networkSync = true;
latchedTeam = -1;
hudTeam = -1;
doingDeathSkin = false;
weaponGone = false;
useInitialSpawns = false;
lastSpectateTeleport = 0;
hiddenWeapon = false;
tipUp = false;
objectiveUp = false;
teleportEntity = NULL;
teleportKiller = -1;
lastKiller = NULL;
respawning = false;
ready = false;
leader = false;
lastSpectateChange = 0;
lastArenaChange = 0;
lastTeleFX = -9999;
weaponCatchup = false;
lastSnapshotSequence = 0;
aimClientNum = -1;
spawnedTime = 0;
isTelefragged = false;
isLagged = false;
isChatting = false;
intentDir.Zero();
aasSensor = rvAASTacticalSensor::CREATE_SENSOR(this);
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
ResetCash();
// RITUAL END
zoomFov.Init ( 0, 0, DefaultFov(), DefaultFov() );
zoomed = false;
memset ( cachedWeaponDefs, 0, sizeof(cachedWeaponDefs) );
memset ( cachedPowerupDefs, 0, sizeof(cachedPowerupDefs) );
lastImpulsePlayer = NULL;
lastImpulseTime = gameLocal.time;
weaponChangeIconsUp = false;
reloadModel = false;
modelDecl = NULL;
disableHud = false;
mutedPlayers = 0;
friendPlayers = 0;
connectTime = 0;
rank = -1;
arena = 0;
memset( nextAmmoRegenPulse, 0, sizeof( int ) * MAX_AMMO );
spectator = 0;
quadOverlay = NULL;
hasteOverlay = NULL;
regenerationOverlay = NULL;
invisibilityOverlay = NULL;
powerUpOverlay = NULL;
tourneyStatus = PTS_UNKNOWN;
vsMsgState = false;
deathSkinTime = 0;
lastPickupTime = 0;
int i;
for ( i = 0; i < MAX_CONCURRENT_VOICES; i++ ) {
voiceDest[i] = -1;
voiceDestTimes[i] = 0;
}
itemCosts = NULL;
teamHealthRegen = NULL;
teamHealthRegenPending = false;
teamAmmoRegen = NULL;
teamAmmoRegenPending = false;
teamDoubler = NULL;
teamDoublerPending = false;
leanVelocity = vec3_zero;
leanMaxSpeed = 0.0f;
leanBlendRatio = 0.0f;
leanMaxLateralAngle = 0.0f;
leanMaxForwardAngle = 0.0f;
leanHeadRatio = 0.0f;
leanHipRatio = 0.0f;
ignoreOnGroundUntilFrame = 0;
prevOnGround = true;
clientIdealWeaponPredictFrame = -1;
serverReceiveEvent = false;
}
/*
==============
idPlayer::SetShowHud
==============
*/
void idPlayer::SetShowHud( bool showHud ) {
disableHud = !showHud;
}
/*
==============
idPlayer::SetShowHud
==============
*/
bool idPlayer::GetShowHud( void ) {
return !disableHud;
}
/*
==============
idPlayer::SetWeapon
==============
*/
void idPlayer::SetWeapon( int weaponIndex ) {
if ( weapon && weaponIndex == currentWeapon ) {
return;
}
// Clear the weapon entity
delete weapon;
weapon = NULL;
previousWeapon = currentWeapon;
currentWeapon = weaponIndex;
weaponGone = false;
if ( weaponIndex < 0 ) {
weaponGone = true;
return;
}
animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
idTypeInfo* typeInfo;
weaponDef = GetWeaponDef( currentWeapon );
if ( !weaponDef ) {
gameLocal.Error( "Weapon definition not found for weapon %d", currentWeapon ) ;
}
typeInfo = idClass::GetClass( weaponDef->dict.GetString( "weaponclass", "rvWeapon" ) );
if ( !typeInfo || !typeInfo->IsType( rvWeapon::GetClassType() ) ) {
gameLocal.Error( "Invalid weapon class '%s' specified for weapon '%s'", animPrefix.c_str(), weaponDef->dict.GetString ( "weaponclass", "rvWeapon" ) );
}
weapon = static_cast<rvWeapon*>( typeInfo->CreateInstance() );
weapon->Init( this, weaponDef, currentWeapon, isStrogg );
weapon->CallSpawn( );
// Reset the zoom fov on weapon change
if ( zoomed ) {
zoomFov.Init ( gameLocal.time, 100, CalcFov(true), DefaultFov() );
zoomed = false;
}
UpdateHudWeapon();
// Remove the "weapon_" from the anim prefect for the player world anims
animPrefix.Strip( "weapon_" );
// Make sure weapon is hidden
if ( !weaponEnabled ) {
Event_DisableWeapon();
}
}
/*
==============
idPlayer::SetupWeaponEntity
==============
*/
void idPlayer::SetupWeaponEntity( void ) {
int w;
const char *weap;
const idDeclEntityDef *decl;
idEntity *spawn;
// don't setup weapons for spectators
if ( gameLocal.isClient || (weaponViewModel && weaponWorldModel) || spectating ) {
return;
}
idDict args;
if ( !weaponViewModel ) {
// setup the view model
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
decl = static_cast< const idDeclEntityDef * >( declManager->FindType( DECL_ENTITYDEF, "player_viewweapon", false, false ) );
if ( !decl ) {
gameLocal.Error( "entityDef not found: player_viewweapon" );
}
args.Set( "name", va( "%s_weapon", name.c_str() ) );
args.SetInt( "instance", instance );
args.Set( "classname", decl->GetName() );
spawn = NULL;
gameLocal.SpawnEntityDef( args, &spawn );
if ( !spawn ) {
gameLocal.Error( "idPlayer::SetupWeaponEntity: failed to spawn weaponViewModel" );
}
weaponViewModel = static_cast<rvViewWeapon*>(spawn);
weaponViewModel->SetName( va("%s_weapon", name.c_str() ) );
weaponViewModel->SetInstance( instance );
}
if ( !weaponWorldModel ) {
// setup the world model
decl = static_cast< const idDeclEntityDef * >( declManager->FindType( DECL_ENTITYDEF, "player_animatedentity", false, false ) );
if ( !decl ) {
gameLocal.Error( "entityDef not found: player_animatedentity" );
}
args.Set( "name", va( "%s_weapon_world", name.c_str() ) );
args.SetInt( "instance", instance );
args.Set( "classname", decl->GetName() );
spawn = NULL;
gameLocal.SpawnEntityDef( args, &spawn );
if ( !spawn ) {
gameLocal.Error( "idPlayer::SetupWeaponEntity: failed to spawn weaponWorldModel" );
}
weaponWorldModel = static_cast<idAnimatedEntity*>(spawn);
weaponWorldModel->fl.networkSync = true;
weaponWorldModel->SetName ( va("%s_weapon_world", name.c_str() ) );
weaponWorldModel->SetInstance( instance );
}
currentWeapon = -1;
weaponWorldModel->fl.persistAcrossInstances = true;
weaponViewModel->fl.persistAcrossInstances = true;
for( w = 0; w < MAX_WEAPONS; w++ ) {
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( weap && *weap ) {
rvWeapon::CacheWeapon( weap );
}
}
}
/*
==============
idPlayer::Init
==============
*/
void idPlayer::Init( void ) {
const char *value;
const idDict *userInfo = GetUserInfo();
Hide();
noclip = false;
godmode = false;
godmodeDamage = 0;
undying = g_forceUndying.GetBool() ? !gameLocal.isMultiplayer : false;
oldButtons = 0;
oldFlags = 0;
currentWeapon = -1;
idealWeapon = -1;
previousWeapon = -1;
weaponSwitchTime = 0;
weaponEnabled = true;
showWeaponViewModel = userInfo ? userInfo->GetBool( "ui_showGun" ) : cvarSystem->GetCVarBool( "ui_showGun" );
oldInventoryWeapons = 0;
lastHitFrame = 0;
lastHitArmor = false;
lastDmgTime = 0;
bobCycle = 0;
bobFrac = 0.0f;
landChange = 0;
landTime = 0;
zoomFov.Init( 0, 0, 0, 0 );
centerView.Init( 0, 0, 0, 0 );
fxFov = false;
influenceFov = 0;
influenceActive = 0;
influenceRadius = 0.0f;
influenceEntity = NULL;
influenceMaterial = NULL;
influenceSkin = NULL;
currentLoggedAccel = 0;
focusTime = 0;
focusUI = NULL;
focusEnt = NULL;
focusType = FOCUS_NONE;
focusBrackets = NULL;
focusBracketsTime = 0;
talkingNPC = NULL;
talkCursor = 0;
lightningEffects = 0;
lightningNextTime = 0;
modelName = idStr();
// Remove any hearing loss that may be set up from the last map
soundSystem->FadeSoundClasses( SOUNDWORLD_GAME, 0, 0.0f, 0 );
// remove any damage effects
playerView.ClearEffects();
// damage values
fl.takedamage = true;
ClearPain();
// restore persistent data
RestorePersistantInfo();
bobCycle = 0;
SetupWeaponEntity( );
currentWeapon = -1;
previousWeapon = -1;
flashlightOn = false;
idealLegsYaw = 0.0f;
legsYaw = 0.0f;
legsForward = true;
oldViewYaw = 0.0f;
leanVelocity = vec3_zero;
ignoreOnGroundUntilFrame = 0;
prevOnGround = true;
vehicleCameraDist = 0.0f;
SetPMCVars();
// air always initialized to maximum too
airTics = pm_airTics.GetFloat();
airless = false;
gibDeath = false;
gibsLaunched = false;
gibDir.Zero();
// set the gravity
physicsObj.SetGravity( gameLocal.GetCurrentGravity(this) );
// start out standing
SetEyeHeight( pm_normalviewheight.GetFloat() );
stepUpTime = 0;
stepUpDelta = 0.0f;
viewBobAngles.Zero();
viewBob.Zero();
if ( gameLocal.isMultiplayer && gameLocal.IsTeamGame() ) {
value = spawnArgs.GetString( va( "model_%s", team ? "strogg" : "marine" ), NULL );
} else {
value = spawnArgs.GetString( "model" );
}
if ( gameLocal.isMultiplayer ) {
UpdateModelSetup( true );
} else {
if ( value && ( *value != 0 ) ) {
SetModel( value );
}
// check head
if( idStr::Icmp( head ? head->spawnArgs.GetString( "classname", "" ) : "", spawnArgs.GetString( "def_head", "" ) ) ) {
SetupHead();
}
}
if ( cursor ) {
cursor->SetStateInt( "talkcursor", 0 );
cursor->HandleNamedEvent( "showCrossCombat" );
}
if ( !gameLocal.isMultiplayer ) {
if ( g_testDeath.GetBool() && skin ) {
SetSkin( skin );
renderEntity.shaderParms[6] = 0.0f;
} else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
skin = declManager->FindSkin( value );
SetSkin( skin );
renderEntity.shaderParms[6] = 0.0f;
}
} else {
SetSkin( skin );
if( clientHead ) {
clientHead->SetSkin( headSkin );
if( clientHead->GetModelDefHandle() > 0) {
gameRenderWorld->RemoveDecals( clientHead->GetModelDefHandle() );
}
}
if( weaponViewModel ) {
weaponViewModel->SetSkin( weaponViewSkin );
}
}
value = spawnArgs.GetString( "joint_hips", "" );
hipJoint = animator.GetJointHandle( value );
if ( hipJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'joint_hips' on '%s'", value, name.c_str() );
}
value = spawnArgs.GetString( "joint_chest", "" );
chestJoint = animator.GetJointHandle( value );
if ( chestJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'joint_chest' on '%s'", value, name.c_str() );
}
value = spawnArgs.GetString( "joint_head", "" );
headJoint = animator.GetJointHandle( value );
if ( headJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'joint_head' on '%s'", value, name.c_str() );
}
value = spawnArgs.GetString( "joint_neckLean", "neckcontrol" );
neckLeanJoint = animator.GetJointHandle( value );
if ( neckLeanJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'joint_neck' on '%s'", value, name.c_str() );
}
// read player lean properties
spawnArgs.GetFloat( "lean_maxSpeed", "500", leanMaxSpeed );
spawnArgs.GetFloat( "lean_blendRatio", "0.9", leanBlendRatio );
spawnArgs.GetFloat( "lean_maxLateralAngle", "30", leanMaxLateralAngle );
spawnArgs.GetFloat( "lean_maxForwardAngle", "50", leanMaxForwardAngle );
spawnArgs.GetFloat( "lean_headRatio", "-0.5", leanHeadRatio );
spawnArgs.GetFloat( "lean_hipRatio", "0.5", leanHipRatio );
// initialize the script variables
memset( &pfl, 0, sizeof( pfl ) );
pfl.onGround = true;
pfl.noFallingDamage = false;
// Start in idle
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
forceScoreBoard = false;
forceScoreBoardTime = 0;
forcedReady = false;
privateCameraView = NULL;
lastSpectateChange = 0;
lastArenaChange = 0;
lastTeleFX = -9999;
hiddenWeapon = false;
tipUp = false;
objectiveUp = false;
teleportEntity = NULL;
lastKiller = NULL;
teleportKiller = -1;
leader = false;
SetPrivateCameraView( NULL );
lastSnapshotSequence = 0;
if ( !gameLocal.isMultiplayer ) {
// in MP we set isStrogg in UpdateModelSetup()
isStrogg = spawnArgs.GetBool ( "strogg", "0" );
}
aimClientNum = -1;
if ( mphud ) {
mphud->HandleNamedEvent( "aim_fade" );
}
isChatting = false;
SetInitialHud();
emote = PE_NONE;
powerupEffectTime = 0;
powerupEffect = NULL;
powerupEffectType = 0;
hasteEffect = NULL;
flagEffect = NULL;
arenaEffect = NULL;
quadOverlay = declManager->FindMaterial( spawnArgs.GetString( "mtr_quaddamage_overlay" ), false );
hasteOverlay = declManager->FindMaterial( spawnArgs.GetString( "mtr_haste_overlay" ), false );
regenerationOverlay = declManager->FindMaterial( spawnArgs.GetString( "mtr_regeneration_overlay" ), false );
invisibilityOverlay = declManager->FindMaterial( spawnArgs.GetString( "mtr_invisibility_overlay" ), false );
powerUpOverlay = NULL;
if ( gameLocal.isMultiplayer && IsLocalClient() ) {
if ( (gameLocal.mpGame.GetGameState()->GetMPGameState() != WARMUP) && gameLocal.mpGame.GetGameState()->GetMPGameState() != SUDDENDEATH ){
if( gameLocal.gameType != GAME_TOURNEY || ((rvTourneyGameState*)(gameLocal.mpGame.GetGameState()))->GetArena( arena ).GetState() != AS_WARMUP && ((rvTourneyGameState*)(gameLocal.mpGame.GetGameState()))->GetArena( arena ).GetState() != AS_SUDDEN_DEATH ) {
// don't clear notices while in warmup modes or sudden death
GUIMainNotice( "" );
GUIFragNotice( "" );
}
}
if ( (gameLocal.mpGame.GetGameState()->GetMPGameState() == WARMUP) && vsMsgState ) {
GUIMainNotice( "" );
GUIFragNotice( "" );
}
}
deathSkinTime = 0;
deathStateHitch = false;
lastPickupTime = 0;
if ( teamHealthRegenPending ) {
assert( teamHealthRegen == NULL );
teamHealthRegenPending = false;
teamHealthRegen = PlayEffect( "fx_guard", renderEntity.origin, renderEntity.axis, true );
}
if ( teamAmmoRegenPending ) {
assert( teamAmmoRegen == NULL );
teamAmmoRegenPending = false;
teamAmmoRegen = PlayEffect( "fx_ammoregen", renderEntity.origin, renderEntity.axis, true );
}
if ( teamDoublerPending ) {
assert( teamDoubler == NULL );
teamDoublerPending = false;
teamDoubler = PlayEffect( "fx_doubler", renderEntity.origin, renderEntity.axis, true );
}
clientIdealWeaponPredictFrame = -1;
serverReceiveEvent = false;
}
/*
===============
idPlayer::ProjectHeadOverlay
===============
*/
void idPlayer::ProjectHeadOverlay( const idVec3 &point, const idVec3 &dir, float size, const char *decal ) {
if( clientHead ) {
clientHead.GetEntity()->ProjectOverlay( point, dir, size, decal );
}
}
/*
===============
idPlayer::GetCursorGUI
===============
*/
idUserInterface* idPlayer::GetCursorGUI( void ) {
idStr temp;
assert( !gameLocal.isMultiplayer || IsLocalClient() );
if ( cursor ) {
return cursor;
}
if ( spawnArgs.GetString( "cursor", "", temp ) ) {
cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer );
}
return cursor;
}
/*
==============
idPlayer::Spawn
Prepare any resources used by the player.
==============
*/
void idPlayer::Spawn( void ) {
idStr temp;
idBounds bounds;
if ( entityNumber >= MAX_CLIENTS && !IsFakeClient() ) {
gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
}
// allow thinking during cinematics
cinematic = true;
if ( gameLocal.isMultiplayer ) {
// always start in spectating state waiting to be spawned in
// do this before SetClipModel to get the right bounding box
spectating = true;
}
// set our collision model
physicsObj.SetSelf( this );
SetClipModel( );
physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
physicsObj.SetContents( CONTENTS_BODY | (use_combat_bbox?CONTENTS_SOLID:0) );
physicsObj.SetClipMask( MASK_PLAYERSOLID );
SetPhysics( &physicsObj );
InitAASLocation();
skin = renderEntity.customSkin;
// only the local player needs guis
// for server netdemos that have no local player, we use demo_* guis in idGameLocal
if ( !gameLocal.isMultiplayer || IsLocalClient() ) {
// load HUD
hud = NULL;
mphud = NULL;
overlayHud = NULL;
overlayHudTime = 0;
objectiveSystem = NULL;
if ( spawnArgs.GetString( "hud", "", temp ) ) {
hud = uiManager->FindGui( temp, true, false, true );
} else {
gameLocal.Warning( "idPlayer::Spawn() - No hud for player." );
}
if ( gameLocal.isMultiplayer ) {
if ( spawnArgs.GetString( "mphud", "", temp ) ) {
mphud = uiManager->FindGui( temp, true, false, true );
} else {
gameLocal.Warning( "idPlayer::Spawn() - No MP hud overlay while in MP.");
}
}
if ( hud ) {
hud->Activate( true, gameLocal.time );
}
if ( mphud ) {
mphud->Activate( true, gameLocal.time );
}
// load cursor
GetCursorGUI();
if ( cursor ) {
cursor->Activate( true, gameLocal.time );
}
// Load
if ( spawnArgs.GetString ( "cinematicHud", "", temp ) ) {
cinematicHud = uiManager->FindGui( temp, true, false, true );
}
if ( !gameLocal.isMultiplayer ) {
objectiveSystem = uiManager->FindGui( spawnArgs.GetString( "wristcomm", "guis/wristcomm.gui" ), true, false, true );
objectiveSystemOpen = false;
#ifdef _XENON
g_ObjectiveSystemOpen = objectiveSystemOpen;
#endif
}
// clear votes
// if we want to display current votes that were started before a player was connected
// but are still being voted on, this should check the current vote and update the gui appropriately
gameLocal.mpGame.ClearVote( entityNumber );
}
// load the armor sound feedback
declManager->FindSound( "player_sounds_hitArmor" );
animator.RemoveOriginOffset( true );
// initialize user info related settings
// on server, we wait for the userinfo broadcast, as this controls when the player is initially spawned in game
// ocassionally, a race condition may mark a client in-game before he is spawned, if this is the case, parse the userinfo here
if ( (gameLocal.isClient || IsLocalClient() ) || (gameLocal.isServer && gameLocal.mpGame.IsInGame( entityNumber ) ) ) {
UserInfoChanged();
}
// create combat collision hull for exact collision detection
SetCombatModel();
// init the damage effects
playerView.SetPlayerEntity( this );
// supress model in non-player views, but allow it in mirrors and remote views
renderEntity.suppressSurfaceInViewID = entityNumber+1;
// don't project shadow on self or weapon
renderEntity.noSelfShadow = true;
if( gameLocal.isMultiplayer ) {
if( clientHead ) {
clientHead->GetRenderEntity()->suppressSurfaceInViewID = entityNumber + 1;
clientHead->GetRenderEntity()->noSelfShadow = true;
}
} else {
idAFAttachment *headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
headEnt->GetRenderEntity()->noSelfShadow = true;
}
}
if ( gameLocal.isMultiplayer ) {
Init();
Hide(); // properly hidden if starting as a spectator
// Normally idPlayer::Move() gets called to set the contents to 0, but we don't call
// move on players not in our snap, so we need to set it manually here.
bool inOtherInstance = gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance;
if( inOtherInstance ) {
physicsObj.SetContents( 0 );
physicsObj.SetMovementType( PM_SPECTATOR );
physicsObj.SetClipMask( MASK_DEADSOLID );
}
if ( !gameLocal.isClient ) {
// set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
SetupWeaponEntity( );
SpawnFromSpawnSpot( );
spectator = entityNumber;
forceRespawn = true;
assert( spectating );
}
} else {
SetupWeaponEntity( );
SpawnFromSpawnSpot( );
}
// trigger playtesting item gives, if we didn't get here from a previous level
// the devmap key will be set on the first devmap, but cleared on any level
// transitions
if ( !gameLocal.isMultiplayer && gameLocal.serverInfo.FindKey( "devmap" ) ) {
// fire a trigger with the name "devmap"
idEntity *ent = gameLocal.FindEntity( "devmap" );
if ( ent ) {
ent->ActivateTargets( this );
}
}
if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
hiddenWeapon = true;
if ( weapon ) {
weapon->LowerWeapon();
}
idealWeapon = SlotForWeapon( "weapon_blaster" );
Event_DisableWeapon();
} else {
hiddenWeapon = false;
}
if ( hud ) {
UpdateHudWeapon( );
hud->StateChanged( gameLocal.time );
}
tipUp = false;
objectiveUp = false;
aiManager.AddTeammate( this );
if ( inventory.levelTriggers.Num() ) {
PostEventMS( &EV_Player_LevelTrigger, 0 );
}
// ddynerman: defaults for these values are the single player fall deltas
fatalFallDelta = spawnArgs.GetFloat("fatal_fall_delta", "65");
hardFallDelta = spawnArgs.GetFloat("hard_fall_delta", "45");
softFallDelta = spawnArgs.GetFloat("soft_fall_delta", "30");
noFallDelta = spawnArgs.GetFloat("no_fall_delta", "7");
// precache decls
declManager->FindType( DECL_ENTITYDEF, "damage_fatalfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_hardfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_softfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_noair", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_suicide", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_telefrag", false, false );
declManager->FindType( DECL_ENTITYDEF, "dmg_shellshock", false, false );
declManager->FindType( DECL_ENTITYDEF, "dmg_shellshock_nohl", false, false );
gibSkin = declManager->FindSkin( spawnArgs.GetString( "skin_gibskin" ) );
// Skil levels
dynamicProtectionScale = 1.0f;
if ( !gameLocal.isMultiplayer ) {
if ( g_skill.GetInteger() < 2 ) {
if ( health < 25 ) {
health = 25;
}
} else {
//g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
}
}
// Powerup joints?
if ( spawnArgs.GetString ( "powerup_effect_joints", "", temp ) ) {
animator.GetJointList ( temp, powerupEffectJoints );
}
// RAVEN BEGIN
// mekberg: allow disabling of objectives during non-cinematic time periods
objectivesEnabled = true;
// mekberg: new objectives are suppressed until this event is processed
PostEventMS( &EV_Player_AllowNewObjectives, 5000 );
tourneyStatus = PTS_UNKNOWN;
predictionOriginError = vec3_zero;
predictionAnglesError = ang_zero;
// zero out view angles when we spawn ourselves in MP - the server will send down
// the correct ones (only zero if our input is still zero'd)
if( gameLocal.isClient && IsLocalClient() && usercmd.angles[ 0 ] == 0 && usercmd.angles[ 1 ] == 0 && usercmd.angles[ 2 ] == 0 ) {
deltaViewAngles = ang_zero;
}
//RITUAL BEGIN
carryOverCurrentWeapon = currentWeapon;
inventory.carryOverWeapons = 0;
//RITUAL END
itemCosts = static_cast< const idDeclEntityDef * >( declManager->FindType( DECL_ENTITYDEF, "ItemCostConstants", false ) );
}
/*
==============
idPlayer::~idPlayer()
Release any resources used by the player.
==============
*/
idPlayer::~idPlayer() {
if( gameLocal.mpGame.GetGameState() ) {
gameLocal.mpGame.GetGameState()->ClientDisconnect( this );
}
delete weaponViewModel;
delete weaponWorldModel;
delete weapon;
delete aasSensor;
SetPhysics( NULL );
}
/*
===========
idPlayer::Save
===========
*/
void idPlayer::Save( idSaveGame *savefile ) const {
assert( !IsFakeClient() );
int i;
savefile->WriteUsercmd( usercmd );
playerView.Save( savefile );
savefile->WriteBool( noclip );
savefile->WriteBool( godmode );
savefile->WriteInt ( godmodeDamage );
savefile->WriteBool( undying );
// don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
savefile->WriteAngles( spawnAngles );
savefile->WriteAngles( viewAngles );
savefile->WriteAngles( cmdAngles );
savefile->WriteInt( buttonMask );
savefile->WriteInt( oldButtons );
savefile->WriteInt( oldFlags );
savefile->WriteInt( 0 );
savefile->WriteInt( 0 );
savefile->WriteInt( lastSavingThrowTime );
// idBoolFields don't need to be saved, just re-linked in Restore
savefile->Write( &pfl, sizeof( pfl ) );
inventory.Save( savefile );
//weapon->Save( savefile ); // Don't save this
weaponViewModel.Save( savefile );
weaponWorldModel.Save ( savefile );
// weaponDef restore = weaponDef = GetWeaponDef ( currentWeapon );
savefile->WriteUserInterface( hud, false );
// savefile->WriteUserInterface( mphud, false ); // Don't save MP stuff
savefile->WriteUserInterface( objectiveSystem, false );
savefile->WriteUserInterface( cinematicHud, false );
savefile->WriteBool( objectiveSystemOpen );
savefile->WriteBool( disableHud );
savefile->WriteInt( lastDmgTime );
savefile->WriteInt( deathClearContentsTime );
savefile->WriteBool( doingDeathSkin );
savefile->WriteInt( nextHealthPulse );
savefile->WriteInt( nextArmorPulse );
savefile->WriteBool( hiddenWeapon );
// savefile->WriteInt( spectator ); // Don't save MP stuff
// savefile->WriteBool( scoreBoardOpen ); // Don't save MP stuff
// savefile->WriteBool( tourneyBracketsOpen ); // Don't save MP stuff
// savefile->WriteBool( forceScoreBoard ); // Don't save MP stuff
// savefile->WriteBool( forceRespawn ); // Don't save MP stuff
// savefile->WriteBool( spectating ); // Don't save MP stuff
// savefile->WriteBool( lastHitToggle ); // Don't save MP stuff
// savefile->WriteBool( forcedReady ); // Don't save MP stuff
// savefile->WriteBool( wantSpectate ); // Don't save MP stuff
// savefile->WriteBool( weaponGone ); // Don't save MP stuff
// savefile->WriteBool( useInitialSpawns ); // Don't save MP stuff
// savefile->WriteBool( isLagged ); // Don't save MP stuff
// savefile->WriteBool( isChatting ); // Don't save MP stuff
// savefile->WriteInt( lastSpectateTeleport ); // Don't save MP stuff
// savefile->WriteInt( latchedTeam ); // Don't save MP stuff
// savefile->WriteInt( spawnedTime ); // Don't save MP stuff
// teleportEntity.Save( savefile ); // Don't save MP stuff
// savefile->WriteInt( teleportKiller ); // Don't save MP stuff
savefile->WriteInt( minRespawnTime );
savefile->WriteInt( maxRespawnTime );
savefile->WriteVec3( firstPersonViewOrigin );
savefile->WriteMat3( firstPersonViewAxis );
// don't bother saving dragEntity or aasSensor since it's a dev tool
savefile->WriteVec3( intentDir );
savefile->WriteFloat ( vehicleCameraDist );
savefile->WriteJoint( hipJoint );
savefile->WriteJoint( chestJoint );
savefile->WriteJoint( headJoint );
savefile->WriteStaticObject( physicsObj );
savefile->WriteInt( aasLocation.Num() );
for( i = 0; i < aasLocation.Num(); i++ ) {
savefile->WriteInt( aasLocation[ i ].areaNum );
savefile->WriteVec3( aasLocation[ i ].pos );
}
savefile->WriteString( modelName ); // cnicholson: Added unsaved var
// TOSAVE: const idDict* modelDict
savefile->WriteInt( bobFoot );
savefile->WriteFloat( bobFrac );
savefile->WriteFloat( bobfracsin );
savefile->WriteInt( bobCycle );
savefile->WriteFloat( xyspeed );
savefile->WriteInt( stepUpTime );
savefile->WriteFloat( stepUpDelta );
savefile->WriteFloat( idealLegsYaw );
savefile->WriteFloat( legsYaw );
savefile->WriteBool( legsForward );
savefile->WriteFloat( oldViewYaw );
savefile->WriteAngles( viewBobAngles );
savefile->WriteVec3( viewBob );
savefile->WriteInt( landChange );
savefile->WriteInt( landTime );
savefile->WriteFloat( fatalFallDelta );
savefile->WriteFloat( hardFallDelta );
savefile->WriteFloat( softFallDelta );
savefile->WriteFloat( noFallDelta );
savefile->WriteInt( currentWeapon );
savefile->WriteInt( idealWeapon );
savefile->WriteInt( previousWeapon );
savefile->WriteInt( weaponSwitchTime );
savefile->WriteBool( weaponEnabled );
savefile->WriteBool ( flashlightOn);
savefile->WriteBool ( zoomed );
savefile->WriteBool( reloadModel );
savefile->WriteSkin( skin );
savefile->WriteSkin( powerUpSkin );
savefile->WriteSkin( gibSkin );
savefile->WriteInt( numProjectilesFired );
savefile->WriteInt( numProjectileHits );
savefile->WriteBool( airless );
savefile->WriteInt( airTics );
savefile->WriteInt( lastAirDamage );
savefile->WriteBool( gibDeath );
savefile->WriteBool( gibsLaunched );
savefile->WriteVec3( gibDir );
savefile->WriteBool( isStrogg );
savefile->WriteInterpolate( zoomFov );
savefile->WriteInterpolate( centerView );
savefile->WriteBool( fxFov );
savefile->WriteFloat( influenceFov );
savefile->WriteInt( influenceActive );
savefile->WriteObject( influenceEntity );
savefile->WriteMaterial( influenceMaterial );
savefile->WriteFloat( influenceRadius );
savefile->WriteSkin( influenceSkin );
savefile->WriteObject( privateCameraView );
for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
savefile->WriteAngles( loggedViewAngles[ i ] );
}
for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
savefile->WriteInt( loggedAccel[ i ].time );
savefile->WriteVec3( loggedAccel[ i ].dir );
}
savefile->WriteInt( currentLoggedAccel );
savefile->WriteUserInterface( focusUI, false );
savefile->WriteInt( focusTime );
savefile->WriteInt ( focusType );
focusEnt.Save ( savefile );
savefile->WriteUserInterface( focusBrackets, false );
savefile->WriteInt( focusBracketsTime );
talkingNPC.Save( savefile );
extraProjPassEntity.Save( savefile );
savefile->WriteInt( talkCursor );
savefile->WriteUserInterface( cursor, false );
savefile->WriteUserInterface( overlayHud, false );
savefile->WriteInt ( overlayHudTime );
savefile->WriteBool( targetFriendly );
savefile->WriteInt( oldMouseX );
savefile->WriteInt( oldMouseY );
savefile->WriteBool( tipUp );
savefile->WriteBool( objectiveUp );
savefile->WriteFloat( dynamicProtectionScale );
savefile->WriteInt( lastDamageDef );
savefile->WriteVec3( lastDamageDir );
savefile->WriteInt( lastDamageLocation );
savefile->WriteInt( predictedFrame );
savefile->WriteVec3( predictedOrigin );
savefile->WriteAngles( predictedAngles );
savefile->WriteBool( predictedUpdated );
savefile->WriteVec3( predictionOriginError );
savefile->WriteAngles( predictionAnglesError );
savefile->WriteInt( predictionErrorTime );
// savefile->WriteBool( ready ); // Don't save MP stuff
// savefile->WriteBool( respawning ); // Don't save MP stuff
// savefile->WriteBool( leader ); // Don't save MP stuff
// savefile->WriteBool( weaponCatchup ); // Don't save MP stuff
// savefile->WriteBool( isTelefragged ); // Don't save MP stuff
// savefile->WriteInt( lastSpectateChange ); // Don't save MP stuff
// savefile->WriteInt( lastTeleFX ); // Don't save MP stuff
// savefile->WriteInt( lastSnapshotSequence ); // Don't save MP stuff
// savefile->WriteInt( aimClientNum ); // Don't save MP stuff
// lastImpulsePlayer->Save( savefile ); // Don't save MP stuff
// savefile->WriteInt( arena ); // Don't save MP stuff
// savefile->WriteInt( connectTime ); // Don't save MP stuff
// savefile->WriteInt( mutedPlayers ); // Don't save MP stuff
// savefile->WriteInt( friendPlayers ); // Don't save MP stuff
// savefile->WriteInt( rank ); // Don't save MP stuff
savefile->WriteInt( lastImpulseTime );
bossEnemy.Save( savefile ); // cnicholson: Added unsaved var
// TOSAVE: const idDeclEntityDef* cachedWeaponDefs [ MAX_WEAPONS ]; // cnicholson: Save these?
// TOSAVE: const idDeclEntityDef* cachedPowerupDefs [ POWERUP_MAX ];
savefile->WriteBool( weaponChangeIconsUp ); // cnicholson: Added unsaved var
// mekberg: added
savefile->WriteBool( showNewObjectives );
savefile->WriteBool( objectivesEnabled );
savefile->WriteBool( flagCanFire );
// TOSAVE: const idDeclEntityDef* cachedWeaponDefs [ MAX_WEAPONS ]; // cnicholson: Save these?
// TOSAVE: const idDeclEntityDef* cachedPowerupDefs [ POWERUP_MAX ];
#ifndef _XENON
if ( hud ) {
hud->SetStateString( "message", common->GetLocalizedString( "#str_102916" ) );
hud->HandleNamedEvent( "Message" );
}
#endif
}
/*
===========
idPlayer::Restore
===========
*/
void idPlayer::Restore( idRestoreGame *savefile ) {
assert( !IsFakeClient() );
int i;
int num;
savefile->ReadUsercmd( usercmd );
playerView.Restore( savefile );
savefile->ReadBool( noclip );
savefile->ReadBool( godmode );
savefile->ReadInt ( godmodeDamage );
savefile->ReadBool( undying );
savefile->ReadAngles( spawnAngles );
savefile->ReadAngles( viewAngles );
savefile->ReadAngles( cmdAngles );
memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
SetViewAngles( viewAngles );
spawnAnglesSet = true;
savefile->ReadInt( buttonMask );
savefile->ReadInt( oldButtons );
savefile->ReadInt( oldFlags );
usercmd.flags = 0;
oldFlags = 0;
int foo;
savefile->ReadInt( foo );
savefile->ReadInt( foo );
savefile->ReadInt( lastSavingThrowTime );
savefile->Read( &pfl, sizeof( pfl ) );
inventory.Restore( savefile );
assert( !weapon );
weaponViewModel.Restore( savefile );
weaponWorldModel.Restore( savefile );
savefile->ReadUserInterface( hud, &spawnArgs );
assert( !mphud ); // Don't save MP stuff
savefile->ReadUserInterface( objectiveSystem, &spawnArgs );
savefile->ReadUserInterface( cinematicHud, &spawnArgs );
savefile->ReadBool( objectiveSystemOpen );
#ifdef _XENON
g_ObjectiveSystemOpen = objectiveSystemOpen;
#endif
objectiveButtonReleased = false;
savefile->ReadBool( disableHud ); // cnicholson: Added unrestored var
savefile->ReadInt( lastDmgTime );
savefile->ReadInt( deathClearContentsTime );
savefile->ReadBool( doingDeathSkin );
savefile->ReadInt( nextHealthPulse );
savefile->ReadInt( nextArmorPulse );
savefile->ReadBool( hiddenWeapon );
assert( !spectator ); // Don't save MP stuff
assert( !scoreBoardOpen ); // Don't save MP stuff
assert( !forceScoreBoard ); // Don't save MP stuff
assert( !forceRespawn ); // Don't save MP stuff
assert( !spectating ); // Don't save MP stuff
assert( !forcedReady ); // Don't save MP stuff
assert( !wantSpectate ); // Don't save MP stuff
assert( !weaponGone ); // Don't save MP stuff
assert( !useInitialSpawns ); // Don't save MP stuff
assert( !isLagged ); // Don't save MP stuff
assert( !isChatting ); // Don't save MP stuff
assert( !lastSpectateTeleport ); // Don't save MP stuff
assert( latchedTeam == -1 ); // Don't save MP stuff
assert( !spawnedTime ); // Don't save MP stuff
assert( !teleportEntity ); // Don't save MP stuff
assert( teleportKiller == -1 ); // Don't save MP stuff
savefile->ReadInt( minRespawnTime );
savefile->ReadInt( maxRespawnTime );
savefile->ReadVec3( firstPersonViewOrigin );
savefile->ReadMat3( firstPersonViewAxis );
// don't bother restoring dragEntity since it's a dev tool
dragEntity.Clear();
savefile->ReadVec3( intentDir );
savefile->ReadFloat ( vehicleCameraDist );
savefile->ReadJoint( hipJoint );
savefile->ReadJoint( chestJoint );
savefile->ReadJoint( headJoint );
savefile->ReadStaticObject( physicsObj );
RestorePhysics( &physicsObj );
savefile->ReadInt( num );
aasLocation.SetGranularity( 1 );
aasLocation.SetNum( num );
for( i = 0; i < num; i++ ) {
savefile->ReadInt( aasLocation[ i ].areaNum );
savefile->ReadVec3( aasLocation[ i ].pos );
}
savefile->ReadString( modelName ); // cnicholson: Added unrestored var
// TORESTORE: const idDict* modelDict
savefile->ReadInt( bobFoot );
savefile->ReadFloat( bobFrac );
savefile->ReadFloat( bobfracsin );
savefile->ReadInt( bobCycle );
savefile->ReadFloat( xyspeed );
savefile->ReadInt( stepUpTime );
savefile->ReadFloat( stepUpDelta );
savefile->ReadFloat( idealLegsYaw );
savefile->ReadFloat( legsYaw );
savefile->ReadBool( legsForward );
savefile->ReadFloat( oldViewYaw );
savefile->ReadAngles( viewBobAngles );
savefile->ReadVec3( viewBob );
savefile->ReadInt( landChange );
savefile->ReadInt( landTime );
savefile->ReadFloat( fatalFallDelta );
savefile->ReadFloat( hardFallDelta );
savefile->ReadFloat( softFallDelta );
savefile->ReadFloat( noFallDelta );
savefile->ReadInt( currentWeapon );
savefile->ReadInt( idealWeapon );
savefile->ReadInt( previousWeapon );
savefile->ReadInt( weaponSwitchTime );
savefile->ReadBool( weaponEnabled );
savefile->ReadBool ( flashlightOn );
savefile->ReadBool ( zoomed );
savefile->ReadBool ( reloadModel );
savefile->ReadSkin( skin );
savefile->ReadSkin( powerUpSkin );
savefile->ReadSkin( gibSkin );
savefile->ReadInt( numProjectilesFired );
savefile->ReadInt( numProjectileHits );
savefile->ReadBool( airless );
savefile->ReadInt( airTics );
savefile->ReadInt( lastAirDamage );
savefile->ReadBool( gibDeath );
savefile->ReadBool( gibsLaunched );
savefile->ReadVec3( gibDir );
savefile->ReadBool( isStrogg );
savefile->ReadInterpolate( zoomFov );
savefile->ReadInterpolate( centerView );
savefile->ReadBool( fxFov );
savefile->ReadFloat( influenceFov );
savefile->ReadInt( influenceActive );
savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
savefile->ReadMaterial( influenceMaterial );
savefile->ReadFloat( influenceRadius );
savefile->ReadSkin( influenceSkin );
savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
savefile->ReadAngles( loggedViewAngles[ i ] );
}
for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
savefile->ReadInt( loggedAccel[ i ].time );
savefile->ReadVec3( loggedAccel[ i ].dir );
}
savefile->ReadInt( currentLoggedAccel );
savefile->ReadUserInterface( focusUI, &spawnArgs ),
savefile->ReadInt( focusTime );
savefile->ReadInt( (int&)focusType );
focusEnt.Restore ( savefile );
savefile->ReadUserInterface( focusBrackets, &spawnArgs );
savefile->ReadInt( focusBracketsTime );
talkingNPC.Restore( savefile );
extraProjPassEntity.Restore( savefile );
savefile->ReadInt( talkCursor );
savefile->ReadUserInterface( cursor, &spawnArgs );
savefile->ReadUserInterface( overlayHud, &spawnArgs ); // cnicholson: Added unrestored var
savefile->ReadInt ( overlayHudTime ); // cnicholson: Added unrestored var
savefile->ReadBool( targetFriendly );
savefile->ReadInt( oldMouseX );
savefile->ReadInt( oldMouseY );
savefile->ReadBool( tipUp );
savefile->ReadBool( objectiveUp );
savefile->ReadFloat( dynamicProtectionScale ); // cnicholson: Added unrestored var
savefile->ReadInt( lastDamageDef );
savefile->ReadVec3( lastDamageDir );
savefile->ReadInt( lastDamageLocation );
savefile->ReadInt( predictedFrame );
savefile->ReadVec3( predictedOrigin );
savefile->ReadAngles( predictedAngles );
savefile->ReadBool( predictedUpdated );
savefile->ReadVec3( predictionOriginError );
savefile->ReadAngles( predictionAnglesError );
savefile->ReadInt( predictionErrorTime );
assert( !ready ); // Don't save MP stuff
assert( !respawning ); // Don't save MP stuff
assert( !leader ); // Don't save MP stuff
assert( !weaponCatchup ); // Don't save MP stuff
assert( !isTelefragged ); // Don't save MP stuff
assert( !lastSpectateChange ); // Don't save MP stuff
assert( lastTeleFX == -9999 ); // Don't save MP stuff
assert( !lastSnapshotSequence ); // Don't save MP stuff
assert( aimClientNum == -1 ); // Don't save MP stuff
assert( !lastImpulsePlayer ); // Don't save MP stuff
assert( !arena ); // Don't save MP stuff
assert( !connectTime ); // Don't save MP stuff
assert( !mutedPlayers ); // Don't save MP stuff
assert( !friendPlayers ); // Don't save MP stuff
assert( rank == -1 ); // Don't save MP stuff
savefile->ReadInt( lastImpulseTime );
bossEnemy.Restore( savefile ); // cnicholson: Added unrestored var
// TORESTORE: const idDeclEntityDef* cachedWeaponDefs [ MAX_WEAPONS ]; // cnicholson: Save these?
// TORESTORE: const idDeclEntityDef* cachedPowerupDefs [ POWERUP_MAX ];
savefile->ReadBool( weaponChangeIconsUp ); // cnicholson: Added unrestored var
// mekberg: added
savefile->ReadBool( showNewObjectives );
savefile->ReadBool( objectivesEnabled );
savefile->ReadBool( flagCanFire );
// set the pm_ cvars
const idKeyValue *kv;
kv = spawnArgs.MatchPrefix( "pm_", NULL );
while( kv ) {
cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
kv = spawnArgs.MatchPrefix( "pm_", kv );
}
// Loading a game on easy mode ensures you alwasy have 20% health when you load
i = spawnArgs.GetInt ( va("minRestoreHealth%d", g_skill.GetInteger ( ) ) );
if ( health < i ) {
health = i;
}
//if there's hearing loss, make sure we post a finishing event
if( pfl.hearingLoss ) {
Event_FinishHearingLoss( 3.0f );
} else {
Event_FinishHearingLoss( 0.05f );
}
// create combat collision hull for exact collision detection
SetCombatModel();
// RAVEN BEGIN
// mekberg: Grab from user info.
showWeaponViewModel = GetUserInfo()->GetBool( "ui_showGun" );
// precache decls
declManager->FindType( DECL_ENTITYDEF, "damage_fatalfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_hardfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_softfall", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_noair", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_suicide", false, false );
declManager->FindType( DECL_ENTITYDEF, "damage_telefrag", false, false );
declManager->FindType( DECL_ENTITYDEF, "dmg_shellshock", false, false );
declManager->FindType( DECL_ENTITYDEF, "dmg_shellshock_nohl", false, false );
// RAVEN END
}
/*
===============
idPlayer::PrepareForRestart
================
*/
void idPlayer::PrepareForRestart( void ) {
ClearPowerUps();
Spectate( true );
forceRespawn = true;
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
allowedToRespawn = true;
// RITUAL END
// we will be restarting program, clear the client entities from program-related things first
ShutdownThreads();
// the sound world is going to be cleared, don't keep references to emitters
FreeSoundEmitter( false );
}
/*
===============
idPlayer::Restart
================
*/
void idPlayer::Restart( void ) {
idActor::Restart();
// client needs to setup the animation script object again
if ( gameLocal.isClient ) {
// clear the existing model to force a reload
Init();
if ( !spectating ) {
Show();
}
} else {
// choose a random spot and prepare the point of view in case player is left spectating
assert( spectating );
SpawnFromSpawnSpot();
}
lastKiller = NULL;
useInitialSpawns = true;
}
/*
===============
idPlayer::ServerSpectate
================
*/
void idPlayer::ServerSpectate( bool spectate ) {
assert( !gameLocal.isClient );
if ( spectating != spectate ) {
// if we select spectating on the client
// mekberg: drop and clear powerups from the player.
DropPowerups();
ClearPowerUps();
Spectate( spectate );
gameLocal.mpGame.GetGameState()->Spectate( this );
if ( spectate ) {
SetSpectateOrigin( );
}
}
if ( !spectate ) {
SpawnFromSpawnSpot( );
}
}
/*
===========
idPlayer::SelectSpawnPoint
Find a spawn point marked, otherwise use normal spawn selection.
============
*/
bool idPlayer::SelectSpawnPoint( idVec3 &origin, idAngles &angles ) {
idEntity *spot;
idStr skin;
spot = gameLocal.SelectSpawnPoint( this );
// no spot, try again next frame
if( !spot ) {
forceRespawn = true;
return false;
}
// set the player skin from the spawn location
if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
spawnArgs.Set( "spawn_skin", skin );
}
if ( spot->spawnArgs.GetString( "spawn_model", NULL ) ) {
spawnArgs.Set( "model", spot->spawnArgs.GetString( "spawn_model", NULL ) );
}
if ( spot->spawnArgs.GetString( "def_head", NULL ) ) {
spawnArgs.Set( "def_head", spot->spawnArgs.GetString( "def_head", NULL ) );
}
// activate the spawn locations targets
spot->PostEventMS( &EV_ActivateTargets, 0, this );
origin = spot->GetPhysics()->GetOrigin();
origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
angles = spot->GetPhysics()->GetAxis().ToAngles();
return true;
}
/*
===========
idPlayer::SpawnFromSpawnSpot
Chooses a spawn location and spawns the player
============
*/
void idPlayer::SpawnFromSpawnSpot( void ) {
idVec3 spawn_origin;
idAngles spawn_angles;
if( !SelectSpawnPoint( spawn_origin, spawn_angles ) ) {
forceRespawn = true;
return;
}
SpawnToPoint( spawn_origin, spawn_angles );
}
/*
===========
idPlayer::SpawnToPoint
Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
when called here with spectating set to true, just place yourself and init
============
*/
void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
idVec3 spec_origin;
assert( !gameLocal.isClient || IsFakeClient() );
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
// Record previous weapons for later restoration
inventory.carryOverWeapons &= ~CARRYOVER_WEAPONS_MASK;
inventory.carryOverWeapons |= inventory.weapons;
}
// RITUAL END
respawning = true;
Init();
// Force players to use bounding boxes when in multiplayer
if ( gameLocal.isMultiplayer ) {
use_combat_bbox = true;
// Make sure the combat model is unlinked
if ( combatModel ) {
combatModel->Unlink( );
}
}
// Any health over max health will tick down
if ( health > inventory.maxHealth ) {
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
}
if ( inventory.armor > inventory.maxarmor ) {
nextArmorPulse = gameLocal.time + ARMOR_PULSE;
}
fl.noknockback = false;
// stop any ragdolls being used
StopRagdoll();
// set back the player physics
SetPhysics( &physicsObj );
physicsObj.SetClipModelAxis();
physicsObj.EnableClip();
if ( !spectating ) {
SetCombatContents( true );
}
physicsObj.SetLinearVelocity( vec3_origin );
// setup our initial view
if ( !spectating ) {
SetOrigin( spawn_origin );
// RAVEN BEGIN
// abahr: taking into account gravity
SetAxis( spawn_angles.ToMat3() );
// RAVEN END
} else {
spec_origin = spawn_origin;
spec_origin[ 2 ] += pm_normalheight.GetFloat();
spec_origin[ 2 ] += SPECTATE_RAISE;
SetOrigin( spec_origin );
}
// if this is the first spawn of the map, we don't have a usercmd yet,
// so the delta angles won't be correct. This will be fixed on the first think.
viewAngles = ang_zero;
SetDeltaViewAngles( ang_zero );
SetViewAngles( spawn_angles );
spawnAngles = spawn_angles;
spawnAnglesSet = false;
legsForward = true;
legsYaw = 0.0f;
idealLegsYaw = 0.0f;
oldViewYaw = viewAngles.yaw;
leanVelocity = vec3_zero;
if ( spectating ) {
Hide();
} else {
Show();
}
if ( gameLocal.isMultiplayer ) {
if ( !spectating ) {
// we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
if ( lastTeleFX < gameLocal.time - 1000 ) {
// currentThinkingEntity not set here (called out of Run())
idEntity* thinker = gameLocal.currentThinkingEntity;
gameLocal.currentThinkingEntity = this;
gameLocal.PlayEffect( spawnArgs, "fx_spawn", renderEntity.origin, idVec3(0,0,1).ToMat3(), false, vec3_origin, true );
lastTeleFX = gameLocal.time;
gameLocal.currentThinkingEntity = thinker;
}
}
//RAVEN BEGIN
//asalmon: Clear the respwan message
if ( mphud ) {
mphud->SetStateInt( "respawnNotice", 0 );
}
//RAVEN END
pfl.teleport = true;
} else {
pfl.teleport = false;
}
// kill anything at the new position
if ( !spectating ) {
physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
// RAVEN BEGIN
// abahr: this is killing the tram car when spawning in. Ooooops!
if( gameLocal.isMultiplayer ) {
gameLocal.KillBox( this );
}
// RAVEN END
}
// don't allow full run speed for a bit
physicsObj.SetKnockBack( 100 );
// set our respawn time and buttons so that if we're killed we don't respawn immediately
minRespawnTime = gameLocal.time;
maxRespawnTime = gameLocal.time;
if ( !spectating ) {
forceRespawn = false;
}
privateCameraView = NULL;
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
if( gameLocal.isMultiplayer && gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() )
{
// restore previous weapons
inventory.weapons |= inventory.carryOverWeapons & CARRYOVER_WEAPONS_MASK;
for( int weaponIndex = 0; weaponIndex < MAX_WEAPONS; weaponIndex++ )
{
if( inventory.weapons & ( 1 << weaponIndex ) )
{
int ammoIndex = inventory.AmmoIndexForWeaponIndex( weaponIndex );
inventory.ammo[ ammoIndex ] = inventory.StartingAmmoForWeaponIndex( weaponIndex );
}
}
/// Restore armor purchased while dead
if( inventory.carryOverWeapons & CARRYOVER_FLAG_ARMOR_LIGHT )
{
inventory.carryOverWeapons &= ~CARRYOVER_FLAG_ARMOR_LIGHT;
GiveItem( "item_armor_small" );
}
if( inventory.carryOverWeapons & CARRYOVER_FLAG_ARMOR_HEAVY )
{
inventory.carryOverWeapons &= ~CARRYOVER_FLAG_ARMOR_HEAVY;
GiveItem( "item_armor_large" );
}
if( inventory.carryOverWeapons & CARRYOVER_FLAG_AMMO )
{
inventory.carryOverWeapons &= ~CARRYOVER_FLAG_AMMO;
GiveItem( "ammorefill" );
}
// Reactivate team powerups
gameLocal.mpGame.SetUpdateForTeamPowerups(team);
UpdateTeamPowerups();
}
// RITUAL END
BecomeActive( TH_THINK );
// run a client frame to drop exactly to the floor,
// initialize animations and other things
Think();
respawning = false;
isTelefragged = false;
isLagged = false;
isChatting = false;
lastImpulsePlayer = NULL;
lastImpulseTime = 0;
}
/*
===============
idPlayer::SavePersistantInfo
Saves any inventory and player stats when changing levels.
===============
*/
void idPlayer::SavePersistantInfo( void ) {
if ( IsFakeClient() ) {
return;
}
idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
playerInfo.Clear();
inventory.GetPersistantData( playerInfo );
playerInfo.SetInt( "health", health );
playerInfo.SetInt( "current_weapon", currentWeapon );
}
/*
===============
idPlayer::RestorePersistantInfo
Restores any inventory and player stats when changing levels.
===============
*/
void idPlayer::RestorePersistantInfo( void ) {
if ( IsFakeClient() ) {
return;
}
if ( gameLocal.isMultiplayer ) {
gameLocal.persistentPlayerInfo[entityNumber].Clear();
}
spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
inventory.RestoreInventory( this, spawnArgs );
health = spawnArgs.GetInt( "health", "100" );
if ( !gameLocal.isClient ) {
idealWeapon = spawnArgs.GetInt( "current_weapon", "0" );
}
}
/*
================
idPlayer::GetUserInfo
================
*/
idDict *idPlayer::GetUserInfo( void ) {
if ( IsFakeClient() ) {
return NULL;
}
return &gameLocal.userInfo[ entityNumber ];
}
/*
==============
idPlayer::UpdateModelSetup
Updates the player's model setup. Model setups are read from the player def, and contain
information about the player's model, the player's head model, a skin, and a team for the
composite model.
==============
*/
void idPlayer::UpdateModelSetup( bool forceReload ) {
const idDict* dict;
const char* uiKeyName = NULL;
const char* defaultModel = NULL;
const char* newModelName = NULL;
if( !gameLocal.isMultiplayer || spectating ) {
return;
}
if( gameLocal.IsTeamGame() ) {
defaultModel = spawnArgs.GetString( va( "def_default_model_%s", idMultiplayerGame::teamNames[ team ] ), NULL );
if( g_forceMarineModel.GetString()[ 0 ] && team == TEAM_MARINE ) {
newModelName = g_forceMarineModel.GetString();
} else if( g_forceStroggModel.GetString()[ 0 ] && team == TEAM_STROGG ) {
newModelName = g_forceStroggModel.GetString();
} else {
uiKeyName = va( "ui_model_%s", idMultiplayerGame::teamNames[ team ] );
newModelName = GetUserInfo() ? GetUserInfo()->GetString( uiKeyName ) : "";
}
} else {
defaultModel = spawnArgs.GetString( "def_default_model" );
if( g_forceModel.GetString()[ 0 ] ) {
newModelName = g_forceModel.GetString();
} else {
uiKeyName = "ui_model";
newModelName = GetUserInfo() ? GetUserInfo()->GetString( uiKeyName ) : "";
}
}
if( !idStr::Icmp( newModelName, "" ) ) {
newModelName = defaultModel;
}
// model hasn't changed
if( !modelName.Icmp( newModelName ) && !forceReload ) {
return;
}
rvDeclPlayerModel* model = (rvDeclPlayerModel*)declManager->FindType( DECL_PLAYER_MODEL, newModelName, false );
// validate that the model they've selected is OK for this team game
if( gameLocal.IsTeamGame() && model ) {
if( idStr::Icmp( model->team, idMultiplayerGame::teamNames[ team ] ) ) {
gameLocal.Warning( "idPlayer::UpdateModelSetup() - Player %d (%s) set to model %s which is restricted to team %s (Player team: %s)\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "?", newModelName, model->team.c_str(), idMultiplayerGame::teamNames[ team ] );
if( uiKeyName ) {
cvarSystem->SetCVarBool( uiKeyName, "" );
}
dict = NULL;
}
}
// check to see if the user-specified ui_model/ui_model_strogg/ui_model_marine is valid
if( !model ) {
newModelName = defaultModel;
model = (rvDeclPlayerModel*)declManager->FindType( DECL_PLAYER_MODEL, newModelName, false );
if( !model ) {
gameLocal.Error( "idPlayer::UpdateModelSetup() - Can't find default model (%s)\n", defaultModel );
} else {
// If it's not valid, set the cvar to the default model
if( uiKeyName && GetUserInfo() ) {
GetUserInfo()->Set( uiKeyName, defaultModel );
if( IsLocalClient() ) {
cvarSystem->SetCVarString( uiKeyName, defaultModel );
}
if( gameLocal.isServer ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "updateUI %d\n", entityNumber ) );
return;
}
}
}
}
modelName = newModelName;
modelDecl = model;
reloadModel = true;
// if we have a strogg model, set the strogg flag here
isStrogg = false;
if( modelDecl && !modelDecl->team.Icmp( "strogg" ) ) {
isStrogg = true;
}
if (gameLocal.isClient || gameLocal.isListenServer)
UpdateSounds(modelDecl);
}
void idPlayer::UpdateSounds(const rvDeclPlayerModel *modelDecl)
{
// reset to the player.def sounds just in case we're switching from a model
// which defines sounds back to one that doesnt
const idDict* playerDict = gameLocal.FindEntityDefDict( "player_marine", false );
const idKeyValue *kv = playerDict->MatchPrefix( "snd_", NULL );
while (kv)
{
spawnArgs.Set(kv->GetKey().c_str(), kv->GetValue().c_str());
kv = playerDict->MatchPrefix( "snd_", kv );
}
spawnArgs.Copy(modelDecl->sounds);
}
/*
==============
idPlayer::BalanceTeam
==============
*/
bool idPlayer::BalanceTeam( void ) {
assert( !IsFakeClient() );
int i, balanceTeam, teamCount[2];
idEntity *ent;
teamCount[ 0 ] = teamCount[ 1 ] = 0;
for( i = 0; i < gameLocal.numClients; i++ ) {
ent = gameLocal.entities[ i ];
if ( ent && ent->IsType( idPlayer::Type ) && gameLocal.mpGame.IsInGame( i ) ) {
if ( !static_cast< idPlayer * >( ent )->spectating ) {
teamCount[ static_cast< idPlayer * >( ent )->team ]++;
}
}
}
balanceTeam = -1;
if ( teamCount[ 0 ] < teamCount[ 1 ] ) {
balanceTeam = 0;
} else if ( teamCount[ 0 ] > teamCount[ 1 ] ) {
balanceTeam = 1;
}
if ( balanceTeam != -1 && team != balanceTeam && teamCount[ balanceTeam ]+1 != teamCount[ !balanceTeam ] ) {
common->DPrintf( "team balance: forcing player %d to %s team\n", entityNumber, balanceTeam ? "strogg" : "marine" );
team = balanceTeam;
GetUserInfo()->Set( "ui_team", team ? "Strogg" : "Marine" );
return true;
}
return false;
}
void HSVtoRGB( float &r, float &g, float &b, float h, float s, float v ) {
int i;
float f, p, q, t;
h = idMath::ClampFloat( 0.0f, 360.0f, h );
s = idMath::ClampFloat( 0.0f, 1.0f, s );
v = idMath::ClampFloat( 0.75f, 1.0f, v );
if( s == 0 ) {
// achromatic (grey)
r = g = b = v;
return;
}
h /= 60; // sector 0 to 5
i = floor( h );
f = h - i; // factorial part of h
p = v * ( 1 - s );
q = v * ( 1 - s * f );
t = v * ( 1 - s * ( 1 - f ) );
switch( i ) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
break;
}
}
/*
==============
idPlayer::UserInfoChanged
==============
*/
bool idPlayer::UserInfoChanged( void ) {
idDict *userInfo;
bool modifiedInfo;
bool spec;
bool newready;
if ( IsFakeClient() ) {
showWeaponViewModel = cvarSystem->GetCVarBool( "ui_showGun" );
wantSpectate = true;
spectator = this->entityNumber;
return false;
}
userInfo = GetUserInfo();
showWeaponViewModel = userInfo->GetBool( "ui_showGun" );
if ( !gameLocal.isMultiplayer ) {
handicap = 1.0f;
return false;
}
modifiedInfo = false;
// update/apply handicap
handicap = userInfo->GetInt( "ui_handicap", "100" ) / 100.0f;
if ( handicap <= 0.0f || handicap >= 1.0f ) {
handicap = 1.0f;
}
if( PowerUpActive( POWERUP_GUARD ) ) {
inventory.maxHealth = 200;
inventory.maxarmor = 200;
} else {
inventory.maxHealth = spawnArgs.GetInt( "maxhealth", "100" );
inventory.maxarmor = spawnArgs.GetInt( "maxarmor", "100" );
}
spec = ( idStr::Icmp( userInfo->GetString( "ui_spectate" ), "Spectate" ) == 0 );
if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
// never let spectators go back to game while sudden death is on
if ( gameLocal.mpGame.GetGameState()->GetMPGameState() == SUDDENDEATH && !spec && wantSpectate == true ) {
userInfo->Set( "ui_spectate", "Spectate" );
modifiedInfo |= true;
} else {
if ( spec != wantSpectate && !spec ) {
// returning from spectate, set forceRespawn so we don't get stuck in spectate forever
forceRespawn = true;
}
wantSpectate = spec;
}
} else {
if ( spec ) {
userInfo->Set( "ui_spectate", "Play" );
modifiedInfo |= true;
} else if ( spectating ) {
// allow player to leaving spectator mode if they were in it when it was disallowed
forceRespawn = true;
}
wantSpectate = false;
}
if ( gameLocal.serverInfo.GetBool( "si_useReady" ) ) {
newready = ( idStr::Icmp( userInfo->GetString( "ui_ready" ), "Ready" ) == 0 );
if ( ready != newready && gameLocal.mpGame.GetGameState()->GetMPGameState() == WARMUP && !wantSpectate ) {
gameLocal.mpGame.AddChatLine( common->GetLocalizedString( "#str_107180" ), userInfo->GetString( "ui_name" ), newready ? common->GetLocalizedString( "#str_104300" ) : common->GetLocalizedString( "#str_104301" ) );
}
ready = newready;
}
int newTeam = ( idStr::Icmp( userInfo->GetString( "ui_team" ), "Strogg" ) == 0 );
if( hud && gameLocal.IsTeamGame() ) {
hud->HandleNamedEvent( (team ? "setTeam_strogg" : "setTeam_marine") );
} else if( hud ) {
hud->HandleNamedEvent( "setTeam_marine" );
}
if ( gameLocal.IsTeamGame() && newTeam != latchedTeam ) {
team = newTeam;
if ( gameLocal.isServer ) {
int verifyTeam = gameLocal.mpGame.VerifyTeamSwitch( newTeam, this );
if( verifyTeam != newTeam ) {
if( verifyTeam == TEAM_MARINE || verifyTeam == TEAM_STROGG ) {
userInfo->Set( "ui_team", gameLocal.mpGame.teamNames[ verifyTeam ] );
if( IsLocalClient() ) {
cvarSystem->SetCVarString( "ui_team", gameLocal.mpGame.teamNames[ verifyTeam ] );
}
modifiedInfo = true;
team = verifyTeam;
}
}
}
// if still OK to change
if( team != latchedTeam ) {
if( gameLocal.isServer ) {
gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
}
SetInitialHud();
if( mphud ) {
mphud->SetStateInt( "playerteam", team );
mphud->HandleNamedEvent( "TeamChange" );
}
if( IsLocalClient() ) {
alreadyDidTeamAnnouncerSound = true;
// the client might set its team to a value before the server corrects for team balance
gameLocal.mpGame.RemoveAnnouncerSound( AS_TEAM_JOIN_MARINE );
gameLocal.mpGame.RemoveAnnouncerSound( AS_TEAM_JOIN_STROGG );
if ( !wantSpectate ) {
if( team == TEAM_STROGG ) {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TEAM_JOIN_STROGG, gameLocal.time + 500 );
} else if( team == TEAM_MARINE ) {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TEAM_JOIN_MARINE, gameLocal.time + 500 );
}
}
}
// ATVI DevTrack #13224 - update on each team change
iconManager->UpdateTeamIcons();
latchedTeam = team;
}
}
//if ( !gameLocal.isClient && gameLocal.serverInfo.GetBool( "si_autoBalance" ) && gameLocal.IsTeamGame() ) {
// bool teamsBalanced = BalanceTeam();
// modifiedInfo |= teamsBalanced;
//}
UpdateModelSetup();
if( (gameLocal.isServer && gameLocal.mpGame.IsInGame( entityNumber )) || (gameLocal.isClient && IsLocalClient() ) ) {
isChatting = userInfo->GetBool( "ui_chat", "0" );
if ( isChatting && pfl.dead ) {
// if dead, always force chat icon off.
isChatting = false;
userInfo->SetBool( "ui_chat", false );
modifiedInfo |= true;
}
}
// grab hitscan tint
hitscanTint = userInfo->GetVec4( "ui_hitscanTint", "81 1 1 1" );
HSVtoRGB( hitscanTint.x, hitscanTint.y, hitscanTint.z, hitscanTint.x, hitscanTint.y, hitscanTint.z );
// force alpha to 1
hitscanTint.w = 1.0f;
return modifiedInfo;
}
/*
===============
idPlayer::UpdateHudAmmo
===============
*/
void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
int inclip;
int ammoamount;
assert( weapon );
assert( _hud );
inclip = weapon->AmmoInClip();
ammoamount = weapon->AmmoAvailable();
if ( ammoamount < 0 ) {
// show infinite ammo
_hud->SetStateString( "player_ammo", "-1" );
_hud->SetStateString( "player_totalammo", "-1" );
_hud->SetStateFloat ( "player_ammopct", 1.0f );
} else if ( weapon->ClipSize ( ) && !gameLocal.isMultiplayer ) {
_hud->SetStateInt ( "player_clip_size", weapon->ClipSize() );
_hud->SetStateFloat ( "player_ammopct", (float)inclip / (float)weapon->ClipSize ( ) );
if ( weapon->ClipSize ( )==1) {
_hud->SetStateInt ( "player_totalammo", ammoamount );
}
else {
_hud->SetStateInt ( "player_totalammo", ammoamount - inclip );
}
_hud->SetStateInt ( "player_ammo", inclip );
} else {
_hud->SetStateFloat ( "player_ammopct", (float)ammoamount / (float)weapon->maxAmmo );
_hud->SetStateInt ( "player_totalammo", ammoamount );
_hud->SetStateInt ( "player_ammo", -1 );
}
_hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
}
/*
===============
idPlayer::UpdateHudStats
===============
*/
void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
int temp;
assert ( _hud );
temp = _hud->State().GetInt ( "player_health", "-1" );
if ( temp != health ) {
_hud->SetStateInt ( "player_healthDelta", temp == -1 ? 0 : (temp - health) );
_hud->SetStateInt ( "player_health", health < -100 ? -100 : health );
_hud->SetStateFloat ( "player_healthpct", idMath::ClampFloat ( 0.0f, 1.0f, (float)health / (float)inventory.maxHealth ) );
_hud->HandleNamedEvent ( "updateHealth" );
}
temp = _hud->State().GetInt ( "player_armor", "-1" );
if ( temp != inventory.armor ) {
_hud->SetStateInt ( "player_armorDelta", temp == -1 ? 0 : (temp - inventory.armor) );
_hud->SetStateInt ( "player_armor", inventory.armor );
_hud->SetStateFloat ( "player_armorpct", idMath::ClampFloat ( 0.0f, 1.0f, (float)inventory.armor / (float)inventory.maxarmor ) );
_hud->HandleNamedEvent ( "updateArmor" );
}
// Boss bar
if ( _hud->State().GetInt ( "boss_health", "-1" ) != (bossEnemy ? bossEnemy->health : -1) ) {
if ( !bossEnemy || bossEnemy->health <= 0 ) {
bossEnemy = NULL;
_hud->SetStateInt ( "boss_health", -1 );
_hud->HandleNamedEvent ( "hideBossBar" );
_hud->HandleNamedEvent ( "hideBossShieldBar" ); // grrr, for boss buddy..but maybe other bosses will have shields?
} else {
_hud->SetStateInt ( "boss_health", bossEnemy->health );
_hud->HandleNamedEvent ( "updateBossBar" );
}
}
// god mode information
_hud->SetStateString( "player_god", va( "%i", (godmode && g_showGodDamage.GetBool()) ) );
_hud->SetStateString( "player_god_damage", va( "%i", godmodeDamage ) );
// Update the hit direction
idVec3 localDir;
viewAxis.ProjectVector( lastDamageDir, localDir );
_hud->SetStateFloat( "hitdir", localDir.ToAngles()[YAW] + 180.0f );
//_hud->HandleNamedEvent( "updateArmorHealthAir" );
if ( weapon ) {
UpdateHudAmmo( _hud );
}
UpdateHudPowerUps( _hud );
if ( hud_showSpeed.GetBool() ) {
idVec3 velocity = physicsObj.GetLinearVelocity();
velocity[2] = 0;
_hud->SetStateString("player_speed", va("%d ups", (int)velocity.Length()));
}
if (hud_showInput.GetBool())
DrawInput();
_hud->StateChanged( gameLocal.time );
}
void idPlayer::DrawInput()
{
const idVec4 highlightColor(1.0f, 0.0f, 0.0f, 0.8f);
sscanf( hud_inputColor.GetString(), "%f %f %f", &highlightColor.x, &highlightColor.y, &highlightColor.z );
const idVec4 normalColor(1.0f, 1.0f, 1.0f, 0.8f);
idVec2 inputPos(500,90);
sscanf( hud_inputPosition.GetString(), "%f %f", &inputPos.x, &inputPos.y );
const idMaterial *centerMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_center");
renderSystem->SetColor( normalColor );
renderSystem->DrawStretchPic(inputPos.x + 10, inputPos.y + 10, 10.0f, 10.0f, 0.0, 0.0, 1.0f, 1.0f, centerMaterial );
const idMaterial *forwardMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_forward");
renderSystem->SetColor( (usercmd.forwardmove == 127) ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x + 10, inputPos.y, 10.0f, 10.0f, 0.0, 0.0, 1.0f, 1.0f, forwardMaterial );
const idMaterial *backMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_back");
renderSystem->SetColor( (usercmd.forwardmove == -127) ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x + 10, inputPos.y + 20, 10.0f, 10.0f, 0.0, 0.0, 1.0f, 1.0f, backMaterial );
const idMaterial *leftMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_moveleft");
renderSystem->SetColor( (usercmd.rightmove == -127) ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x, inputPos.y + 10, 10.0f, 10.0f, 0.0, 0.0, 1.0f, 1.0f, leftMaterial );
const idMaterial *rightMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_moveright");
renderSystem->SetColor( (usercmd.rightmove == 127) ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x + 20, inputPos.y + 10, 10.0f, 10.0f, 0.0, 0.0, 1.0f, 1.0f, rightMaterial );
const idMaterial *upMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_moveup");
renderSystem->SetColor( physicsObj.IsJumping() ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x + 36, inputPos.y, 14.0f, 14.0f, 0.0, 0.0, 1.0f, 1.0f, upMaterial );
const idMaterial *downMaterial = declManager->FindMaterial("gfx/guis/showinput/hud_showinput_movedown");
renderSystem->SetColor( physicsObj.IsCrouching() ? highlightColor : normalColor );
renderSystem->DrawStretchPic(inputPos.x + 36, inputPos.y + 17, 14.0f, 14.0f, 0.0, 0.0, 1.0f, 1.0f, downMaterial );
}
/*
===============
idPlayer::UpdateHudWeapon
===============
*/
void idPlayer::UpdateHudWeapon( int displayWeapon ) {
if ( ( displayWeapon < -1 ) || ( displayWeapon >= MAX_WEAPONS ) ) {
common->DPrintf( "displayweapon was out of range" );
return;
}
int index = 0;
int idealIndex = 0;
idUserInterface * hud = idPlayer::hud;
idUserInterface * mphud = idPlayer::mphud;
idUserInterface * cursor = idPlayer::cursor;
idPlayer *p = gameLocal.GetLocalPlayer();
if ( p && p->spectating && p->spectator == entityNumber ) {
hud = p->hud;
mphud = p->mphud;
cursor = p->cursor;
}
if ( !hud || !weapon ) {
return;
}
for ( int i = 0; i < MAX_WEAPONS; i++ ) {
const char *weapnum = va( "weapon%d", i );
int weapstate = 0;
if ( ( inventory.weapons & ( 1 << i ) ) && spawnArgs.GetBool( va( "weapon%d_cycle", i ) ) ) {
hud->SetStateBool( weapnum, true );
const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( weap && *weap ) {
weapstate++;
if ( idealWeapon == i ) {
idealIndex = index;
}
const char *weaponIcon = GetWeaponDef ( i )->dict.GetString ( "inv_icon" );
hud->SetStateInt ( va( "weapon%d_index", i ), index++ );
hud->SetStateString ( va( "weapon%d_icon", i ), weaponIcon );
hud->SetStateInt ( va( "weapon%d_ammo", i ), inventory.ammo[ inventory.AmmoIndexForWeaponClass( weap ) ] );
}
} else {
hud->SetStateBool( weapnum, false );
}
}
hud->SetStateInt( "weaponcount", index );
const idMaterial *material = declManager->FindMaterial( weapon->GetIcon() );
if ( material ) {
material->SetSort( SS_GUI );
}
material = declManager->FindMaterial( weapon->spawnArgs.GetString( "inv_icon" ) );
hud->SetStateString( "weapicon", weapon->GetIcon() );
hud->SetStateString( "ammoIcon", weapon->spawnArgs.GetString( "inv_icon" ) );
hud->SetStateInt( "player_weapon", currentWeapon );
hud->SetStateInt( "player_lastweapon", previousWeapon );
int hud_idealWeapon = ( displayWeapon != -1 ) ? displayWeapon : idealWeapon;
hud->SetStateInt( "player_idealWeapon", hud_idealWeapon );
// unused in q4mp hud.gui
//hud->SetStateInt( "player_idealIndex", idealIndex );
// Weapon name for weapon selection
const idDeclEntityDef* w = GetWeaponDef( ( displayWeapon != -1 ) ? displayWeapon : idealWeapon );
if ( w ) {
idStr langToken = w->dict.GetString( "inv_name" );
hud->SetStateString( "weaponname", common->GetLocalizedString( langToken ) );
}
UpdateHudAmmo( hud );
if ( cursor ) {
weapon->UpdateCrosshairGUI( cursor );
// mekberg: force a redraw so ON_INIT gets called and doesn't stomp all over
// the color values we set in weaponChange.
cursor->Redraw( gameLocal.time );
cursor->HandleNamedEvent( "weaponChange" );
}
hud->HandleNamedEvent( "weaponChange" );
hud->StateChanged( gameLocal.time );
weaponChangeIconsUp = true;
}
void idPlayer::UpdateHudPowerUps( idUserInterface *_hud ) {
assert( _hud );
int i, index;
_hud->HandleNamedEvent( "clearPowerups" );
for ( i = 0, index = 0; i < POWERUP_MAX; i++ ) {
// Do we have this powerup?
if ( !(inventory.powerups & ( 1 << i ) ) ) {
continue;
}
if ( inventory.powerupEndTime[i] > gameLocal.time || inventory.powerupEndTime[i] == -1 ) {
// If there is still time remaining on the powerup then update the hud
// for flags, set the powerup_flag_* variables, which give us a special pulsing flag display
if( i == POWERUP_CTF_MARINEFLAG || i == POWERUP_CTF_STROGGFLAG || i == POWERUP_CTF_ONEFLAG ) {
_hud->SetStateInt( "powerup_flag_visible", 1 );
} else {
_hud->SetStateString ( va("powerup%d_icon", index ), GetPowerupDef(i)->dict.GetString ( "inv_icon" ) );
_hud->SetStateString ( va("powerup%d_time", index ), inventory.powerupEndTime[i] == -1 ? "" : va( "%d" , (int)MS2SEC(inventory.powerupEndTime[i] - gameLocal.time) + 1 ) );
_hud->SetStateInt ( va( "powerup%d_visible", index ), 1 );
index++;
}
continue;
}
}
}
/*
===============
idPlayer::StartRadioChatter
===============
*/
void idPlayer::StartRadioChatter ( void ) {
if ( hud ) {
hud->HandleNamedEvent( "radioChatterUp" );
}
if ( vehicleController.IsDriving ( ) ) {
vehicleController.StartRadioChatter ( );
}
}
/*
===============
idPlayer::StopRadioChatter
===============
*/
void idPlayer::StopRadioChatter ( void ) {
if ( hud ) {
hud->HandleNamedEvent( "radioChatterDown" );
}
if ( vehicleController.IsDriving( ) ) {
vehicleController.StopRadioChatter( );
}
}
/*
===============
idPlayer::DrawShadow
===============
*/
void idPlayer::DrawShadow( renderEntity_t *headRenderEnt ) {
if ( gameLocal.isMultiplayer && g_skipPlayerShadowsMP.GetBool() ) {
// Disable all player shadows for the local client
renderEntity.suppressShadowInViewID = gameLocal.localClientNum+1;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = gameLocal.localClientNum+1;
}
} else if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() ) {
// Show all player shadows
renderEntity.suppressShadowInViewID = 0;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = 0;
}
} else {
// Only show player shadows for other clients
renderEntity.suppressShadowInViewID = entityNumber+1;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = entityNumber+1;
}
}
}
/*
===============
idPlayer::DrawHUD
===============
*/
void idPlayer::DrawHUD( idUserInterface *_hud ) {
idUserInterface * cursor = idPlayer::cursor;
if ( gameLocal.GetLocalPlayer() ) {
if ( team != gameLocal.GetLocalPlayer()->hudTeam && _hud ) {
_hud->HandleNamedEvent( (team ? "setTeam_strogg" : "setTeam_marine") );
gameLocal.GetLocalPlayer()->hudTeam = team;
}
}
// if updating the hud of a followed client
idPlayer *p = gameLocal.GetLocalPlayer();
if ( p && p != this && p->spectating && p->spectator == entityNumber ) {
cursor = p->GetCursorGUI();
if ( cursor ) {
cursor->HandleNamedEvent( "showCrossCombat" );
}
}
if ( disableHud || influenceActive != INFLUENCE_NONE || privateCameraView || !_hud || !g_showHud.GetBool() ) {
return;
}
if ( objectiveSystemOpen ) {
if ( !GuiActive() ) {
// showing weapon zoom gui when objectives are open because that's the way I'z told to make it werkz
if ( weapon && weapon->GetZoomGui() && zoomed ) {
weapon->GetZoomGui()->Redraw( gameLocal.time );
}
}
return;
}
_hud->SetStateBool( "mp", true );
// Draw the cinematic hud when in a cinematic
if ( gameLocal.GetCamera() ) {
if ( cinematicHud && !(gameLocal.editors & EDITOR_MODVIEW) ) {
cinematicHud->Redraw( gameLocal.time );
}
return;
}
// Let the vehicle draw the hud instead
if ( vehicleController.IsDriving( ) ) {
if ( !gameDebug.IsHudActive( DBGHUD_ANY ) ) {
vehicleController.DrawHUD( );
if ( cursor && health > 0 ) {
// mekberg: adjustable crosshair size.
int crossSize = cvarSystem->GetCVarInteger( "g_crosshairSize" );
crossSize = crossSize - crossSize % 8;
cvarSystem->SetCVarInteger( "g_crosshairSize", crossSize );
cursor->SetStateInt( "g_crosshairSize", crossSize );
cursor->SetStateBool( "vehiclecursor", true );
vehicleController.UpdateCursorGUI( cursor );
cursor->Redraw( gameLocal.time );
}
}
_hud = GetHud();
// Boss bar
if ( _hud && _hud->State().GetInt( "boss_health", "-1" ) != (bossEnemy ? bossEnemy->health : -1) ) {
if ( !bossEnemy || bossEnemy->health <= 0 ) {
bossEnemy = NULL;
_hud->SetStateInt( "boss_health", -1 );
_hud->HandleNamedEvent( "hideBossBar" );
_hud->HandleNamedEvent( "hideBossShieldBar" ); // grrr, for boss buddy..but maybe other bosses will have shields?
} else {
_hud->SetStateInt( "boss_health", bossEnemy->health );
_hud->HandleNamedEvent( "updateBossBar" );
}
}
return;
}
if ( cursor ) {
cursor->SetStateBool( "vehiclecursor", false );
}
// FIXME: this is temp to allow the sound meter to show up in the hud
// it should be commented out before shipping but the code can remain
// for mod developers to enable for the same functionality
_hud->SetStateInt( "s_debug", cvarSystem->GetCVarInteger( "s_showLevelMeter" ) );
// don't draw main hud in spectator (only mphud)
if ( !spectating && !gameDebug.IsHudActive( DBGHUD_ANY ) ) {
// weapon targeting crosshair
if ( !GuiActive() ) {
if ( weapon && weapon->GetZoomGui() && zoomed ) {
weapon->GetZoomGui()->Redraw( gameLocal.time );
}
if ( cursor && health > 0 ) {
// Pass the current weapon to the cursor gui for custom crosshairs
int crossSize = cvarSystem->GetCVarInteger( "g_crosshairSize" );
crossSize = crossSize - crossSize % 8;
cvarSystem->SetCVarInteger( "g_crosshairSize", crossSize );
cursor->SetStateInt( "g_crosshairSize", crossSize );
cursor->Redraw( gameLocal.time );
}
}
UpdateHudStats( _hud );
if ( focusBrackets ) {
// If 2d_calc is still true then the gui didnt render so we can abandon it
if ( focusBrackets->State().GetBool( "2d_calc" ) ) {
focusBrackets->SetStateBool( "2d_calc", false );
focusBrackets = NULL;
focusBracketsTime = 0;
_hud->HandleNamedEvent( "hideBrackets" );
} else {
_hud->SetStateString( "bracket_left", focusBrackets->State().GetString( "2d_min_x" ) );
_hud->SetStateString( "bracket_top", focusBrackets->State().GetString( "2d_min_y" ) );
_hud->SetStateFloat( "bracket_width", focusBrackets->State().GetFloat( "2d_max_x" ) - focusBrackets->State().GetFloat( "2d_min_x" ) );
_hud->SetStateFloat( "bracket_height", focusBrackets->State().GetFloat( "2d_max_y" ) - focusBrackets->State().GetFloat( "2d_min_y" ) );
// TODO: Find a way to get bracket text from gui to hud
}
}
_hud->Redraw( gameLocal.realClientTime );
}
if ( gameLocal.isMultiplayer ) {
idUserInterface* _mphud = mphud;
if ( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer() != this ) {
// if we're spectating someone else, use our local hud
_mphud = gameLocal.GetLocalPlayer()->mphud;
}
if ( _mphud ) {
gameLocal.mpGame.UpdateHud( _mphud );
_mphud->Redraw( gameLocal.time );
}
if ( overlayHud && overlayHudTime > gameLocal.time && overlayHudTime != 0 ) {
overlayHud->Redraw( gameLocal.time );
} else {
overlayHud = NULL;
overlayHudTime = 0;
}
}
}
/*
===============
idPlayer::EnterCinematic
===============
*/
void idPlayer::EnterCinematic( void ) {
Hide();
// RAVEN BEGIN
// jnewquist: Cinematics are letterboxed, this auto-fixes on widescreens
g_fixedHorizFOV.SetBool(true);
// RAVEN END
if ( hud ) {
hud->HandleNamedEvent( "radioChatterDown" );
}
cinematicHud = NULL;
// See if camera has custom cinematic gui
if ( gameLocal.GetCamera ( ) ) {
const char* guiCinematic;
guiCinematic = gameLocal.GetCamera()->spawnArgs.GetString ( "guiCinematic", "" );
if ( *guiCinematic ) {
cinematicHud = uiManager->FindGui( guiCinematic, true, false, true );
}
}
// Load default cinematic gui?
if ( !cinematicHud ) {
const char* temp;
if ( spawnArgs.GetString ( "cinematicHud", "", &temp ) ) {
cinematicHud = uiManager->FindGui( temp, true, false, true );
}
}
// Have the cinematic hud start
if ( cinematicHud ) {
cinematicHud->Activate ( true, gameLocal.time );
cinematicHud->HandleNamedEvent ( "cinematicStart" );
// RAVEN BEGIN
// jnewquist: Option to adjust vertical fov instead of horizontal for non 4:3 modes
if ( cvarSystem->GetCVarInteger( "r_aspectRatio" ) != 0 ) {
cinematicHud->HandleNamedEvent ( "hideLetterbox" );
}
// RAVEN END
}
physicsObj.SetLinearVelocity( vec3_origin );
if ( weaponEnabled && weapon ) {
//this preSave kills all effects and sounds that we don't need lingering around.
weapon->PreSave();
weapon->EnterCinematic();
}
// Reset state flags
memset( &pfl, 0, sizeof(pfl) );
pfl.onGround = true;
pfl.dead = (health <= 0);
}
/*
===============
idPlayer::ExitCinematic
===============
*/
void idPlayer::ExitCinematic( void ) {
Show();
// RAVEN BEGIN
// jnewquist: Cinematics are letterboxed, this auto-fixes on widescreens
g_fixedHorizFOV.SetBool(false);
// RAVEN END
if ( weaponEnabled && weapon ) {
//and this will restore them!
weapon->PostSave();
weapon->ExitCinematic();
}
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
UpdateState();
}
/*
===============
idPlayer::SkipCinematic
===============
*/
bool idPlayer::SkipCinematic( void ) {
StartSound( "snd_skipcinematic", SND_CHANNEL_ANY, 0, false, NULL );
return gameLocal.SkipCinematic();
}
/*
=====================
idPlayer::UpdateConditions
=====================
*/
void idPlayer::UpdateConditions( void ) {
idVec3 velocity;
float fallspeed;
float forwardspeed;
float sidespeed;
// minus the push velocity to avoid playing the walking animation and sounds when riding a mover
velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
fallspeed = velocity * physicsObj.GetGravityNormal();
if ( influenceActive ) {
pfl.forward = false;
pfl.backward = false;
pfl.strafeLeft = false;
pfl.strafeRight = false;
} else if ( gameLocal.time - lastDmgTime < 500 ) {
forwardspeed = velocity * viewAxis[ 0 ];
sidespeed = velocity * viewAxis[ 1 ];
pfl.forward = pfl.onGround && ( forwardspeed > 20.01f );
pfl.backward = pfl.onGround && ( forwardspeed < -20.01f );
pfl.strafeLeft = pfl.onGround && ( sidespeed > 20.01f );
pfl.strafeRight = pfl.onGround && ( sidespeed < -20.01f );
} else if ( xyspeed > MIN_BOB_SPEED ) {
pfl.forward = pfl.onGround && ( usercmd.forwardmove > 0 );
pfl.backward = pfl.onGround && ( usercmd.forwardmove < 0 );
pfl.strafeLeft = pfl.onGround && ( usercmd.rightmove < 0 );
pfl.strafeRight = pfl.onGround && ( usercmd.rightmove > 0 );
} else {
pfl.forward = false;
pfl.backward = false;
pfl.strafeLeft = false;
pfl.strafeRight = false;
}
pfl.run = 1;
pfl.dead = ( health <= 0 );
}
/*
==================
idPlayer::WeaponFireFeedback
Called when a weapon fires, generates head twitches, etc
==================
*/
void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
// force a blink
blink_time = 0;
// play the fire animation
pfl.weaponFired = true;
// Bias the intent direction more heavily due to firing
BiasIntentDir( viewAxis[0]*100.0f, 1.0f );
// update view feedback
playerView.WeaponFireFeedback( weaponDef );
}
/*
===============
idPlayer::StopFiring
===============
*/
void idPlayer::StopFiring( void ) {
pfl.attackHeld = false;
pfl.weaponFired = false;
pfl.reload = false;
if ( weapon ) {
weapon->EndAttack();
}
}
/*
===============
idPlayer::FireWeapon
===============
*/
void idPlayer::FireWeapon( void ) {
idMat3 axis;
idVec3 muzzle;
if ( gameLocal.GetIsFrozen() && gameLocal.gameType == GAME_DEADZONE ) {
return;
}
if ( privateCameraView ) {
return;
}
if ( g_editEntityMode.GetInteger() ) {
GetViewPos( muzzle, axis );
gameLocal.editEntities->SelectEntity( muzzle, axis[0], this );
return;
}
if ( !hiddenWeapon && weapon->IsReady() ) {
// cheap hack so in MP the LG isn't allowed to fire in the short lapse while it goes from Fire -> Idle before changing to another weapon
// this gimps the weapon a lil bit but is consistent with the visual feedback clients are getting since 1.0
bool noFireWhileSwitching = false;
noFireWhileSwitching = ( gameLocal.isMultiplayer && idealWeapon != currentWeapon && weapon->NoFireWhileSwitching() );
if ( !noFireWhileSwitching ) {
if ( weapon->AmmoInClip() || weapon->AmmoAvailable() ) {
pfl.attackHeld = true;
weapon->BeginAttack();
} else {
pfl.attackHeld = false;
pfl.weaponFired = false;
StopFiring();
NextBestWeapon();
}
} else {
StopFiring();
}
}
// If reloading when fire is hit cancel the reload
else if ( weapon->IsReloading() ) {
weapon->CancelReload();
}
if ( hud && weaponChangeIconsUp ) {
hud->HandleNamedEvent( "weaponFire" );
// nrausch: objectiveSystem does not necessarily exist (in mp it doesn't)
if ( objectiveSystem ) {
objectiveSystem->HandleNamedEvent( "weaponFire" );
}
weaponChangeIconsUp = false;
}
}
/*
===============
idPlayer::CacheWeapons
===============
*/
void idPlayer::CacheWeapons( void ) {
idStr weap;
int w;
// check if we have any weapons
if ( !inventory.weapons ) {
return;
}
for( w = 0; w < MAX_WEAPONS; w++ ) {
if ( inventory.weapons & ( 1 << w ) ) {
if ( !GetWeaponDef ( w ) ) {
inventory.weapons &= ~( 1 << w );
} else {
rvWeapon::CacheWeapon( spawnArgs.GetString( va( "def_weapon%d", w ) ) );
}
}
}
}
/*
===============
idPlayer::Give
===============
*/
bool idPlayer::Give( const char *statname, const char *value, bool dropped ) {
int amount;
if ( pfl.dead ) {
return false;
}
if ( IsInVehicle ( ) ) {
vehicleController.Give ( statname, value );
}
int boundaryHealth = inventory.maxHealth;
int boundaryArmor = inventory.maxarmor;
if( PowerUpActive( POWERUP_GUARD ) ) {
boundaryHealth = inventory.maxHealth / 2;
boundaryArmor = inventory.maxarmor / 2;
}
if( PowerUpActive( POWERUP_SCOUT ) ) {
boundaryArmor = 0;
}
if ( gameLocal.isMultiplayer ) {
//In MP, you can get twice your max from pickups
boundaryArmor *= 2;
}
if ( !idStr::Icmp( statname, "health" ) ) {
if ( health >= boundaryHealth ) {
return false;
}
amount = atoi( value );
if ( amount ) {
health += amount;
if ( health > boundaryHealth ) {
health = boundaryHealth;
}
}
} else if ( !idStr::Icmp( statname, "bonushealth" ) ) {
// allow health over max health
if ( health >= boundaryHealth * 2 ) {
return false;
}
amount = atoi( value );
if ( amount ) {
health += amount;
if ( health > boundaryHealth * 2 ) {
health = boundaryHealth * 2;
}
}
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
} else if ( !idStr::Icmp( statname, "armor" ) ) {
if ( inventory.armor >= boundaryArmor ) {
return false;
}
amount = atoi( value );
inventory.armor += amount;
if ( inventory.armor > boundaryArmor ) {
inventory.armor = boundaryArmor;
}
nextArmorPulse = gameLocal.time + ARMOR_PULSE;
} else if ( !idStr::Icmp( statname, "air" ) ) {
if ( airTics >= pm_airTics.GetInteger() ) {
return false;
}
airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger();
if ( airTics > pm_airTics.GetInteger() ) {
airTics = pm_airTics.GetInteger();
}
} else if ( !idStr::Icmp ( statname, "weaponmod" ) ) {
if( !idStr::Icmp( value, "all" ) ) {
for( int i = 0; i < MAX_WEAPONS; i++ ) {
if ( inventory.weapons & ( 1 << i ) ) {
GiveWeaponMods( i, 0xFFFFFFFF );
}
}
} else {
const char* pos = value;
while( pos != NULL ) {
const char* end = strchr( pos, ',' );
int len;
if ( end ) {
len = end - pos;
end++;
} else {
len = strlen( pos );
}
idStr weaponMod ( pos, 0, len );
GiveWeaponMod ( weaponMod );
pos = end;
}
}
} else {
return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true, dropped );
}
return true;
}
/*
===============
idPlayer::GiveItem
Returns false if the item shouldn't be picked up
===============
*/
bool idPlayer::GiveItem( idItem *item ) {
int i;
const idKeyValue *arg;
idDict attr;
bool gave;
bool dropped = item->spawnArgs.GetBool( "dropped" );
if ( gameLocal.isMultiplayer && spectating ) {
return false;
}
item->GetAttributes( attr );
if( gameLocal.isServer || !gameLocal.isMultiplayer ) {
gave = false;
bool skipWeaponKey = false;
bool skipRestOfKeys = false;
if ( gameLocal.IsMultiplayer() ) {
dropped = item->spawnArgs.GetBool( "dropped" );
if ( item->spawnArgs.FindKey( "weaponclass" ) ) {
//this is really fucking lame, but
//this is the only way we know we're trying
//to pick up a weapon before we blindly start
//processesing the attribute arguments in
//whatever order they're in below. We need
//to not process any at all if we're not allowed
//to pick up the weapon in the first place!
arg = attr.FindKey( "weapon" );
if ( arg ) {
skipWeaponKey = true;
if ( Give( arg->GetKey(), arg->GetValue(), dropped ) ) {
gave = true;
} else if ( !dropped//not a dropped weapon
&& gameLocal.IsWeaponsStayOn() ) {
//if failed to give weapon, don't give anything else with the weapon
skipRestOfKeys = true;
}
}
}
}
if ( !skipRestOfKeys ) {
for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
arg = attr.GetKeyVal( i );
if ( skipWeaponKey && arg->GetKey() == "weapon" ) {
//already processed this above
continue;
}
if ( Give( arg->GetKey(), arg->GetValue(), dropped ) ) {
gave = true;
}
}
}
// hack - powerups call into this code to let them give stuff based on inv_ keywords
// for powerups that don't have any ammo/etc to give to the player, we still want to
// display the inv_name on the hud
// since idItemPowerup::GiveToPlayer() handles whether or not a player gets a powerup,
// we can override gave here for powerups
if ( !gave && !item->IsType( idItemPowerup::GetClassType() ) ) {
return false;
}
} else {
gave = true;
}
arg = item->spawnArgs.MatchPrefix( "inv_ammo_", NULL );
if ( arg && hud ) {
hud->HandleNamedEvent( "ammoPulse" );
}
arg = item->spawnArgs.MatchPrefix( "inv_health", NULL );
if ( arg && hud ) {
hud->HandleNamedEvent( "healthPulse" );
}
arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
if ( arg && hud ) {
// We need to update the weapon hud manually, but not
// the armor/ammo/health because they are updated every
// frame no matter what
if ( gameLocal.isMultiplayer ) {
UpdateHudWeapon();
} else {
//so weapon mods highlight the correct weapon when received
int weapon = SlotForWeapon( arg->GetValue() );
UpdateHudWeapon( weapon );
}
hud->HandleNamedEvent( "weaponPulse" );
}
arg = item->spawnArgs.MatchPrefix( "inv_armor", NULL );
if ( arg && hud ) {
hud->HandleNamedEvent( "armorPulse" );
}
// GiveDatabaseEntry ( &item->spawnArgs );
// Show the item pickup on the hud
if ( hud ) {
idStr langToken = item->spawnArgs.GetString( "inv_name" );
hud->SetStateString ( "itemtext", common->GetLocalizedString( langToken ) );
hud->SetStateString ( "itemicon", item->spawnArgs.GetString( "inv_icon" ) );
hud->HandleNamedEvent ( "itemPickup" );
}
//RITUAL BEGIN
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() )
gameLocal.mpGame.RedrawLocalBuyMenu();
//RITUAL END
return gave;
}
/*
===============
idPlayer::PowerUpModifier
===============
*/
float idPlayer::PowerUpModifier( int type ) {
float mod = 1.0f;
if ( PowerUpActive( POWERUP_QUADDAMAGE ) ) {
switch( type ) {
case PMOD_PROJECTILE_DAMAGE: {
mod *= 3.0f;
break;
}
case PMOD_MELEE_DAMAGE: {
mod *= 3.0f;
break;
}
case PMOD_PROJECTILE_DEATHPUSH: {
mod *= 2.0f;
break;
}
}
}
if ( PowerUpActive( POWERUP_HASTE ) ) {
switch ( type ) {
case PMOD_SPEED:
mod *= 1.3f;
break;
case PMOD_FIRERATE:
mod *= 0.7f;
break;
}
}
// Arena CTF powerups
if( PowerUpActive( POWERUP_AMMOREGEN ) ) {
switch( type ) {
case PMOD_FIRERATE: {
mod *= 0.7f;
break;
}
}
}
if( PowerUpActive( POWERUP_DOUBLER ) ) {
switch( type ) {
case PMOD_PROJECTILE_DAMAGE: {
mod *= 2.0f;
break;
}
case PMOD_MELEE_DAMAGE: {
mod *= 2.0f;
break;
}
}
}
//RITUAL BEGIN
if( PowerUpActive( POWERUP_TEAM_DAMAGE_MOD ) ) {
switch( type ) {
case PMOD_PROJECTILE_DAMAGE: {
mod *= 1.75f;
break;
}
case PMOD_MELEE_DAMAGE: {
mod *= 1.75f;
break;
}
case PMOD_FIRERATE: {
mod *= 0.80f;
break;
}
}
}
//RITUAL END
if( PowerUpActive( POWERUP_SCOUT ) ) {
switch( type ) {
case PMOD_FIRERATE: {
mod *= (2.0f / 3.0f);
break;
}
case PMOD_SPEED: {
mod *= 1.5f;
break;
}
}
}
return mod;
}
/*
===============
idPlayer::PowerUpActive
===============
*/
bool idPlayer::PowerUpActive( int powerup ) const {
return ( inventory.powerups & ( 1 << powerup ) ) != 0;
}
/*
===============
idPlayer::StartPowerUpEffect
===============
*/
void idPlayer::StartPowerUpEffect( int powerup ) {
switch( powerup ) {
case POWERUP_CTF_MARINEFLAG: {
AddClientModel( "mp_ctf_flag_pole" );
AddClientModel( "mp_ctf_marine_flag_world" );
flagEffect = PlayEffect( "fx_ctf_marine_flag_world", animator.GetJointHandle( spawnArgs.GetString( "flagEffectJoint" ) ), spawnArgs.GetVector( "flagEffectOrigin" ), physicsObj.GetAxis(), true );
break;
}
case POWERUP_CTF_STROGGFLAG: {
AddClientModel( "mp_ctf_flag_pole" );
AddClientModel( "mp_ctf_strogg_flag_world" );
flagEffect = PlayEffect( "fx_ctf_strogg_flag_world", animator.GetJointHandle( spawnArgs.GetString( "flagEffectJoint" ) ), spawnArgs.GetVector( "flagEffectOrigin" ), physicsObj.GetAxis(), true );
break;
}
case POWERUP_CTF_ONEFLAG: {
AddClientModel( "mp_ctf_one_flag" );
break;
}
case POWERUP_DEADZONE: {
PlayEffect( "fx_deadzone", animator.GetJointHandle( "origin" ), true );
break;
}
case POWERUP_QUADDAMAGE: {
powerUpOverlay = quadOverlay;
StopEffect( "fx_regeneration" );
PlayEffect( "fx_quaddamage", animator.GetJointHandle( "chest" ), true );
StartSound( "snd_quaddamage_idle", SND_CHANNEL_POWERUP_IDLE, 0, false, NULL );
// Spawn quad effect
powerupEffect = gameLocal.GetEffect( spawnArgs, "fx_quaddamage_crawl" );
powerupEffectTime = gameLocal.time;
powerupEffectType = POWERUP_QUADDAMAGE;
break;
}
case POWERUP_REGENERATION: {
// when buy mode is enabled, we use the guard effect for team powerup regen ( more readable than everyone going red )
if ( gameLocal.IsTeamPowerups() ) {
// don't setup the powerup on dead bodies, it will float up where the body is invisible and the orientation will be messed up
if ( teamHealthRegen == NULL ) {
if ( health <= 0 ) {
// we can't start it now, it will be floating where the hidden dead body is
teamHealthRegenPending = true;
} else {
teamHealthRegen = PlayEffect( "fx_guard", renderEntity.origin, renderEntity.axis, true );
}
}
} else {
powerUpOverlay = regenerationOverlay;
StopEffect( "fx_quaddamage" );
PlayEffect( "fx_regeneration", animator.GetJointHandle( "chest" ), true );
// Spawn regen effect
powerupEffect = gameLocal.GetEffect( spawnArgs, "fx_regeneration" );
powerupEffectTime = gameLocal.time;
powerupEffectType = POWERUP_REGENERATION;
}
break;
}
case POWERUP_HASTE: {
powerUpOverlay = hasteOverlay;
hasteEffect = PlayEffect( "fx_haste", GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), true );
break;
}
case POWERUP_INVISIBILITY: {
powerUpOverlay = invisibilityOverlay;
powerUpSkin = declManager->FindSkin( spawnArgs.GetString( "skin_invisibility" ), false );
break;
}
case POWERUP_GUARD: {
if ( arenaEffect != NULL ) {
// don't accumulate. clear whatever was there
arenaEffect->Stop( true );
}
arenaEffect = PlayEffect( "fx_guard", physicsObj.GetOrigin(), physicsObj.GetAxis(), true );
break;
}
case POWERUP_SCOUT: {
if ( arenaEffect != NULL ) {
// don't accumulate. clear whatever was there
arenaEffect->Stop( true );
}
arenaEffect = PlayEffect( "fx_scout", physicsObj.GetOrigin(), physicsObj.GetAxis(), true );
break;
}
case POWERUP_AMMOREGEN: {
if ( gameLocal.IsTeamPowerups() ) {
if ( teamAmmoRegen == NULL ) {
if ( health <= 0 ) {
teamAmmoRegenPending = true;
} else {
teamAmmoRegen = PlayEffect( "fx_ammoregen", renderEntity.origin, renderEntity.axis, true );
}
}
} else {
assert( health > 0 );
if ( arenaEffect != NULL ) {
// don't accumulate. clear whatever was there
arenaEffect->Stop( true );
}
arenaEffect = PlayEffect( "fx_ammoregen", renderEntity.origin, renderEntity.axis, true );
}
break;
}
case POWERUP_TEAM_DAMAGE_MOD: {
assert( gameLocal.IsTeamPowerups() );
if ( teamDoubler == NULL ) {
if ( health <= 0 ) {
teamDoublerPending = true;
} else {
teamDoubler = PlayEffect( "fx_doubler", renderEntity.origin, renderEntity.axis, true );
}
}
break;
}
case POWERUP_DOUBLER: {
assert( health > 0 );
if ( arenaEffect != NULL ) {
// don't accumulate. clear whatever was there
arenaEffect->Stop( true );
}
arenaEffect = PlayEffect( "fx_doubler", renderEntity.origin, renderEntity.axis, true );
break;
}
}
}
/*
===============
idPlayer::StopPowerUpEffect
===============
*/
void idPlayer::StopPowerUpEffect( int powerup ) {
//if the player doesn't have quad, regen, haste or invisibility remaining on him, remove the power up overlay.
if ( !(
(inventory.powerups & ( 1 << POWERUP_QUADDAMAGE ) ) ||
(inventory.powerups & ( 1 << POWERUP_REGENERATION ) ) ||
(inventory.powerups & ( 1 << POWERUP_HASTE ) ) ||
(inventory.powerups & ( 1 << POWERUP_INVISIBILITY ) )
) ) {
powerUpOverlay = NULL;
}
// only quad has a hum, clear it even when other powerups might mean leaving the overlay on
if ( ( inventory.powerups & ( 1 << POWERUP_QUADDAMAGE ) ) == 0 ) {
StopSound( SND_CHANNEL_POWERUP_IDLE, false );
}
switch( powerup ) {
case POWERUP_QUADDAMAGE: {
powerupEffect = NULL;
powerupEffectTime = 0;
powerupEffectType = 0;
StopEffect( "fx_quaddamage" );
break;
}
case POWERUP_REGENERATION: {
if ( gameLocal.IsTeamPowerups() ) {
teamHealthRegenPending = false;
StopEffect( "fx_guard" );
} else {
powerupEffect = NULL;
powerupEffectTime = 0;
powerupEffectType = 0;
StopEffect( "fx_regeneration" );
}
break;
}
case POWERUP_HASTE: {
StopEffect( "fx_haste" );
break;
}
case POWERUP_INVISIBILITY: {
powerUpSkin = NULL;
break;
}
case POWERUP_CTF_STROGGFLAG: {
RemoveClientModel( "mp_ctf_flag_pole" );
RemoveClientModel( "mp_ctf_strogg_flag_world" );
StopEffect( "fx_ctf_strogg_flag_world" );
break;
}
case POWERUP_CTF_MARINEFLAG: {
RemoveClientModel( "mp_ctf_flag_pole" );
RemoveClientModel( "mp_ctf_marine_flag_world" );
StopEffect( "fx_ctf_marine_flag_world" );
break;
}
case POWERUP_CTF_ONEFLAG: {
RemoveClientModel ( "mp_ctf_one_flag" );
break;
}
case POWERUP_DEADZONE: {
StopEffect( "fx_deadzone" );
break;
}
case POWERUP_SCOUT: {
StopEffect( "fx_scout" );
break;
}
case POWERUP_GUARD: {
StopEffect( "fx_guard" );
break;
}
case POWERUP_TEAM_DAMAGE_MOD:
teamDoublerPending = false;
// fallthrough
case POWERUP_DOUBLER: {
StopEffect( "fx_doubler" );
break;
}
case POWERUP_AMMOREGEN: {
teamAmmoRegenPending = false;
StopEffect( "fx_ammoregen" );
break;
}
}
}
/*
===============
idPlayer::GivePowerUp
passiveEffectsOnly - GivePowerup() is used to restore effects on stale players coming
back into snapshot. We don't want to announce powerups in this case
(just re-start effects)
===============
*/
bool idPlayer::GivePowerUp( int powerup, int time, bool team ) {
if ( powerup < 0 || powerup >= POWERUP_MAX ) {
gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
return false;
}
if ( gameLocal.isServer ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteShort( powerup );
msg.WriteBits( 1, 1 );
// team flag only needed for POWERUP_AMMOREGEN
msg.WriteBits( team, 1 );
ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
}
inventory.GivePowerUp( this, powerup, time );
// only start client effects in the same instance
// play all stuff in instance 0 for server netdemo - atm other instances are not recorded
bool playClientEffects = ( ( gameLocal.IsServerDemoPlaying() && instance == 0 ) ||
( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() == instance ) );
switch( powerup ) {
case POWERUP_CTF_MARINEFLAG: {
// shouchard: added notice for picking up the flag
if ( playClientEffects && this == gameLocal.GetLocalPlayer() ) {
if ( mphud ) {
mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104419" ) );
mphud->HandleNamedEvent( "main_notice" );
}
}
UpdateTeamPowerups();
break;
}
case POWERUP_CTF_STROGGFLAG: {
// shouchard: added notice for picking up the flag
if ( playClientEffects && this == gameLocal.GetLocalPlayer() ) {
if ( mphud ) {
mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104419" ) );
mphud->HandleNamedEvent( "main_notice" );
}
}
UpdateTeamPowerups();
break;
}
case POWERUP_CTF_ONEFLAG: {
// shouchard: added notice for picking up the flag
if ( playClientEffects && this == gameLocal.GetLocalPlayer() ) {
if ( mphud ) {
mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104419" ) );
mphud->HandleNamedEvent( "main_notice" );
}
}
UpdateTeamPowerups();
break;
}
case POWERUP_QUADDAMAGE: {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_QUAD_DAMAGE, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
break;
}
case POWERUP_REGENERATION: {
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
// Have to test for this because buying the team regeneration powerup will cause
// this to get hit multiple times as the server distributes the powerups to the clients.
if ( gameLocal.GetLocalPlayer() == this ) {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_REGENERATION, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
}
break;
}
case POWERUP_HASTE: {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_HASTE, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
break;
}
case POWERUP_INVISIBILITY: {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_INVISIBILITY, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
break;
}
case POWERUP_GUARD: {
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
inventory.maxHealth = 200;
inventory.maxarmor = 200;
break;
}
case POWERUP_SCOUT: {
inventory.armor = 0;
break;
}
case POWERUP_AMMOREGEN: {
if ( team && gameLocal.GetLocalPlayer() == this ) {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TEAM_AMMOREGEN, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
}
break;
}
case POWERUP_TEAM_DAMAGE_MOD: {
if ( gameLocal.GetLocalPlayer() == this ) {
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TEAM_DOUBLER, gameLocal.time, gameLocal.gameType == GAME_TOURNEY ? GetInstance() : -1 );
}
break;
}
//RITUAL BEGIN
case POWERUP_DEADZONE: {
if ( playClientEffects && this == gameLocal.GetLocalPlayer() ) {
if ( mphud ) {
mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_122000" ) ); // Squirrel@Ritual - Localized for 1.2 Patch
mphud->HandleNamedEvent( "main_notice" );
}
}
break;
}
//RITUAL END
}
// only start effects if in our instances and snapshot
if ( playClientEffects && !fl.networkStale ) {
StartPowerUpEffect( powerup );
}
return true;
}
/*
==============
idPlayer::ClearPowerup
==============
*/
void idPlayer::ClearPowerup( int i ) {
if ( gameLocal.isServer ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteShort( i );
msg.WriteBits( 0, 1 );
ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
}
inventory.powerups &= ~( 1 << i );
inventory.powerupEndTime[ i ] = 0;
//if the player doesn't have quad, regen, haste or invisibility remaining on him, remove the power up overlay.
if ( !(
(inventory.powerups & ( 1 << POWERUP_TEAM_DAMAGE_MOD ) ) ||
(inventory.powerups & ( 1 << POWERUP_QUADDAMAGE ) ) ||
(inventory.powerups & ( 1 << POWERUP_REGENERATION ) ) ||
(inventory.powerups & ( 1 << POWERUP_HASTE ) ) ||
(inventory.powerups & ( 1 << POWERUP_INVISIBILITY ) ) ||
(inventory.powerups & ( 1 << POWERUP_DEADZONE ) )
) ) {
powerUpOverlay = NULL;
}
// only quad has a hum, clear it even when other powerups might mean leaving the overlay on
if ( ( inventory.powerups & ( 1 << POWERUP_QUADDAMAGE ) ) == 0 ) {
StopSound( SND_CHANNEL_POWERUP_IDLE, false );
}
StopPowerUpEffect( i );
}
/*
==============
idPlayer::GetArenaPowerupString
==============
*/
const char* idPlayer::GetArenaPowerupString ( void ) {
if ( PowerUpActive( POWERUP_SCOUT ) ) {
return "^isct";
} else if ( PowerUpActive( POWERUP_GUARD ) ) {
return "^igrd";
} else if ( PowerUpActive( POWERUP_DOUBLER ) ) {
return "^idbl";
} else if ( PowerUpActive( POWERUP_AMMOREGEN ) ) {
return "^irgn";
} else {
return "^ixxx";
}
}
/*
==============
idPlayer::UpdatePowerUps
==============
*/
void idPlayer::UpdatePowerUps( void ) {
int i;
int wearoff = -1;
bool playWearoffSound = false;
idPlayer *p = gameLocal.GetLocalPlayer();
if ( p && ( p->spectating && p->spectator == entityNumber || !p->spectating && p->entityNumber == entityNumber ) ) {
playWearoffSound = true;
}
for ( i = 0; i < POWERUP_MAX; i++ ) {
// Do we have this powerup?
if ( !(inventory.powerups & ( 1 << i ) ) ) {
continue;
}
if ( inventory.powerupEndTime[i] > gameLocal.time || inventory.powerupEndTime[i] == -1 ) {
// If there is still time remaining on the powerup then update the hud
if ( playWearoffSound ) {
// Play the wearoff sound for the powerup that is closest to wearing off
if ( ( wearoff == -1 || inventory.powerupEndTime[i] < inventory.powerupEndTime[wearoff] ) && inventory.powerupEndTime[i] != -1 ) {
wearoff = i;
}
}
continue;
} else if ( inventory.powerupEndTime[ i ] != -1 && gameLocal.isServer ) {
// This particular powerup needs to respawn in a special way.
if ( i == POWERUP_DEADZONE ) {
gameLocal.mpGame.GetGameState()->SpawnDeadZonePowerup();
}
// Powerup time has run out so take it away from the player
ClearPowerup( i );
}
}
// PLay wear off sound?
if ( gameLocal.isNewFrame && wearoff != -1 ) {
if ( (inventory.powerupEndTime[wearoff] - gameLocal.time) < POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
if ( (inventory.powerupEndTime[wearoff] - gameLocal.time) / POWERUP_BLINK_TIME != ( inventory.powerupEndTime[wearoff] - gameLocal.previousTime ) / POWERUP_BLINK_TIME ) {
StartSound ( "snd_powerup_wearoff", SND_CHANNEL_POWERUP, 0, false, NULL );
}
}
}
// Reneration regnerates faster when less than maxHealth and can regenerate up to maxHealth * 2
if ( gameLocal.time > nextHealthPulse ) {
// RITUAL BEGIN
// squirrel: health regen only applies if you have positive health
if( health > 0 ) {
if ( PowerUpActive ( POWERUP_REGENERATION ) || PowerUpActive ( POWERUP_GUARD ) ) {
int healthBoundary = inventory.maxHealth; // health will regen faster under this value, slower above
int healthTic = 15;
if( PowerUpActive ( POWERUP_GUARD ) ) {
// guard max health == 200, so set the boundary back to 100
healthBoundary = inventory.maxHealth / 2;
if( PowerUpActive (POWERUP_REGENERATION) ) {
healthTic = 30;
}
}
if ( health < healthBoundary ) {
// only actually give health on the server
if( gameLocal.isServer ) {
health += healthTic;
if ( health > (healthBoundary * 1.1f) ) {
health = healthBoundary * 1.1f;
}
}
StartSound ( "snd_powerup_regen", SND_CHANNEL_POWERUP, 0, false, NULL );
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
} else if ( health < (healthBoundary * 2) ) {
if( gameLocal.isServer ) {
health += healthTic / 3;
if ( health > (healthBoundary * 2) ) {
health = healthBoundary * 2;
}
}
StartSound ( "snd_powerup_regen", SND_CHANNEL_POWERUP, 0, false, NULL );
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
}
// Health above max technically isnt a powerup but functions as one so handle it here
} else if ( health > inventory.maxHealth && gameLocal.isServer ) {
nextHealthPulse = gameLocal.time + HEALTH_PULSE;
health--;
}
}
// RITUAL END
}
// Regenerate ammo
if( gameLocal.isServer && PowerUpActive( POWERUP_AMMOREGEN ) ) {
for( int i = 0; i < MAX_WEAPONS; i++ ) {
if( inventory.weapons & ( 1 << i ) ) {
int ammoIndex = inventory.AmmoIndexForWeaponIndex( i );
int max = inventory.StartingAmmoForWeaponIndex( i );
// only regen ammo if lower than starting
if( gameLocal.time > nextAmmoRegenPulse[ ammoIndex ] && inventory.ammo[ ammoIndex ] < max ) {
int step = inventory.AmmoRegenStepForWeaponIndex( i );
int time = inventory.AmmoRegenTimeForWeaponIndex( i );
if( inventory.ammo[ ammoIndex ] < max ) {
inventory.ammo[ ammoIndex ] += step;
}
if( inventory.ammo[ ammoIndex ] >= max ) {
inventory.ammo[ ammoIndex ] = max;
}
nextAmmoRegenPulse[ ammoIndex ] = gameLocal.time + time;
}
}
}
}
// Tick armor down if greater than max armor
if ( !gameLocal.isClient && gameLocal.time > nextArmorPulse ) {
if ( inventory.armor > inventory.maxarmor ) {
nextArmorPulse += ARMOR_PULSE;
inventory.armor--;
}
}
// Assign the powerup skin as long as we are alive
if ( health > 0 ) {
if ( powerUpSkin ) {
renderEntity.customSkin = powerUpSkin;
if( clientHead ) {
clientHead->SetSkin( powerUpSkin );
}
if( weaponWorldModel ) {
weaponWorldModel->SetSkin( powerUpSkin );
}
if( weaponViewModel ) {
weaponViewModel->SetSkin( powerUpSkin );
}
} else {
renderEntity.customSkin = skin;
if( clientHead ) {
clientHead->SetSkin( headSkin );
}
if( weaponViewModel ) {
weaponViewModel->SetSkin( weaponViewSkin );
}
}
if( weaponViewModel ) {
weaponViewModel->SetOverlayShader( powerUpOverlay );
}
if( clientHead ) {
clientHead->GetRenderEntity()->overlayShader = powerUpOverlay;
}
if( weaponWorldModel ) {
weaponWorldModel->GetRenderEntity()->overlayShader = powerUpOverlay;
}
renderEntity.overlayShader = powerUpOverlay;
} else {
renderEntity.overlayShader = NULL;
powerUpOverlay = NULL;
if( clientHead ) {
clientHead->GetRenderEntity()->overlayShader = NULL;
}
if ( renderEntity.customSkin != gibSkin ) {
if ( influenceSkin ) {
renderEntity.customSkin = influenceSkin;
} else {
renderEntity.customSkin = skin;
}
}
}
// Spawn quad effect
if( PowerUpActive( powerupEffectType ) && powerupEffect && gameLocal.time >= powerupEffectTime ) {
rvClientCrawlEffect* effect = new rvClientCrawlEffect( powerupEffect, this, 100, &powerupEffectJoints );
effect->Play ( gameLocal.time, false );
effect->GetRenderEffect()->suppressSurfaceInViewID = entityNumber+1;
powerupEffectTime = gameLocal.time + 400;
}
// Attenuate haste effect
if ( hasteEffect ) {
hasteEffect->Attenuate( idMath::ClampFloat( 0.0f, 1.0f, physicsObj.GetLinearVelocity().LengthSqr() / Square(100.0f) ) );
}
if ( flagEffect ) {
flagEffect->Attenuate( idMath::ClampFloat( 0.0f, 1.0f, physicsObj.GetLinearVelocity().LengthSqr() / Square(100.0f) ) );
}
if( arenaEffect ) {
arenaEffect->SetOrigin( vec3_zero );
}
}
/*
===============
idPlayer::ClearPowerUps
===============
*/
void idPlayer::ClearPowerUps( void ) {
int i;
for ( i = 0; i < POWERUP_MAX; i++ ) {
if ( PowerUpActive( i ) ) {
ClearPowerup( i );
}
}
inventory.ClearPowerUps();
}
/*
===============
idPlayer::GiveWeaponMods
===============
*/
bool idPlayer::GiveWeaponMods( int mods ) {
inventory.weaponMods[currentWeapon] |= mods;
currentWeapon = -1;
return true;
}
/*
===============
idPlayer::GiveWeaponMods
===============
*/
bool idPlayer::GiveWeaponMods( int weapon, int mods ) {
inventory.weaponMods[weapon] |= mods;
currentWeapon = -1;
return true;
}
/*
==============
idPlayer::GiveWeaponMod
==============
*/
void idPlayer::GiveWeaponMod ( const char* weaponmod ) {
const idDict* modDict;
const idDict* weaponDict;
const char* weaponClass;
int m;
int weaponIndex;
// Grab the weapon mod dictionary
modDict = gameLocal.FindEntityDefDict ( weaponmod, false );
if ( !modDict ) {
gameLocal.Warning ( "Invalid weapon modification def specified '%s'", weaponmod );
return;
}
// Get the weapon it modifies
weaponClass = modDict->GetString ( "weapon" );
weaponDict = gameLocal.FindEntityDefDict ( weaponClass, false );
if ( !weaponDict ) {
gameLocal.Warning ( "Invalid weapon classname '%s' specified on weapon modification '%s'", weaponClass, weaponmod );
return;
}
weaponIndex = SlotForWeapon ( weaponClass );
// Find the index of the weapon mod
for ( m = 0; m < MAX_WEAPONMODS; m ++ ) {
const char* mod;
mod = weaponDict->GetString ( va("def_mod%d",m+1) );
if ( !mod || !*mod ) {
break;
}
if ( !idStr::Icmp ( weaponmod, mod ) ) {
if ( !(inventory.weaponMods[weaponIndex] & (1<<m)) ) {
inventory.weaponMods[weaponIndex] |= (1<<m);
// If the players current weapon is the modified one then we need
// to force the weapon to switch so the mods dont just appear on it
if ( currentWeapon == weaponIndex ) {
currentWeapon = -1;
}
}
return;
}
}
}
/*
===============
idPlayer::GiveInventoryItem
===============
*/
bool idPlayer::GiveInventoryItem( idDict *item ) {
if ( gameLocal.isMultiplayer && spectating ) {
return false;
}
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM(this);
// RAVEN END
inventory.items.Append( new idDict( *item ) );
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_POP_HEAP();
// RAVEN END
if ( hud ) {
const char *itemName = common->GetLocalizedString( item->GetString( "inv_name" ) );
hud->SetStateString ( "itemtext", itemName );
hud->SetStateString ( "itemicon", item->GetString( "inv_icon" ) );
hud->HandleNamedEvent ( "itemPickup" );
}
return true;
}
/*
==============
idPlayer::UpdateObjectiveInfo
==============
*/
void idPlayer::UpdateObjectiveInfo( void ) {
if ( objectiveSystem == NULL ) {
return;
}
objectiveSystem->SetStateString( "objective1", "" );
objectiveSystem->SetStateString( "objective2", "" );
objectiveSystem->SetStateString( "objective3", "" );
// RAVEN BEGIN
// mekberg: swap objective positions to allow for stack-like appearance.
int objectiveCount = inventory.objectiveNames.Num();
for ( int i = 0; i < inventory.objectiveNames.Num(); i++, objectiveCount-- ) {
objectiveSystem->SetStateString( va( "objective%i", objectiveCount ), "1" );
objectiveSystem->SetStateString( va( "objectivetitle%i", objectiveCount ), inventory.objectiveNames[i].title.c_str() );
objectiveSystem->SetStateString( va( "objectivetext%i", objectiveCount), inventory.objectiveNames[i].text.c_str() );
objectiveSystem->SetStateInt( va( "objectiveLength%i", objectiveCount), inventory.objectiveNames[i].text.Length() );
objectiveSystem->SetStateString( va( "objectiveshot%i", objectiveCount), inventory.objectiveNames[i].screenshot.c_str() );
}
objectiveSystem->SetStateBool( "noObjective", !objectiveCount );
// RAVEN END
objectiveSystem->StateChanged( gameLocal.time );
}
/*
===============
idPlayer::GiveObjective
===============
*/
void idPlayer::GiveObjective( const char *title, const char *text, const char *screenshot ) {
idObjectiveInfo info;
// RAVEN BEGIN
info.title = common->GetLocalizedString( title );
info.text = common->GetLocalizedString( text );
// RAVEN END
info.screenshot = screenshot;
inventory.objectiveNames.Append( info );
if ( showNewObjectives ) {
ShowObjective( "newObjective" );
}
if ( objectiveSystem ) {
if ( objectiveSystemOpen ) {
objectiveSystemOpen = false;
ToggleObjectives ( );
#ifdef _XENON
g_ObjectiveSystemOpen = objectiveSystemOpen;
#endif
}
}
}
/*
===============
idPlayer::CompleteObjective
===============
*/
void idPlayer::CompleteObjective( const char *title ) {
// RAVEN BEGIN
title = common->GetLocalizedString( title );
// RAVEN END
int c = inventory.objectiveNames.Num();
for ( int i = 0; i < c; i++ ) {
if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
inventory.objectiveNames.RemoveIndex( i );
break;
}
}
ShowObjective( "newObjectiveComplete" );
if ( objectiveSystem ) {
objectiveSystem->HandleNamedEvent( "newObjectiveComplete" );
}
if ( objectiveSystemOpen ) {
objectiveSystemOpen = false;
ToggleObjectives ( );
#ifdef _XENON
g_ObjectiveSystemOpen = objectiveSystemOpen;
#endif
}
}
/*
===============
idPlayer::FailObjective
===============
*/
void idPlayer::FailObjective ( const char* title ) {
// RAVEN BEGIN
title = common->GetLocalizedString( title );
// mekberg: prevent save games if objective failed.
gameLocal.sessionCommand = "objectiveFailed ";
gameLocal.sessionCommand += title;
// RAVEN END
HideObjective ( );
if ( objectiveSystem ) {
objectiveSystem->HandleNamedEvent( "objectiveFailed" );
}
if( IsInVehicle() ) {
vehicleController.GetVehicle()->EjectAllDrivers();
}
fl.takedamage = true;
pfl.objectiveFailed = true;
#ifdef _XENON
playerView.Fade( colorBlack, MAX_RESPAWN_TIME_XEN_SP );
minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME_XEN_SP;
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME_XEN_SP;
#else
playerView.Fade( colorBlack, 12000 );
minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
#endif
}
/*
===============
idPlayer::FindInventoryItem
===============
*/
idDict *idPlayer::FindInventoryItem( const char *name ) {
for ( int i = 0; i < inventory.items.Num(); i++ ) {
const char *iname = inventory.items[i]->GetString( "inv_name" );
if ( iname && *iname ) {
if ( idStr::Icmp( name, iname ) == 0 ) {
return inventory.items[i];
}
}
}
return NULL;
}
/*
===============
idPlayer::RemoveInventoryItem
===============
*/
void idPlayer::RemoveInventoryItem( const char *name ) {
idDict *item = FindInventoryItem(name);
if ( item ) {
RemoveInventoryItem( item );
}
}
/*
===============
idPlayer::RemoveInventoryItem
===============
*/
void idPlayer::RemoveInventoryItem( idDict *item ) {
inventory.items.Remove( item );
delete item;
}
/*
===============
idPlayer::GiveItem
===============
*/
void idPlayer::GiveItem( const char *itemname ) {
idDict args;
args.Set( "classname", itemname );
args.Set( "owner", name.c_str() );
args.Set( "givenToPlayer", va( "%d", entityNumber ) );
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
// check if this is a weapon
if( !idStr::Icmpn( itemname, "weapon_", 7 ) ) {
int weaponIndex = SlotForWeapon( itemname );
if( weaponIndex >= 0 && weaponIndex < MAX_WEAPONS )
{
int weaponIndexBit = ( 1 << weaponIndex );
inventory.weapons |= weaponIndexBit;
inventory.carryOverWeapons |= weaponIndexBit;
carryOverCurrentWeapon = weaponIndex;
}
}
// if the player is dead, credit him with this armor or ammo purchase
if ( health <= 0 ) {
if( !idStr::Icmp( itemname, "item_armor_small" ) ) {
inventory.carryOverWeapons |= CARRYOVER_FLAG_ARMOR_LIGHT;
} else if( !idStr::Icmp( itemname, "item_armor_large" ) ) {
inventory.carryOverWeapons |= CARRYOVER_FLAG_ARMOR_HEAVY;
} else if( !idStr::Icmp( itemname, "ammorefill" ) ) {
inventory.carryOverWeapons |= CARRYOVER_FLAG_AMMO;
}
} else {
if ( !idStr::Icmp( itemname, "ammorefill" ) ) {
int i;
for ( i = 0 ; i < MAX_AMMOTYPES; i++ ) {
int a = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( rvWeapon::GetAmmoNameForIndex( i ), 0 );
inventory.ammo[i] += a;
if ( inventory.ammo[i] > inventory.MaxAmmoForAmmoClass( this, rvWeapon::GetAmmoNameForIndex(i) ) ) {
inventory.ammo[i] = inventory.MaxAmmoForAmmoClass( this, rvWeapon::GetAmmoNameForIndex(i) );
}
}
}
}
}
// spawn the item if the player is alive
if ( health > 0 && idStr::Icmp( itemname, "ammorefill" ) ) {
gameLocal.SpawnEntityDef( args );
}
}
/*
==================
idPlayer::SlotForWeapon
==================
*/
int idPlayer::SlotForWeapon( const char *weaponName ) {
int i;
for( i = 0; i < MAX_WEAPONS; i++ ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( !idStr::Cmp( weap, weaponName ) ) {
return i;
}
}
// not found
return -1;
}
/*
===============
idPlayer::Reload
===============
*/
void idPlayer::Reload( void ) {
if ( gameLocal.isClient || spectating || gameLocal.inCinematic || influenceActive || !weapon ) {
return;
}
weapon->Reload();
}
#ifdef _XENON
/*
===============
idPlayer::ScheduleWeaponSwitch
===============
*/
void idPlayer::ScheduleWeaponSwitch(int weapon)
{
CancelEvents(&EV_Player_SelectWeapon);
hud->SetStateInt("player_selectedWeapon", weapon-1);
hud->HandleNamedEvent( "weaponSelect" );
// nrausch: support for turning the weapon change ui on and off
idWindow *win = FindWindowByName( "p_weapswitch", hud->GetDesktop() );
if ( win ) {
win->SetVisible( false );
}
if ( weapon > 0 ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", weapon-1 ) );
PostEventSec(&EV_Player_SelectWeapon, 0.25f, weap);
}
}
#endif
/*
===============
idPlayer::ShowCrosshair
===============
*/
void idPlayer::ShowCrosshair( void ) {
if ( !weaponEnabled ) {
return;
}
if ( cursor ) {
cursor->HandleNamedEvent( "showCrossCombat" );
}
UpdateHudWeapon();
}
/*
===============
idPlayer::HideCrosshair
===============
*/
void idPlayer::HideCrosshair( void ) {
if ( cursor ) {
cursor->HandleNamedEvent( "crossHide" );
}
}
/*
===============
idPlayer::LastWeapon
===============
*/
void idPlayer::LastWeapon( void ) {
// Dont bother if previousWeapon is invalid or the player is spectating
if ( spectating || previousWeapon < 0 ) {
return;
}
// Do we have the weapon still?
if ( !(inventory.weapons & ( 1 << previousWeapon ) ) ) {
return;
}
idealWeapon = previousWeapon;
}
/*
===============
idPlayer::NextBestWeapon
===============
*/
void idPlayer::NextBestWeapon( void ) {
const char *weap;
int w = MAX_WEAPONS;
if ( gameLocal.isClient || !weaponEnabled ) {
return;
}
while ( w > 0 ) {
w--;
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap ) ) ) {
continue;
}
if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
continue;
}
break;
}
idealWeapon = w;
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
UpdateHudWeapon();
}
/*
===============
idPlayer::NextWeapon
===============
*/
void idPlayer::NextWeapon( void ) {
const char *weap;
int w;
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
return;
}
// check if we have any weapons
if ( !inventory.weapons ) {
return;
}
if ( gameLocal.isClient ) {
if ( !net_clientPredictWeaponSwitch.GetBool() ) {
return;
}
if ( entityNumber != gameLocal.localClientNum ) {
return;
}
} else {
// if we are processing a GAME_RELIABLE_MESSAGE_EVENT, then the impulse has been predicted on the client,
// who now expects a ACK back to know when it's ok to read snapshot data again
// if the weapon change below fails (weapon not present), we still send back a ACK just in case the client decided it had a weapon
CheckAckReply();
}
w = idealWeapon;
while ( 1 ) {
w++;
if ( w >= MAX_WEAPONS ) {
w = 0;
}
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
continue;
}
if ( !weap[ 0 ] ) {
continue;
}
if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
continue;
}
if ( inventory.HasAmmo( weap ) ) {
break;
}
}
if ( w != idealWeapon ) {
if ( gameLocal.isClient && entityNumber == gameLocal.localClientNum ) {
// only if we are actually predicting a weapon change because if the server doesn't reply
// we'll be stuck with a bad weapon
clientIdealWeaponPredictFrame = gameLocal.framenum;
}
idealWeapon = w;
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
UpdateHudWeapon();
}
}
/*
===============
idPlayer::PrevWeapon
===============
*/
void idPlayer::PrevWeapon( void ) {
const char *weap;
int w;
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
return;
}
// check if we have any weapons
if ( !inventory.weapons ) {
return;
}
if ( gameLocal.isClient ) {
if ( !net_clientPredictWeaponSwitch.GetBool() ) {
return;
}
if ( entityNumber != gameLocal.localClientNum ) {
return;
}
} else {
// if we are processing a GAME_RELIABLE_MESSAGE_EVENT, then the impulse has been predicted on the client,
// who now expects a ACK back to know when it's ok to read snapshot data again
CheckAckReply();
}
w = idealWeapon;
while( 1 ) {
w--;
if ( w < 0 ) {
w = MAX_WEAPONS - 1;
}
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
continue;
}
if ( !weap[ 0 ] ) {
continue;
}
if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
continue;
}
if ( inventory.HasAmmo( weap ) ) {
break;
}
}
if ( w != idealWeapon ) {
if ( gameLocal.isClient && entityNumber == gameLocal.localClientNum ) {
// only if we are actually predicting a weapon change because if the server doesn't reply
// we'll be stuck with a bad weapon
clientIdealWeaponPredictFrame = gameLocal.framenum;
}
idealWeapon = w;
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
UpdateHudWeapon();
}
}
/*
===============
idPlayer::SelectWeapon
===============
*/
void idPlayer::SelectWeapon( const char *weapon_name ) {
Event_SelectWeapon( weapon_name );
}
/*
===============
idPlayer::SelectWeapon
===============
*/
void idPlayer::SelectWeapon( int num, bool force ) {
const char *weap;
if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
return;
}
if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
return;
}
if ( gameLocal.isClient ) {
if ( !net_clientPredictWeaponSwitch.GetBool() ) {
return;
}
if ( entityNumber != gameLocal.localClientNum ) {
return;
}
} else {
// if we are processing a GAME_RELIABLE_MESSAGE_EVENT, then the impulse has been predicted on the client,
// who now expects a ACK back to know when it's ok to read snapshot data again
// if the weapon change below fails (weapon not present), we still send back a ACK just in case the client decided it had a weapon
CheckAckReply();
}
weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
if ( !weap[ 0 ] ) {
gameLocal.Warning( "Invalid weapon def_weapon%d\n", num );
return;
}
// cycle in-between weapons
// if a weapon_def has a "def_weapon_swap" keyvalue pointing to another
// weapon, hitting that impulse twice will cycle to the target swap.
if ( num == currentWeapon ) {
const idDict* weapDict = gameLocal.FindEntityDefDict( weap, false );
if ( weapDict == NULL ) {
gameLocal.Warning( "Invalid weapon entity %s\n", weap );
return;
}
const char* destWeapon = weapDict->GetString( "def_weapon_swap", NULL );
if ( destWeapon != NULL ) {
int swapNum = SlotForWeapon( destWeapon );
if( swapNum == -1 ) {
gameLocal.Warning( "Swap weapon for %s (%s) is invalid", weap, destWeapon );
} else {
num = swapNum;
}
}
}
if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
return;
}
if ( gameLocal.isClient && entityNumber == gameLocal.localClientNum ) {
// only if we are actually predicting a weapon change because if the server doesn't reply
// we'll be stuck with a bad weapon
clientIdealWeaponPredictFrame = gameLocal.framenum;
}
if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
assert( false ); // I'm assuming this does not happen in MP
weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
return;
}
idealWeapon = previousWeapon;
} else {
idealWeapon = num;
}
UpdateHudWeapon();
}
}
/*
=================
idPlayer::DropItem
=================
*/
idEntity* idPlayer::DropItem( const char* itemClass, const idDict& customArgs, const idVec3& velocity ) const {
idDict args;
idEntity* ent;
args.Set( "classname", itemClass );
args.Set( "origin", GetPhysics()->GetAbsBounds().GetCenter().ToString ( ) );
args.Set( "dropped", "1" );
args.SetFloat ( "angle", 360.0f * gameLocal.random.RandomFloat ( ) );
args.Copy ( customArgs );
gameLocal.SpawnEntityDef ( args, &ent );
if ( !ent ) {
return NULL;
}
// If a velocity was given then just use that, otherwise randomly throw it around
if ( velocity != vec3_origin ) {
ent->GetPhysics()->SetLinearVelocity ( velocity );
} else {
idVec3 vel;
float ang;
ang = idMath::TWO_PI * gameLocal.random.RandomFloat();
vel[0] = PLAYER_ITEM_DROP_SPEED * idMath::Cos ( ang );
vel[1] = PLAYER_ITEM_DROP_SPEED * idMath::Sin ( ang );
vel[2] = PLAYER_ITEM_DROP_SPEED * 2;
ent->GetPhysics()->SetLinearVelocity ( vel );
}
return ent;
}
/*
=================
idPlayer::DropPowerups
=================
*/
void idPlayer::DropPowerups( void ) {
int i;
idEntity* item;
assert( !gameLocal.isClient );
for ( i = 0; i < POWERUP_MAX; i++ ) {
if ( !(inventory.powerups & ( 1 << i )) ) {
continue;
}
// These powerups aren't dropped
if ( i >= POWERUP_TEAM_AMMO_REGEN && i <= POWERUP_TEAM_DAMAGE_MOD )
continue;
// Don't drop this either with buying enabled.
if ( i == POWERUP_REGENERATION && gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() )
continue;
/// Don't drop arena rune powerups in non-Arena modes
if( gameLocal.gameType != GAME_ARENA_CTF )
{
if( i == POWERUP_AMMOREGEN ||
i == POWERUP_GUARD ||
i == POWERUP_DOUBLER ||
i == POWERUP_SCOUT )
{
continue;
}
}
const idDeclEntityDef* def;
def = GetPowerupDef ( i );
if ( !def ) {
continue;
}
if( def->dict.GetBool( "nodrop" ) ) {
continue;
}
idDict args;
args.SetFloat ( "time", inventory.powerupEndTime[i] == -1 ? -1 : MS2SEC(inventory.powerupEndTime[i]-gameLocal.time) );
args.SetInt( "instance", GetInstance() );
item = DropItem ( def->dict.GetString ( "classname" ), args );
if ( !item ) {
gameLocal.Warning ( "Player %d failed to drop powerup '%s'", entityNumber, def->dict.GetString ( "classname" ) );
return;
}
}
}
/*
=================
idPlayer::ResetFlag
=================
*/
idEntity* idPlayer::ResetFlag ( const char* itemClass, const idDict& customArgs ) const {
idDict args;
idEntity* ent;
args.Set( "classname", itemClass );
args.Set( "origin", GetPhysics()->GetAbsBounds().GetCenter().ToString ( ) );
args.Set( "dropped", "1" );
args.Set( "reset", "1" );
args.Copy ( customArgs );
gameLocal.SpawnEntityDef ( args, &ent );
if ( !ent ) {
return NULL;
}
return ent;
}
/*
=================
idPlayer::RespawnFlags
=================
*/
void idPlayer::RespawnFlags ( void ) {
int i;
idEntity* item;
assert( !gameLocal.isClient );
for ( i = POWERUP_CTF_MARINEFLAG; i < POWERUP_CTF_ONEFLAG; i++ ) {
if ( !(inventory.powerups & ( 1 << i )) ) {
continue;
}
const idDeclEntityDef* def;
def = GetPowerupDef ( i );
if ( !def ) {
continue;
}
idDict args;
args.SetFloat ( "time", inventory.powerupEndTime[i] == -1 ? -1 : MS2SEC(inventory.powerupEndTime[i]-gameLocal.time) );
item = ResetFlag ( def->dict.GetString ( "classname" ), args );
if ( !item ) {
gameLocal.Warning ( "Player %d failed to drop powerup '%s'", entityNumber, def->dict.GetString ( "classname" ) );
return;
}
}
}
/*
=================
DropWeapon
=================
*/
void idPlayer::DropWeapon( void ) {
idEntity* item;
idDict args;
const char* itemClass;
assert( !gameLocal.isClient );
if( !gameLocal.isMultiplayer ) {
return;
}
// RITUAL BEGIN
// squirrel: don't drop weapons in Buying modes unless "always drop" is on
if( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() && !gameLocal.serverInfo.GetBool( "si_dropWeaponsInBuyingModes" ) ) {
return;
}
// RITUAL END
if ( spectating || weaponGone || !weapon ) {
return;
}
// Make sure the weapon is droppable
itemClass = weapon->spawnArgs.GetString ( "def_dropItem" );
if ( !itemClass || !*itemClass ) {
return;
}
// If still alive then the weapon is being thrown so start it a bit in front of the player
// copy over the instance
args.SetInt( "instance", GetInstance() );
if ( health > 0 ) {
idVec3 forward;
idVec3 up;
viewAngles.ToVectors( &forward, NULL, &up );
args.SetBool( "triggerFirst", true );
item = DropItem ( itemClass, args, 250.0f*forward + 150.0f*up );
} else {
item = DropItem ( itemClass, args );
}
// Drop the weapon
if ( !item ) {
gameLocal.Warning ( "Player %d failed to drop weapon '%s'", entityNumber, weapon->spawnArgs.GetString ( "def_dropItem" ) );
return;
}
// Since this weapon was dropped, replace any starting ammo values with real ammo values
const idKeyValue* keyval = item->spawnArgs.MatchPrefix( "inv_start_ammo_" );
idDict newArgs;
while( keyval ) {
newArgs.Set( va( "inv_ammo_%s", keyval->GetKey().Right( keyval->GetKey().Length() - 15 ).c_str() ), keyval->GetValue().c_str() );
item->spawnArgs.Set( keyval->GetKey(), "" );
keyval = item->spawnArgs.MatchPrefix( "inv_start_ammo_", keyval );
}
item->spawnArgs.SetDefaults( &newArgs );
// Set the appropriate mods on the dropped item
int i;
int mods;
idStr out;
mods = weapon->GetMods ( );
for ( i = 0; i < MAX_WEAPONMODS; i ++ ) {
if ( mods & (1<<i) ) {
if ( out.Length() ) {
out += ",";
}
out += weapon->spawnArgs.GetString ( va("def_mod%d", i+1) );
}
}
if ( out.Length() ) {
item->spawnArgs.Set ( "inv_weaponmod", out );
}
// Make sure the weapon removes itself over time.
item->PostEventMS ( &EV_Remove, WEAPON_DROP_TIME );
// Delay aquire since the weapon is being thrown
if ( health > 0 ) {
item->PostEventMS ( &EV_Activate, 500, item );
inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
NextWeapon();
}
}
/*
===============
idPlayer::ActiveGui
===============
*/
idUserInterface *idPlayer::ActiveGui( void ) {
#ifdef _XENON
if ( objectiveSystemOpen ) {
return 0;
}
#endif
return focusUI;
}
/*
===============
idPlayer::Weapon_Combat
===============
*/
void idPlayer::Weapon_Combat( void ) {
if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
return;
}
if ( weapon ) {
weapon->RaiseWeapon();
if ( weapon->IsReloading() ) {
if ( !pfl.reload ) {
pfl.reload = true;
SetAnimState ( ANIMCHANNEL_TORSO, "Torso_Reload", 4 );
UpdateState();
}
} else {
pfl.reload = false;
}
}
if ( idealWeapon != currentWeapon ) {
if ( !weapon || weaponCatchup ) {
assert( gameLocal.isClient );
weaponGone = false;
SetWeapon( idealWeapon );
weapon->NetCatchup();
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
UpdateState();
} else {
if ( weapon->IsReady() || weapon->IsReloading() ) {
weapon->PutAway();
}
if ( weapon->IsHolstered() && weaponViewModel ) {
assert( idealWeapon >= 0 );
assert( idealWeapon < MAX_WEAPONS );
SetWeapon( idealWeapon );
weapon->Raise();
}
}
} else {
weaponGone = false;
if ( weapon->IsHolstered() ) {
if ( !weapon->AmmoAvailable() ) {
// weapons can switch automatically if they have no more ammo
NextBestWeapon();
} else {
weapon->Raise();
SetAnimState ( ANIMCHANNEL_TORSO, "Torso_RaiseWeapon", 3 );
}
}
}
weaponCatchup = false;
// check for attack
pfl.weaponFired = false;
if ( !influenceActive ) {
if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
FireWeapon();
} else if ( oldButtons & BUTTON_ATTACK ) {
pfl.attackHeld = false;
weapon->EndAttack();
}
}
if ( gameLocal.isMultiplayer && spectating ) {
UpdateHudWeapon();
}
// update our ammo clip in our inventory
if ( gameLocal.GetLocalPlayer() == this && ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
inventory.clip[ currentWeapon ] = weapon->AmmoInClip();
if ( hud && ( currentWeapon == idealWeapon ) ) {
UpdateHudAmmo( hud );
}
}
}
/*
===============
idPlayer::Weapon_Vehicle
===============
*/
void idPlayer::Weapon_Vehicle( void ) {
StopFiring();
weapon->LowerWeapon();
if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
ProcessEvent ( &AI_EnterVehicle, focusEnt.GetEntity() );
ClearFocus ( );
}
}
/*
===============
idPlayer::Weapon_Usable
===============
*/
void idPlayer::Weapon_Usable( void ) {
StopFiring();
weapon->LowerWeapon();
if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
focusEnt->ProcessEvent ( &EV_Activate, this );
ClearFocus ( );
}
}
/*
===============
idPlayer::Weapon_NPC
===============
*/
void idPlayer::Weapon_NPC( void ) {
flagCanFire = false;
if ( idealWeapon != currentWeapon ) {
Weapon_Combat();
}
if ( currentWeapon ) {
StopFiring();
}
if ( !focusEnt || focusEnt->health <= 0 ) {
ClearFocus ( );
return;
}
if ( talkCursor && ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
buttonMask |= BUTTON_ATTACK;
if ( !talkingNPC ) {
idAI *focusAI = static_cast<idAI*>(focusEnt.GetEntity());
if ( focusAI ) {
focusAI->TalkTo( this );
talkingNPC = focusAI;
}
}
} else if ( currentWeapon == SlotForWeapon ( "weapon_blaster" ) ) {
Weapon_Combat();
}
}
/*
===============
idPlayer::LowerWeapon
===============
*/
void idPlayer::LowerWeapon( void ) {
if ( weapon && !weapon->IsHidden() ) {
weapon->LowerWeapon();
}
}
/*
===============
idPlayer::RaiseWeapon
===============
*/
void idPlayer::RaiseWeapon( void ) {
if ( weapon && weapon->IsHidden() ) {
weapon->RaiseWeapon();
}
}
/*
===============
idPlayer::WeaponLoweringCallback
===============
*/
void idPlayer::WeaponLoweringCallback( void ) {
SetAnimState ( ANIMCHANNEL_TORSO, "Torso_LowerWeapon", 3 );
UpdateState();
}
/*
===============
idPlayer::WeaponRisingCallback
===============
*/
void idPlayer::WeaponRisingCallback( void ) {
SetAnimState ( ANIMCHANNEL_TORSO, "Torso_RaiseWeapon", 2 );
UpdateState();
}
/*
===============
idPlayer::Weapon_GUI
===============
*/
void idPlayer::Weapon_GUI( void ) {
flagCanFire = false;
if ( !objectiveSystemOpen ) {
if ( idealWeapon != currentWeapon ) {
Weapon_Combat();
}
StopFiring();
weapon->LowerWeapon();
}
// disable click prediction for the GUIs. handy to check the state sync does the right thing
if ( gameLocal.isClient && !net_clientPredictGUI.GetBool() ) {
return;
}
if ( ( oldButtons ^ usercmd.buttons ) & BUTTON_ATTACK ) {
sysEvent_t ev;
const char *command = NULL;
bool updateVisuals = false;
idUserInterface *ui = ActiveGui();
if ( ui ) {
ev = sys->GenerateMouseButtonEvent( 1, ( usercmd.buttons & BUTTON_ATTACK ) != 0 );
command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
if ( updateVisuals && focusEnt && ui == focusUI ) {
focusEnt->UpdateVisuals();
}
}
if ( gameLocal.isClient ) {
// we predict enough, but don't want to execute commands
return;
}
if ( focusEnt ) {
HandleGuiCommands( focusEnt, command );
} else {
HandleGuiCommands( this, command );
}
}
}
/*
===============
idPlayer::UpdateWeapon
===============
*/
void idPlayer::UpdateWeapon( void ) {
if ( health <= 0 ) {
return;
}
assert( !spectating );
// clients need to wait till the weapon and it's world model entity
// are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
if ( gameLocal.isClient && (!weaponViewModel || !weaponWorldModel) ) {
return;
}
// always make sure the weapon is correctly setup before accessing it
if ( !weapon ) {
if ( idealWeapon != -1 ) {
SetWeapon( idealWeapon );
weaponCatchup = false;
assert( weapon );
} else {
return;
}
}
if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
HideTip();
}
// Make sure the weapon is in a settled state before preventing thinking due
// to drag entity. This way things like hitting reload, zoom, etc, wont crash
if ( g_dragEntity.GetInteger() ) {
StopFiring();
flagCanFire = false;
if ( weapon ) {
weapon->LowerWeapon();
}
dragEntity.Update( this );
return;
} else if ( focusType == FOCUS_CHARACTER) {
flagCanFire = false;
Weapon_NPC();
} else if ( focusType == FOCUS_VEHICLE ) {
flagCanFire = false;
Weapon_Vehicle();
} else if ( focusType == FOCUS_USABLE || focusType == FOCUS_USABLE_VEHICLE ) {
flagCanFire = false;
Weapon_Usable();
} else if ( ActiveGui() ) {
flagCanFire = false;
Weapon_GUI();
} else if ( !hiddenWeapon ) { /* no pda yet || ( ( weapon_pda >= 0 ) && ( idealWeapon == weapon_pda ) ) ) { */
flagCanFire = true;
Weapon_Combat();
}
// Range finder for debugging
if ( g_showRange.GetBool ( ) ) {
idVec3 start;
idVec3 end;
trace_t tr;
start = GetEyePosition();
end = start + viewAngles.ToForward() * 50000.0f;
gameLocal.TracePoint( this, tr, start, end, MASK_SHOT_BOUNDINGBOX, this );
idVec3 forward;
idVec3 right;
idVec3 up;
viewAngles.ToVectors ( &forward, &right, &up );
gameRenderWorld->DrawText( va( "%d qu", ( int )( tr.endpos - start ).Length() ), start + forward * 100.0f + right * 25.0f, .2f, colorCyan, viewAxis );
gameRenderWorld->DrawText( va( "%d m", ( int )( tr.endpos - start ).Length() ), start + forward * 100.0f + right * 25.0f - up * 6.0f, .2f, colorCyan, viewAxis );
gameRenderWorld->DrawText( va( "%d 2d", ( int )DistanceTo2d( tr.endpos ) ), start + forward * 100.0f + right * 25.0f - up * 12.0f, .2f, colorCyan, viewAxis );
}
if ( weapon ) {
if ( hiddenWeapon ) {
weapon->LowerWeapon();
}
// update weapon state, particles, dlights, etc
weaponViewModel->PresentWeapon( showWeaponViewModel );
}
}
/*
===============
idPlayer::SpectateFreeFly
===============
*/
void idPlayer::SpectateFreeFly( bool force ) {
idPlayer *player;
idVec3 newOrig;
idVec3 spawn_origin;
idAngles spawn_angles;
player = gameLocal.GetClientByNum( spectator );
if ( force || gameLocal.time > lastSpectateChange ) {
spectator = entityNumber;
if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
newOrig = player->GetPhysics()->GetOrigin();
if ( player->physicsObj.IsCrouching() ) {
newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
} else {
newOrig[ 2 ] += pm_normalviewheight.GetFloat();
}
newOrig[ 2 ] += SPECTATE_RAISE;
idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
idVec3 start = player->GetPhysics()->GetOrigin();
start[2] += pm_spectatebbox.GetFloat() * 0.5f;
trace_t t;
// assuming spectate bbox is inside stand or crouch box
// RAVEN BEGIN
// ddynerman: multiple clip worlds
gameLocal.TraceBounds( player, t, start, newOrig, b, MASK_PLAYERSOLID, player );
// RAVEN END
newOrig.Lerp( start, newOrig, t.fraction );
SetOrigin( newOrig );
idAngles angle = player->viewAngles;
angle[ 2 ] = 0;
SetViewAngles( angle );
} else {
if( !SelectSpawnPoint( spawn_origin, spawn_angles ) ) {
return;
}
spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
spawn_origin[ 2 ] += SPECTATE_RAISE;
SetOrigin( spawn_origin );
SetViewAngles( spawn_angles );
}
lastSpectateChange = gameLocal.time + 500;
}
}
/*
===============
idPlayer::SpectateCycle
===============
*/
void idPlayer::SpectateCycle( void ) {
idPlayer *player;
if ( gameLocal.time > lastSpectateChange ) {
spectator = gameLocal.GetNextClientNum( spectator );
player = gameLocal.GetClientByNum( spectator );
if ( !player ) {
SpectateFreeFly( true );
return;
}
// ignore other spectators
int latchedSpectator = spectator;
while ( player->spectating ) {
spectator = gameLocal.GetNextClientNum( spectator );
player = gameLocal.GetClientByNum( spectator );
if ( spectator == latchedSpectator ) {
break;
}
}
lastSpectateChange = gameLocal.time + 500;
if ( !player || player->spectating ) {
SpectateFreeFly( true );
return;
}
if ( player ) {
player->UpdateHudWeapon( player->currentWeapon );
}
}
}
/*
===============
idPlayer::UpdateSpectating
===============
*/
void idPlayer::UpdateSpectating( void ) {
assert( spectating );
assert( !gameLocal.isClient || IsFakeClient() );
assert( IsHidden() );
idPlayer *player;
if ( !gameLocal.isMultiplayer ) {
return;
}
player = gameLocal.GetClientByNum( spectator );
if ( !player || ( player->spectating && player != this ) ) {
SpectateFreeFly( true );
} else if ( usercmd.upmove > 0 && player && player != this ) {
// following someone and hit jump? release.
SpectateFreeFly( false );
} else if ( usercmd.buttons & BUTTON_ATTACK && gameLocal.gameType != GAME_TOURNEY ) {
// tourney mode uses seperate cycling
SpectateCycle();
}
}
/*
===============
idPlayer::HandleSingleGuiCommand
===============
*/
bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
idToken token;
if ( !src->ReadToken( &token ) ) {
return false;
}
if ( token == ";" ) {
return false;
}
if ( token.Icmp( "addhealth" ) == 0 ) {
if ( entityGui && health < 100 ) {
int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
_health -= amt;
entityGui->spawnArgs.SetInt( "gui_parm1", _health );
if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
}
health += amt;
if ( health > 100 ) {
health = 100;
}
}
return true;
}
if ( token.Icmp( "ready" ) == 0 ) {
PerformImpulse( IMPULSE_17 );
return true;
}
if ( token.Icmp( "heal" ) == 0 &&
entityGui->IsType( rvHealingStation::GetClassType() ) &&
src->ReadToken( &token ) )
{
rvHealingStation * station = static_cast< rvHealingStation * >( entityGui );
if ( token.Icmp( "begin" ) == 0 ) {
station->BeginHealing( this );
} else if ( token.Icmp( "end" ) == 0 ) {
station->EndHealing( );
} else {
return false;
}
return true;
}
src->UnreadToken( &token );
return false;
}
/*
==============
idPlayer::Collide
==============
*/
bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
idEntity *other;
other = gameLocal.entities[ collision.c.entityNum ];
// allow client-side prediction of item collisions for simple client effects
if ( gameLocal.isClient && !other->IsType( idItem::GetClassType() ) ) {
return false;
}
if ( other ) {
other->Signal( SIG_TOUCH );
if ( !spectating ) {
if ( other->RespondsTo( EV_Touch ) ) {
other->ProcessEvent( &EV_Touch, this, &collision );
}
} else {
if ( other->RespondsTo( EV_SpectatorTouch ) ) {
other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
}
}
}
return false;
}
/*
================
idPlayer::UpdateLocation
Searches nearby locations
================
*/
void idPlayer::UpdateLocation( void ) {
if ( hud ) {
idLocationEntity *locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
if ( locationEntity ) {
hud->SetStateString( "location", locationEntity->GetLocation() );
} else {
// RAVEN BEGIN
// rjohnson: temp fix until id corrects slow downs created from constant string lookup
// hud->SetStateString( "location", common->GetLocalizedString( "#str_102911" ) );
hud->SetStateString( "location", "Unidentified" );
// RAVEN END
}
}
}
/*
================
idPlayer::UpdateFocus
Searches nearby entities for interactive guis, possibly making one of them
the focus and sending it a mouse move event
================
*/
void idPlayer::UpdateFocus( void ) {
// These only need to be updated at the last tic
if ( !gameLocal.isLastPredictFrame ) {
return;
}
idClipModel* clipModelList[ MAX_GENTITIES ];
idClipModel* clip;
int listedClipModels;
idEntity* ent;
idUserInterface* oldBrackets;
int oldTalkCursor;
int i;
int j;
idVec3 start;
idVec3 end;
trace_t renderTrace, bboxTrace, allTrace;
guiPoint_t pt;
// RAVEN BEGIN
// mekberg: removed check to see if attack was held.
// RAVEN END
// No focus during cinimatics
if ( gameLocal.inCinematic ) {
return;
}
// Focus has a limited time, make sure it hasnt expired
if ( focusTime && gameLocal.time > focusTime ) {
ClearFocus ( );
}
if ( spectating ) {
return;
}
if ( g_perfTest_noPlayerFocus.GetBool() ) {
return;
}
#ifndef _XENON
cvarSystem->SetCVarInteger( "pm_isZoomed", zoomed ? pm_zoomedSlow.GetInteger() : 0 );
#endif
#ifdef _XENON
if ( cursor ) {
if ( weapon ) {
cursor->SetStateInt( "autoaim", weapon->AllowAutoAim() ? 1 : 0 );
} else {
cursor->SetStateInt( "autoaim", 0 );
}
}
#endif
// Kill the focus brackets when their time has elapsed
oldBrackets = focusBrackets;
if ( focusBracketsTime && gameLocal.time > focusBracketsTime ) {
focusBrackets = NULL;
}
oldTalkCursor = talkCursor;
talkCursor = 0;
start = GetEyePosition();
end = start + viewAngles.ToForward() * 768.0f;
// player identification -> names to the hud
if ( gameLocal.isMultiplayer && IsLocalClient() ) {
trace_t trace;
idVec3 end = start + viewAngles.ToForward() * 768.0f;
gameLocal.TracePoint( this, trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
// no aim text if player is invisible
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) && ( !((idPlayer*)gameLocal.entities[ trace.c.entityNum ])->PowerUpActive( POWERUP_INVISIBILITY ) ) ) {
char* teammateHealth = "";
idPlayer* p = static_cast<idPlayer*>(gameLocal.entities[ trace.c.entityNum ]);
if( trace.c.entityNum != aimClientNum ) {
if( mphud ) {
mphud->SetStateString( "aim_text", va( "%s\n%s", gameLocal.userInfo[ trace.c.entityNum ].GetString( "ui_name" ), gameLocal.userInfo[ trace.c.entityNum ].GetString( "ui_clan" ) ) );
if( gameLocal.IsTeamGame() ) {
mphud->SetStateInt( "aim_player_team", p->team );
if( p->team == team ) {
teammateHealth = va( "^iteh %d / ^itea %d", p->health, p->inventory.armor );
}
// when looking at a friendly, color the crosshair
if( cursor ) {
if( p->team == team ) {
cursor->HandleNamedEvent( "targetFriendly" );
} else {
cursor->HandleNamedEvent( "clearTarget" );
}
}
}
mphud->SetStateString( "aim_teammate_health", teammateHealth );
mphud->HandleNamedEvent( "aim_text" );
aimClientNum = trace.c.entityNum;
}
} else {
// update health
if( gameLocal.IsTeamGame() && p->team == team ) {
teammateHealth = va( "^iteh %d / ^itea %d", p->health, p->inventory.armor );
}
mphud->SetStateString( "aim_teammate_health", teammateHealth );
}
} else {
if( mphud && aimClientNum != -1 ) {
mphud->HandleNamedEvent( "aim_fade" );
aimClientNum = -1;
}
if( cursor ) {
cursor->HandleNamedEvent( "clearTarget" );
}
}
}
#ifdef _XENON
bboxTrace.fraction = -1;
bestEnemy = NULL;
if ( gameLocal.isMultiplayer ) {
if ( !weapon || !weapon->AllowAutoAim() ) {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 0);
}
return;
}
idEntity *ent = NULL;
idActor *actor = NULL;
bool inside;
bool showDebug = cvarSystem->GetCVarBool("pm_showAimAssist");
float bDist = cvarSystem->GetCVarInteger("pm_AimAssistDistance");
float dist;
idFrustum aimArea;
float dNear, dFar, size;
renderView_t *rv = GetRenderView();
float fovY, fovX;
// field of view
gameLocal.CalcFov( CalcFov( true ), fovX, fovY );
dNear = cvarSystem->GetCVarFloat( "r_znear" );
dFar = cvarSystem->GetCVarInteger("pm_AimAssistDistance");
#ifndef _FINAL
if ( cvarSystem->GetCVarInteger("pm_AimAssistTest") != 0 ) {
size = dFar * idMath::Tan( DEG2RAD( fovY * 0.5f ) ) * pm_AimAssistFOV.GetFloat()/100.0;
} else
#endif
{
size = dFar * idMath::Tan( DEG2RAD( fovY * 0.5f ) ) * weapon->GetAutoAimFOV()/100.0;
}
aimArea.SetOrigin( GetEyePosition() );
aimArea.SetAxis( viewAngles.ToMat3() );
aimArea.SetSize( dNear, dFar, size, size );
if ( showDebug ) {
gameRenderWorld->DebugFrustum(colorRed, aimArea, false, 20);
}
idLinkList<idEntity> *entities = &gameLocal.snapshotEntities;
if ( gameLocal.isServer ) {
entities = &gameLocal.spawnedEntities;
}
ent = entities->Next();
while ( ent != NULL ) {
bool isOk = true;
if ( !ent->IsType(idPlayer::GetClassType()) ) {
isOk = false;
} else if ( gameLocal.IsTeamGame() && ((idPlayer*)ent)->team == team ) {
isOk = false;
} else if ( ((idPlayer*)ent)->instance != instance ) {
isOk = false;
} else if ( ((idPlayer*)ent)->spectating ) {
isOk = false;
} else if ( !idStr::Icmp(name.c_str(),ent->name.c_str()) ) {
isOk = false;
} else if ( ent->health <= 0 ) {
isOk = false;
}
if ( !isOk ) {
if ( gameLocal.isServer ) {
ent = ent->spawnNode.Next();
} else {
ent = ent->snapshotNode.Next();
}
continue;
}
const idBounds &bounds = ent->GetPhysics()->GetAbsBounds();
if ( showDebug ) {
gameRenderWorld->DebugBounds(colorGreen, bounds, vec3_origin, 20);
}
inside = aimArea.IntersectsBounds(bounds);
if ( inside ) {
dist = bounds.ShortestDistance(GetEyePosition());
if ( bDist > dist ) {
if ( bboxTrace.fraction == -1 ) {
gameLocal.TracePoint( this, bboxTrace, start, end, MASK_SHOT_BOUNDINGBOX, this );
}
if ( ( bboxTrace.fraction < 1.0f ) && ( bboxTrace.c.entityNum != ent->entityNumber ) ) {
idVec3 v = end - start;
if ( ((v.Length() * bboxTrace.fraction) + 5.0f) >= dist ) {
// close enough to the bbox
bDist = dist;
bestEnemy = (idActor *)ent;
}
} else {
// Didn't hit anything, so this must be in view
bDist = dist;
bestEnemy = (idActor *)ent;
}
}
}
if ( gameLocal.isServer ) {
ent = ent->spawnNode.Next();
} else {
ent = ent->snapshotNode.Next();
}
}
if ( !gameLocal.isMultiplayer || (gameLocal.isMultiplayer && IsLocalClient() ) ) {
if ( bestEnemy ) {
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 1);
idVec3 altEnd = bestEnemy->GetPhysics()->GetOrigin();
altEnd[2]+=35.0f; // origin is always below the monster's feet for whatever reason, so aim at this
trace_t trace;
gameLocal.TracePoint( this, trace, start, altEnd, MASK_SHOT_BOUNDINGBOX, this );
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == bestEnemy->entityNumber ) ) {
int slow = pm_AimAssistSlow.GetInteger();
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : slow );
} else {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
}
} else {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 0);
}
}
} else {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 0);
}
}
}
return;
}
#endif
idBounds bounds( start );
bounds.AddPoint( end );
listedClipModels = gameLocal.ClipModelsTouchingBounds( this, bounds, -1, clipModelList, MAX_GENTITIES );
// dluetscher: added optimization to eliminate redundant traces
renderTrace.fraction = -1;
bboxTrace.fraction = -1;
allTrace.fraction = -1;
// no pretense at sorting here, just assume that there will only be one active
// gui within range along the trace
bool wasTargetFriendly = targetFriendly;
targetFriendly = false;
for ( i = 0; i < listedClipModels; i++ ) {
clip = clipModelList[ i ];
ent = clip->GetEntity();
//RITUAL BEGIN
//singlis: if ent is null, continue;
if(ent == NULL || ent->IsHidden()) {
continue;
}
//RITUAL END
float focusLength = (ent->GetPhysics()->GetOrigin() - start).LengthFast() - ent->GetPhysics()->GetBounds().GetRadius();
// SP only
// basically what was happening was that heads, which are an idAFAttachment, were being used for the focusLength
// calculations, but that generates a different focus length than the body would (when you scan the crosshair back
// and forth between the head and body). This ends up looking like a bug when you are right at the threshold where
// the body will display the name and rank, but doesn't when you pitch up to aim at the head. Hence, using the body
// for this special case.
if ( !gameLocal.isMultiplayer && ent->IsType( idAFAttachment::GetClassType() )) {
idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
if ( body && body->IsType( idAI::GetClassType()) ) {
focusLength = (body->GetPhysics()->GetOrigin() - start).LengthFast() - body->GetPhysics()->GetBounds().GetRadius();
}
}
bool isAI = ent->IsType( idAI::GetClassType() );
bool isFriendly = false;
if ( isAI ) {
isFriendly = (static_cast<idAI *>( ent )->team == team);
}
//change crosshair color if over a friendly
if ( !gameLocal.isMultiplayer
&& focusType == FOCUS_NONE
&& !g_crosshairCharInfoFar.GetBool() ) {
if ( focusLength < 512 ) {
bool newTargetFriendly = false;
if ( isAI && isFriendly ) {
newTargetFriendly = true;
} else if ( ent->IsType( idAFAttachment::GetClassType() ) ) {
idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
if ( body && body->IsType( idAI::GetClassType() ) && ( static_cast<idAI *>( body )->team == team ) ) {
newTargetFriendly = true;
}
}
if ( newTargetFriendly ) {
// dluetscher: added optimization to eliminate redundant traces
if ( renderTrace.fraction == -1 ) {
gameLocal.TracePoint( this, renderTrace, start, end, MASK_SHOT_RENDERMODEL, this );
}
if ( ( renderTrace.fraction < 1.0f ) && ( renderTrace.c.entityNum == ent->entityNumber ) ) {
targetFriendly = true;
if( cursor && !wasTargetFriendly ) {
cursor->HandleNamedEvent( "showCrossBuddy" );
}
}
}
}
}
// RAVEN BEGIN
#ifdef _XENON
if ( doAimAssist ) {
if ( isAI && !isFriendly && (ent->health > 0) ) {
if ( idStr::Icmp( name.c_str(),ent->name.c_str() ) != 0 ) {
const idBounds &bounds = ent->GetPhysics()->GetAbsBounds();
//if ( showDebug ) {
//gameRenderWorld->DebugBounds(colorGreen, bounds, vec3_origin, 20);
//}
bool inside = aimArea.IntersectsBounds(bounds);
if ( inside ) {
dist = bounds.ShortestDistance(GetEyePosition());
if ( bDist > dist ) {
if ( bboxTrace.fraction == -1 ) {
gameLocal.TracePoint( this, bboxTrace, start, end, MASK_SHOT_BOUNDINGBOX, this );
}
if ( ( bboxTrace.fraction < 1.0f ) && ( bboxTrace.c.entityNum != ent->entityNumber ) ) {
idVec3 v = end - start;
if ( ((v.Length() * bboxTrace.fraction) + 5.0f) >= dist ) {
// close enough to the bbox
bDist = dist;
bestEnemy = (idActor *)ent;
}
} else {
// Didn't hit anything, so this must be in view
bDist = dist;
bestEnemy = (idActor *)ent;
}
}
}
}
}
}
#endif
// mekberg: allowFocus removed
if ( focusLength < (g_crosshairCharInfoFar.GetBool()?256.0f:80.0f) ) {
// RAVEN END
if ( ent->IsType( idAFAttachment::GetClassType() ) ) {
idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
if ( body && body->IsType( idAI::GetClassType() ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
// dluetscher: added optimization to eliminate redundant traces
if ( renderTrace.fraction == -1 ) {
gameLocal.TracePoint( this, renderTrace, start, end, MASK_SHOT_RENDERMODEL, this );
}
if ( ( renderTrace.fraction < 1.0f ) && ( renderTrace.c.entityNum == ent->entityNumber ) ) {
SetFocus ( FOCUS_CHARACTER, FOCUS_TIME, body, NULL );
if ( focusLength < 80.0f ) {
talkCursor = 1;
}
break;
}
}
continue;
}
if ( isAI ) {
if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
// dluetscher: added optimization to eliminate redundant traces
if ( renderTrace.fraction == -1 ) {
gameLocal.TracePoint( this, renderTrace, start, end, MASK_SHOT_RENDERMODEL, this );
}
if ( ( renderTrace.fraction < 1.0f ) && ( renderTrace.c.entityNum == ent->entityNumber ) ) {
SetFocus ( FOCUS_CHARACTER, FOCUS_TIME, ent, NULL );
if ( focusLength < 80.0f ) {
talkCursor = 1;
}
break;
}
}
continue;
}
}
if ( focusLength < 80.0f ) {
if ( ent->IsType( rvVehicle::GetClassType() ) ) {
rvVehicle* vehicle = static_cast<rvVehicle*>(ent);
// dluetscher: added optimization to eliminate redundant traces
if ( bboxTrace.fraction == -1 ) {
gameLocal.TracePoint( this, bboxTrace, start, end, MASK_SHOT_BOUNDINGBOX, this );
}
if ( ( bboxTrace.fraction < 1.0f ) && ( bboxTrace.c.entityNum == ent->entityNumber ) && ((end - start).Length() * bboxTrace.fraction < vehicle->FocusLength()) ) {
//jshepard: locked or unusable vehicles
if ( !vehicle->IsLocked() && vehicle->HasOpenPositions() ) {
SetFocus ( FOCUS_VEHICLE, FOCUS_TIME, ent, NULL );
} else {
SetFocus ( FOCUS_LOCKED_VEHICLE, FOCUS_TIME, ent, NULL );
}
break;
}
}
//jshepard: unusable vehicle
if( ent->spawnArgs.GetBool( "unusableVehicle", "0" ) ) {
if ( allTrace.fraction == -1.f ) {
gameLocal.TracePoint( this, allTrace, start, end, MASK_ALL, this );
}
if ( ( allTrace.fraction < 1.0f ) && ( allTrace.c.entityNum == ent->entityNumber ) ) {
SetFocus ( FOCUS_LOCKED_VEHICLE, FOCUS_TIME, ent, NULL );
break;
}
}
// Usable entities are last
if ( ent->fl.usable ) {
//jshepard: fake vehicles
if ( allTrace.fraction == -1.f ) {
gameLocal.TracePoint( this, allTrace, start, end, MASK_ALL, this );
}
if ( ( allTrace.fraction < 1.0f ) && ( allTrace.c.entityNum == ent->entityNumber ) ) {
if( ent->spawnArgs.GetBool("crosshair_vehicle")) {
SetFocus ( FOCUS_USABLE_VEHICLE, FOCUS_TIME, ent, NULL );
} else {
SetFocus ( FOCUS_USABLE, FOCUS_TIME, ent, NULL );
}
break;
}
}
}
if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
continue;
}
if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
// don't allow guis on pickup items focus
continue;
}
pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
if ( pt.x != -1 ) {
idUserInterface* ui = NULL;
renderEntity_t* focusGUIrenderEntity;
focusGUIrenderEntity = ent->GetRenderEntity();
if ( !focusGUIrenderEntity ) {
continue;
}
if ( pt.guiId >= 1 && pt.guiId <= MAX_RENDERENTITY_GUI ) {
ui = focusGUIrenderEntity->gui[ pt.guiId-1 ];
}
if ( ui == NULL || !ui->IsInteractive ( ) ) {
continue;
}
// All focused guis get brackets
focusBrackets = ui;
// Any GUI that is too far away will just get bracket focus so the player can still shoot
// but still see which guis are interractive
if ( focusLength > 300.0f ) {
ClearFocus ( );
break;
} else if ( focusLength > 80.0f ) {
ClearFocus ( );
focusType = FOCUS_BRACKETS;
#ifdef _XENON
// Hide the "press whatever" text
hud->SetStateInt( "GUIIsNotUsingDPad", 3 );
hud->SetStateInt( "hideInteractive", 1 );
#endif
break;
}
// If this is the first time this gui was activated then set up some things
if ( focusEnt != ent ) {
const idKeyValue* kv;
// new activation
// going to see if we have anything in inventory a gui might be interested in
// need to enumerate inventory items
ui->SetStateInt( "inv_count", inventory.items.Num() );
for ( j = 0; j < inventory.items.Num(); j++ ) {
idDict *item = inventory.items[ j ];
const char *iname = item->GetString( "inv_name" );
iname = common->GetLocalizedString( iname );
const char *iicon = item->GetString( "inv_icon" );
const char *itext = item->GetString( "inv_text" );
ui->SetStateString( va( "inv_name_%i", j), iname );
ui->SetStateString( va( "inv_icon_%i", j), iicon );
ui->SetStateString( va( "inv_text_%i", j), itext );
kv = item->MatchPrefix("inv_id", NULL);
if ( kv ) {
ui->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
}
ui->SetStateInt( iname, 1 );
}
for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
const char *p = inventory.pdaSecurity[ j ];
if ( p && *p ) {
ui->SetStateInt( p, 1 );
}
}
ui->SetStateString( "player_health", va("%i", health ) );
ui->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
kv = ent->spawnArgs.MatchPrefix( "gui_", NULL );
while ( kv ) {
ui->SetStateString( kv->GetKey(), common->GetLocalizedString( kv->GetValue() ) );
kv = ent->spawnArgs.MatchPrefix( "gui_", kv );
}
}
// clamp the mouse to the corner
const char* command;
sysEvent_t ev;
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
command = ui->HandleEvent( &ev, gameLocal.time );
HandleGuiCommands( ent, command );
// move to an absolute position
ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
command = ui->HandleEvent( &ev, gameLocal.time );
HandleGuiCommands( ent, command );
#ifdef _XENON
int usepad = 0;
if ( focusUI ) {
idWindow *dwin = ui->GetDesktop();
if ( dwin ) {
idWinVar *wv = dwin->GetWinVarByName("dpadGUI");
if ( wv ) {
usepad = atoi(wv->c_str());
}
}
}
hud->SetStateInt( "GUIIsNotUsingDPad", (usepad) ? 0 : 1 );
hud->SetStateInt( "hideInteractive", 0 );
#endif
SetFocus ( FOCUS_GUI, FOCUS_GUI_TIME, ent, ui );
break;
}
}
#ifdef _XENON
if ( !gameLocal.isMultiplayer || (gameLocal.isMultiplayer && IsLocalClient() ) ) {
if ( !weapon || !weapon->AllowAutoAim() ) {
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 0);
}
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
} else {
if ( bestEnemy && doAimAssist ) {
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 1);
}
trace_t trace;
idVec3 altEnd = bestEnemy->GetPhysics()->GetOrigin();
altEnd[2]+=35.0f; // origin is always below the monster's feet for whatever reason, so aim at this
gameLocal.TracePoint( this, trace, start, altEnd, MASK_SHOT_BOUNDINGBOX, this );
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == bestEnemy->entityNumber ) ) {
int slow = pm_AimAssistSlow.GetInteger();
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : slow );
} else {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
}
} else {
usercmdGen->SetSlowJoystick( zoomed ? pm_zoomedSlow.GetInteger() : 100 );
if ( cursor ) {
cursor->SetStateInt("enemyFocus", 0);
}
}
}
}
#endif
if ( !gameLocal.isMultiplayer || (gameLocal.isMultiplayer && IsLocalClient() ) ) {
if ( wasTargetFriendly && !targetFriendly && focusType == FOCUS_NONE ) {
if ( cursor ) {
cursor->HandleNamedEvent ( WeaponIsEnabled() ? "showCrossCombat" : "crossHide" );
}
}
}
// Update the focus brackets within the hud
if ( focusBrackets && hud ) {
if (focusType == FOCUS_BRACKETS || focusType == FOCUS_GUI ) {
if ( !oldBrackets || focusBracketsTime ) {
hud->HandleNamedEvent ( "showBrackets" );
}
focusBracketsTime = 0;
} else {
if ( focusBracketsTime == 0 ) {
hud->HandleNamedEvent ( "fadeBrackets" );
focusBracketsTime = gameLocal.time + 2000;
}
}
focusBrackets->SetStateBool ( "2d_calc", true );
}
if ( cursor && ( oldTalkCursor != talkCursor ) ) {
cursor->SetStateInt( "talkcursor", talkCursor );
}
}
/*
================
idPlayer::ClearFocus
================
*/
void idPlayer::ClearFocus ( void ) {
SetFocus ( FOCUS_NONE, 0, NULL, NULL );
}
void idPlayer::UpdateFocusCharacter( idEntity* newEnt ) {
if ( !cursor ) {
return;
}
// Handle character interaction
cursor->SetStateString( "npc", common->GetLocalizedString(newEnt->spawnArgs.GetString( "npc_name", "Joe" )) );
cursor->SetStateString( "npcdesc", common->GetLocalizedString(newEnt->spawnArgs.GetString( "npc_description", "" )) );
if ( newEnt->IsType( rvAIMedic::GetClassType() ) ) {
if ( ((rvAIMedic*)newEnt)->isTech ) {
cursor->SetStateInt( "npc_medictech", 2 );
} else {
cursor->SetStateInt( "npc_medictech", 1 );
}
} else {
cursor->SetStateInt( "npc_medictech", 0 );
}
}
/*
================
idPlayer::SetFocus
================
*/
void idPlayer::SetFocus ( playerFocus_t newType, int _focusTime, idEntity* newEnt, idUserInterface* newUI ) {
const char* command;
// Handle transitions from one user interface to another or to none
if ( newUI != focusUI ) {
if ( focusUI ) {
command = focusUI->Activate( false, gameLocal.time );
HandleGuiCommands( focusEnt, command );
StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
}
if ( newUI ) {
command = newUI->Activate( true, gameLocal.time );
HandleGuiCommands( newEnt, command );
StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
// Hide the weapon when a gui is being used
/*
if ( weapon ) {
weapon->Hide();
}
*/
}/*
else if ( weapon ) {
// Show the weapon since it was hidden when a gui first got focus
weapon->Show();
}
*/
}
//jshepard: the medic/tech crosshair won't update unless handleNamedEvent is called. I moved this outside
//of the switch below because the focus type is the same when moving directly from marine to marine, but the
//medic/tech status may change.
if ( newType == FOCUS_CHARACTER ) {
if ( newEnt != focusEnt ) {
UpdateFocusCharacter( newEnt );
cursor->HandleNamedEvent ( "showCrossTalk" );
}
}
// Show the appropriate cursor for the current focus type
if ( cursor && ( focusType != newType ) ) {
switch ( newType ) {
case FOCUS_VEHICLE:
case FOCUS_USABLE_VEHICLE:
cursor->HandleNamedEvent ( "showCrossVehicle" );
break;
case FOCUS_LOCKED_VEHICLE:
cursor->HandleNamedEvent ( "showCrossVehicleLocked" );
break;
case FOCUS_USABLE:
cursor->HandleNamedEvent ( "showCrossUsable" );
break;
case FOCUS_GUI:
cursor->HandleNamedEvent ( "showCrossGui" );
break;
case FOCUS_CHARACTER:
if ( newEnt != focusEnt ) {
UpdateFocusCharacter( newEnt );
}
cursor->HandleNamedEvent ( "showCrossTalk" );
break;
default:
// Make sure the weapon is shown in the default state
// RAVEN BEGIN
// abahr: don't do this if weapons are disabled
cursor->HandleNamedEvent ( WeaponIsEnabled() ? "showCrossCombat" : "crossHide" );
// RAVEN END
break;
}
}
focusType = newType;
focusEnt = newEnt;
focusUI = newUI;
if ( focusType == FOCUS_NONE ) {
focusTime = 0;
} else {
focusTime = gameLocal.time + _focusTime;
}
}
/*
=================
idPlayer::CrashLand
Check for hard landings that generate sound events
=================
*/
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
idVec3 origin, velocity;
idVec3 gravityVector, gravityNormal;
float delta;
float dist;
float vel, acc;
float t;
float a, b, c, den;
waterLevel_t waterLevel;
bool noDamage;
pfl.softLanding = false;
pfl.hardLanding = false;
// if the player is not on the ground
if ( !physicsObj.HasGroundContacts() ) {
return;
}
gravityNormal = physicsObj.GetGravityNormal();
// if the player wasn't going down
if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
return;
}
waterLevel = physicsObj.GetWaterLevel();
// never take falling damage if completely underwater
if ( waterLevel == WATERLEVEL_HEAD ) {
return;
}
// no falling damage if touching a nodamage surface
noDamage = false;
for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
const contactInfo_t &contact = physicsObj.GetContact( i );
if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
noDamage = true;
break;
}
}
//jshepard: no falling damage if falling damage is disabled
if( pfl.noFallingDamage ) {
return;
}
origin = GetPhysics()->GetOrigin();
gravityVector = physicsObj.GetGravity();
// calculate the exact velocity on landing
dist = ( origin - oldOrigin ) * -gravityNormal;
vel = oldVelocity * -gravityNormal;
acc = -gravityVector.Length();
a = acc / 2.0f;
b = vel;
c = -dist;
den = b * b - 4.0f * a * c;
if ( den < 0 ) {
return;
}
t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
delta = vel + t * acc;
delta = delta * delta * 0.0001;
// reduce falling damage if there is standing water
if ( waterLevel == WATERLEVEL_WAIST ) {
delta *= 0.25f;
}
if ( waterLevel == WATERLEVEL_FEET ) {
delta *= 0.5f;
}
if ( delta < 1.0f ) {
return;
}
// Some bug in the player movement code causes double fall damage on the 2nd frame in certain situations,
// so hack around this. Basically, if the next frame's velocity is going to be great enough to cause damage,
// don't do the damage this frame. This feels a lot safer than messing with things that could break player
// movement in worse ways.
if ( gameLocal.isMultiplayer ) {
float vel2 = GetPhysics()->GetLinearVelocity() * -gravityNormal;
a = acc / 2.0f;
b = vel2;
c = -dist;
den = b * b - 4.0f * a * c;
if ( den < 0 ) {
return;
}
t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
float delta2 = vel2 + t * acc;
delta2 = delta2 * delta2 * 0.0001;
if ( delta2 > softFallDelta ) {
return;
}
}
// ddynerman: moved height delta selection to player def
if ( delta > fatalFallDelta && fatalFallDelta > 0.0f ) {
pfl.hardLanding = true;
landChange = -32;
landTime = gameLocal.time;
if ( !noDamage ) {
pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
}
} else if ( delta > hardFallDelta && hardFallDelta > 0.0f ) {
pfl.hardLanding = true;
landChange = -24;
landTime = gameLocal.time;
if ( !noDamage ) {
pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
}
} else if ( delta > softFallDelta && softFallDelta > 0.0f ) {
pfl.softLanding = true;
landChange = -16;
landTime = gameLocal.time;
if ( !noDamage ) {
pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
}
} else if ( delta > noFallDelta && noFallDelta > 0.0f ) {
pfl.softLanding = true;
landChange = -8;
landTime = gameLocal.time;
}
// ddynerman: sometimes the actual landing animation is pre-empted by another animation (i.e. sliding, moving forward)
// so we play the landing sound here instead of relying on the anim
if( pfl.hardLanding ) {
StartSound ( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
StartSound ( "snd_land_hard_pain", SND_CHANNEL_ANY, 0, false, NULL );
} else if ( pfl.softLanding ) {
// todo - 2 different landing sounds for variety?
StartSound ( "snd_land_soft", SND_CHANNEL_ANY, 0, false, NULL );
}
}
/*
===============
idPlayer::BobCycle
===============
*/
void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
float bobmove;
int old, deltaTime;
idVec3 vel, gravityDir, velocity;
idMat3 viewaxis;
float bob;
float delta;
float speed;
float f;
//
// calculate speed and cycle to be used for
// all cyclic walking effects
//
velocity = physicsObj.GetLinearVelocity() - pushVelocity;
if ( noclip ) {
velocity.Zero ( );
}
gravityDir = physicsObj.GetGravityNormal();
vel = velocity - ( velocity * gravityDir ) * gravityDir;
xyspeed = vel.LengthFast();
if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( gameLocal.isMultiplayer && spectating ) ) {
// airborne
bobCycle = 0;
bobFoot = 0;
bobfracsin = 0;
} else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
// start at beginning of cycle again
bobCycle = 0;
bobFoot = 0;
bobfracsin = 0;
} else {
if ( physicsObj.IsCrouching() ) {
bobmove = pm_crouchbob.GetFloat();
// ducked characters never play footsteps
} else {
// vary the bobbing based on the speed of the player
bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
}
// check for footstep / splash sounds
old = bobCycle;
bobCycle = (int)( old + bobmove * gameLocal.GetMSec() ) & 255;
bobFoot = ( bobCycle & 128 ) >> 7;
bobfracsin = idMath::Fabs( idMath::Sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
}
// calculate angles for view bobbing
viewBobAngles.Zero();
// no view bob at all in MP while zoomed in
if ( IsZoomed() ) {
bobCycle = 0;
bobFoot = 0;
bobfracsin = 0;
} else {
viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
// add angles based on velocity
delta = velocity * viewaxis[0];
viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
delta = velocity * viewaxis[1];
viewBobAngles.roll -= delta * pm_runroll.GetFloat();
// add angles based on bob
// make sure the bob is visible even at low speeds
speed = xyspeed > 200 ? xyspeed : 200;
delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
if ( physicsObj.IsCrouching() ) {
delta *= 3; // crouching
}
viewBobAngles.pitch += delta;
delta = bobfracsin * pm_bobroll.GetFloat() * speed;
if ( physicsObj.IsCrouching() ) {
delta *= 3; // crouching accentuates roll
}
if ( bobFoot & 1 ) {
delta = -delta;
}
viewBobAngles.roll += delta;
}
// calculate position for view bobbing
viewBob.Zero();
if ( physicsObj.HasSteppedUp() ) {
// check for stepping up before a previous step is completed
deltaTime = gameLocal.time - stepUpTime;
if ( deltaTime < STEPUP_TIME ) {
stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
} else {
stepUpDelta = physicsObj.GetStepUp();
}
if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
stepUpDelta = 2.0f * pm_stepsize.GetFloat();
}
stepUpTime = gameLocal.time;
}
idVec3 gravity = physicsObj.GetGravityNormal();
// if the player stepped up recently
deltaTime = gameLocal.time - stepUpTime;
if ( deltaTime < STEPUP_TIME ) {
viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
}
// add bob height after any movement smoothing
bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
if ( bob > 6 ) {
bob = 6;
}
// RAVEN BEGIN
// abahr: added gravity
viewBob += bob * -gravityDir;
// RAVEN END
// add fall height
delta = gameLocal.time - landTime;
if ( delta < LAND_DEFLECT_TIME ) {
f = delta / LAND_DEFLECT_TIME;
viewBob -= gravity * ( landChange * f );
} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
delta -= LAND_DEFLECT_TIME;
f = 1.0 - ( delta / LAND_RETURN_TIME );
viewBob -= gravity * ( landChange * f );
}
}
/*
================
idPlayer::UpdateDeltaViewAngles
================
*/
void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
// set the delta angle
idAngles delta;
for( int i = 0; i < 3; i++ ) {
delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
}
SetDeltaViewAngles( delta );
}
/*
================
idPlayer::SetViewAngles
================
*/
void idPlayer::SetViewAngles( const idAngles &angles ) {
UpdateDeltaViewAngles(angles);
viewAngles = angles;
}
/*
================
idPlayer::UpdateViewAngles
================
*/
void idPlayer::UpdateViewAngles( void ) {
int i;
idAngles delta;
if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 ) ) {
// no view changes at all, but we still want to update the deltas or else when
// we get out of this mode, our view will snap to a kind of random angle
UpdateDeltaViewAngles( viewAngles );
return;
}
// if dead
if ( health <= 0 ) {
if ( pm_thirdPersonDeath.GetBool() ) {
viewAngles.roll = 0.0f;
viewAngles.pitch = 30.0f;
} else {
viewAngles.roll = 40.0f;
viewAngles.pitch = -15.0f;
}
return;
}
for ( i = 0; i < 3; i++ ) {
cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
if ( influenceActive == INFLUENCE_LEVEL3 ) {
viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
} else {
viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i] ) + deltaViewAngles[i] );
}
}
if ( !centerView.IsDone( gameLocal.time ) ) {
viewAngles.pitch = centerView.GetCurrentValue( gameLocal.time );
}
// clamp the pitch
if ( noclip ) {
if ( viewAngles.pitch > 89.0f ) {
// don't let the player look down more than 89 degrees while noclipping
viewAngles.pitch = 89.0f;
} else if ( viewAngles.pitch < -89.0f ) {
// don't let the player look up more than 89 degrees while noclipping
viewAngles.pitch = -89.0f;
}
} else {
if ( viewAngles.pitch > pm_maxviewpitch.GetFloat() ) {
// don't let the player look down enough to see the shadow of his (non-existant) feet
viewAngles.pitch = pm_maxviewpitch.GetFloat();
} else if ( viewAngles.pitch < pm_minviewpitch.GetFloat() ) {
// don't let the player look up more than 89 degrees
viewAngles.pitch = pm_minviewpitch.GetFloat();
}
}
UpdateDeltaViewAngles( viewAngles );
// orient the model towards the direction we're looking
SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
// save in the log for analyzing weapon angle offsets
loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
}
/*
==============
idPlayer::UpdateAir
==============
*/
void idPlayer::UpdateAir( void ) {
if ( health <= 0 ) {
return;
}
// see if the player is connected to the info_vacuum
bool newAirless = false;
if ( gameLocal.vacuumAreaNum != -1 ) {
int num = GetNumPVSAreas();
if ( num > 0 ) {
int areaNum;
// if the player box spans multiple areas, get the area from the origin point instead,
// otherwise a rotating player box may poke into an outside area
if ( num == 1 ) {
const int *pvsAreas = GetPVSAreas();
areaNum = pvsAreas[0];
} else {
areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
}
newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
}
}
if ( newAirless ) {
if ( !airless ) {
StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
if ( hud ) {
hud->HandleNamedEvent( "noAir" );
}
}
airTics--;
if ( airTics < 0 ) {
airTics = 0;
// check for damage
const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
if ( gameLocal.time > lastAirDamage + dmgTiming ) {
Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
lastAirDamage = gameLocal.time;
}
}
} else {
if ( airless ) {
StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
StopSound( SND_CHANNEL_BODY2, false );
if ( hud ) {
hud->HandleNamedEvent( "Air" );
}
}
airTics+=2; // regain twice as fast as lose
if ( airTics > pm_airTics.GetInteger() ) {
airTics = pm_airTics.GetInteger();
}
}
airless = newAirless;
if ( hud ) {
hud->SetStateInt( "player_air", 100 * airTics / pm_airTics.GetInteger() );
}
}
// RAVEN BEGIN
// abahr
/*
==============
idPlayer::UpdateGravity
==============
*/
void idPlayer::UpdateGravity( void ) {
GetPhysics()->SetGravity( gameLocal.GetCurrentGravity(this) );
}
// RAVEN END
/*
==============
idPlayer::ToggleObjectives
==============
*/
void idPlayer::ToggleObjectives ( void ) {
// RAVEN BEGIN
// mekberg: allow disabling of objectives.
if ( objectiveSystem == NULL || !objectivesEnabled ) {
return;
}
// RAVEN END
if ( !objectiveSystemOpen ) {
int j, c = inventory.items.Num();
objectiveSystem->SetStateInt( "inv_count", c );
for ( j = 0; j < c; j++ ) {
idDict *item = inventory.items[j];
if ( !item->GetBool( "inv_pda" ) ) {
const char *iname = item->GetString( "inv_name" );
iname = common->GetLocalizedString( iname );
const char *iicon = item->GetString( "inv_icon" );
const char *itext = item->GetString( "inv_text" );
objectiveSystem->SetStateString( va( "inv_name_%i", j ), iname );
objectiveSystem->SetStateString( va( "inv_icon_%i", j ), iicon );
objectiveSystem->SetStateString( va( "inv_text_%i", j ), itext );
const idKeyValue *kv = item->MatchPrefix( "inv_id", NULL );
if ( kv ) {
objectiveSystem->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
}
}
}
for ( j = 0; j < MAX_WEAPONS; j++ ) {
const char *weapnum = va( "def_weapon%d", j );
int weapstate = 0;
if ( inventory.weapons & ( 1 << j ) ) {
const char *weap = spawnArgs.GetString( weapnum );
if ( weap && *weap ) {
weapstate++;
}
}
objectiveSystem->SetStateInt( weapnum, weapstate );
}
UpdateObjectiveInfo();
objectiveSystem->Activate( true, gameLocal.time );
objectiveSystem->HandleNamedEvent( "wristcommShow" );
} else {
objectiveSystem->Activate( false, gameLocal.time );
objectiveSystem->HandleNamedEvent( "wristcommHide" );
}
objectiveSystemOpen ^= 1;
}
/*
==============
idPlayer::ToggleScoreboard
==============
*/
void idPlayer::ToggleScoreboard( void ) {
scoreBoardOpen ^= 1;
}
/*
==============
idPlayer::Spectate
==============
*/
void idPlayer::Spectate( bool spectate, bool force ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
// track invisible player bug
// all hiding and showing should be performed through Spectate calls
// except for the private camera view, which is used for teleports
// ok for players in other instances to be hidden
assert( force || ( teleportEntity.GetEntity() != NULL ) || ( IsHidden() || !spectating ) || ( IsHidden() && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) );
if ( spectating == spectate && !force ) {
return;
}
bool inOtherInstance = gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance;
spectating = spectate;
// don't do any smoothing with this snapshot
predictedFrame = gameLocal.framenum;
if ( gameLocal.isServer ) {
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteBits( spectating, 1 );
ServerSendEvent( EVENT_SPECTATE, &msg, false, -1 );
}
// on the client, we'll get spectate messages about clients in other instances - always assume they are spectating
if ( spectating || inOtherInstance ) {
// join the spectators
delete clientHead;
clientHead = NULL;
ClearPowerUps();
spectator = this->entityNumber;
Init();
StopRagdoll();
SetPhysics( &physicsObj );
common->DPrintf( "idPlayer::Spectate() - Disabling clip for %d '%s' - spectate: %d force: %d\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "?", spectate, force );
physicsObj.DisableClip();
Hide();
Event_DisableWeapon();
// remove the weapon
delete weapon;
weapon = NULL;
if ( !gameLocal.isClient ) {
delete weaponViewModel;
weaponViewModel = NULL;
delete weaponWorldModel;
weaponWorldModel = NULL;
}
} else {
// put everything back together again
UpdateModelSetup( true );
currentWeapon = -1; // to make sure the def will be loaded if necessary
Show();
Event_EnableWeapon();
}
SetClipModel( inOtherInstance );
if ( inOtherInstance ) {
// Normally idPlayer::Move() gets called to set the contents to 0, but we don't call
// move on players not in our snap, so we need to set it manually here.
physicsObj.SetContents( 0 );
physicsObj.SetMovementType( PM_SPECTATOR );
physicsObj.SetClipMask( MASK_DEADSOLID );
}
}
/*
==============
idPlayer::SetClipModel
==============
*/
void idPlayer::SetClipModel( bool forceSpectatorBBox ) {
idBounds bounds;
common->DPrintf( "idPlayer::SetClipModel() - Called on %d '%s' forceSpectatorBBox = %d spectate = %d instance = %d local instance = %d\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "", forceSpectatorBBox, spectating, instance, gameLocal.GetLocalPlayer() ? gameLocal.GetLocalPlayer()->GetInstance() : -1 );
if ( spectating || forceSpectatorBBox ) {
bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
} else {
bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
}
// the origin of the clip model needs to be set before calling SetClipModel
// otherwise our physics object's current origin value gets reset to 0
idClipModel *newClip;
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM_AUTO(p0,this);
// RAVEN END
// < 0: don't use alternate alignment, > 0: use alternate alignment (faces aligned with axes for even-sided cylinders)
// 0: use AABB; 1: use 8-sided cylinder; 3+: use custom number of sides for cylinder
int sides = pm_usecylinder.GetInteger();
bool alt_align = (sides > 0);
sides = idMath::Abs( sides );
if ( sides >= 1 ) {
if ( sides < 3 ) {
sides = 8;
}
idTraceModel trm( bounds, sides, alt_align );
// trm.findWalkSurfaces = true;
newClip = new idClipModel( trm, NULL );
} else {
idTraceModel trm( bounds );
// trm.findWalkSurfaces = true;
newClip = new idClipModel( trm, NULL );
}
newClip->Translate( physicsObj.PlayerGetOrigin() );
physicsObj.SetClipModel( newClip, 1.0f );
}
/*
==============
idPlayer::EnterVehicle
==============
*/
bool idPlayer::EnterVehicle( idEntity* vehicle ) {
if ( !idActor::EnterVehicle ( vehicle ) ) {
return false;
}
// RAVEN BEGIN
// jshepard: safety first
if( weapon) {
weapon->Hide();
}
// abahr:
//HideCrosshair();
// RAVEN END
return true;
}
/*
==============
idPlayer::ExitVehicle
==============
*/
bool idPlayer::ExitVehicle ( bool force ) {
if ( !idActor::ExitVehicle ( force ) ) {
return false;
}
SetViewAngles( viewAxis[0].ToAngles() );
// RAVEN BEGIN
// jshepard: had this crash on me more than once :(
if(weapon) {
weapon->Show();
}
// abahr: Would like to call something more specific
ShowCrosshair();
// RAVEN END
return true;
}
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
/*
==============
GetItemCost
==============
*/
int idPlayer::GetItemCost( const char* itemName ) {
if ( !itemCosts ) {
assert( false );
return 99999;
}
return itemCosts->dict.GetInt( itemName, "99999" );
}
/*
==============
GetItemBuyImpulse
==============
*/
int GetItemBuyImpulse( const char* itemName )
{
struct ItemBuyImpulse
{
const char* itemName;
int itemBuyImpulse;
};
ItemBuyImpulse itemBuyImpulseTable[] =
{
{ "weapon_shotgun", IMPULSE_100, },
{ "weapon_machinegun", IMPULSE_101, },
{ "weapon_hyperblaster", IMPULSE_102, },
{ "weapon_grenadelauncher", IMPULSE_103, },
{ "weapon_nailgun", IMPULSE_104, },
{ "weapon_rocketlauncher", IMPULSE_105, },
{ "weapon_railgun", IMPULSE_106, },
{ "weapon_lightninggun", IMPULSE_107, },
// IMPULSE_108 - Unused
{ "weapon_napalmgun", IMPULSE_109, },
// { "weapon_dmg", IMPULSE_110, },
// IMPULSE_111 - Unused
// IMPULSE_112 - Unused
// IMPULSE_113 - Unused
// IMPULSE_114 - Unused
// IMPULSE_115 - Unused
// IMPULSE_116 - Unused
// IMPULSE_117 - Unused
{ "item_armor_small", IMPULSE_118, },
{ "item_armor_large", IMPULSE_119, },
{ "ammorefill", IMPULSE_120, },
// IMPULSE_121 - Unused
// IMPULSE_122 - Unused
{ "ammo_regen", IMPULSE_123, },
{ "health_regen", IMPULSE_124, },
{ "damage_boost", IMPULSE_125, },
// IMPULSE_126 - Unused
// IMPULSE_127 - Unused
};
const int itemBuyImpulseTableSize = sizeof(itemBuyImpulseTable) / sizeof(itemBuyImpulseTable[0]);
for( int i = 0; i < itemBuyImpulseTableSize; ++ i )
{
if( !stricmp( itemBuyImpulseTable[ i ].itemName, itemName ) )
{
return itemBuyImpulseTable[ i ].itemBuyImpulse;
}
}
return 0;
}
bool idPlayer::CanBuyItem( const char* itemName )
{
itemBuyStatus_t buyStatus = ItemBuyStatus( itemName );
return( buyStatus == IBS_CAN_BUY );
}
itemBuyStatus_t idPlayer::ItemBuyStatus( const char* itemName )
{
idStr itemNameStr = itemName;
if ( itemNameStr == "notimplemented" )
{
return IBS_NOT_ALLOWED;
}
else if( !idStr::Cmpn( itemName, "wpmod_", 6 ) )
{
return IBS_NOT_ALLOWED;
}
else if( itemNameStr == "item_armor_small" )
{
if( inventory.armor >= 190 )
return IBS_ALREADY_HAVE;
if( inventory.carryOverWeapons & CARRYOVER_FLAG_ARMOR_LIGHT )
return IBS_ALREADY_HAVE;
if( PowerUpActive( POWERUP_SCOUT ) )
return IBS_NOT_ALLOWED;
}
else if( itemNameStr == "item_armor_large" )
{
if( inventory.armor >= 190 )
return IBS_ALREADY_HAVE;
if( inventory.carryOverWeapons & CARRYOVER_FLAG_ARMOR_HEAVY )
return IBS_ALREADY_HAVE;
if( PowerUpActive( POWERUP_SCOUT ) )
return IBS_NOT_ALLOWED;
}
else if( itemNameStr == "ammorefill" )
{
if( inventory.carryOverWeapons & CARRYOVER_FLAG_AMMO )
return IBS_ALREADY_HAVE;
// If we are full of ammo for all weapons, you can't buy the ammo refill anymore.
bool fullAmmo = true;
for ( int i = 0 ; i < MAX_AMMOTYPES; i++ )
{
if ( inventory.ammo[i] != inventory.MaxAmmoForAmmoClass( this, rvWeapon::GetAmmoNameForIndex(i) ) )
fullAmmo = false;
}
if ( fullAmmo )
return IBS_NOT_ALLOWED;
}
else if ( itemNameStr == "fc_armor_regen" )
{
return IBS_NOT_ALLOWED;
}
if ( gameLocal.gameType == GAME_DM || gameLocal.gameType == GAME_TOURNEY || gameLocal.gameType == GAME_ARENA_CTF || gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
if ( itemNameStr == "ammo_regen" )
return IBS_NOT_ALLOWED;
if ( itemNameStr == "health_regen" )
return IBS_NOT_ALLOWED;
if ( itemNameStr == "damage_boost" )
return IBS_NOT_ALLOWED;
}
if ( CanSelectWeapon(itemName) != -1 )
return IBS_ALREADY_HAVE;
int cost = GetItemCost(itemName);
if ( cost > (int)buyMenuCash )
{
return IBS_CANNOT_AFFORD;
}
return IBS_CAN_BUY;
}
/*
==============
idPlayer::UpdateTeamPowerups
==============
*/
void idPlayer::UpdateTeamPowerups( bool isBuying ) {
for ( int i=0; i<MAX_TEAM_POWERUPS; i++ )
{
// If the powerup needs updating...
if ( gameLocal.mpGame.teamPowerups[team][i].powerup != 0 && gameLocal.mpGame.teamPowerups[team][i].update )
{
int powerup = gameLocal.mpGame.teamPowerups[team][i].powerup;
int time = gameLocal.mpGame.teamPowerups[team][i].time;
// Go through all the clients and update the status of this powerup.
for( int j = 0; j < gameLocal.numClients; j++ ) {
idEntity* ent = gameLocal.entities[ j ];
if ( !ent )
continue;
if ( !ent->IsType( idPlayer::Type ) )
continue;
idPlayer* player = static_cast< idPlayer * >( ent );
// Not my teammate
if ( player->team != team )
continue;
// when a teammate respawns while a powerup is active, we don't want to go through other players which already have the powerup
// otherwise they'll get multiple VO announces
// ignore this when setting up powerups after a buying impulse, it only means someone is buying before expiration
if ( !isBuying && ( ( player->inventory.powerups & ( 1 << powerup ) ) != 0 ) ) {
continue;
}
player->GivePowerUp( powerup, time, true );
}
gameLocal.mpGame.teamPowerups[team][i].update = false;
}
}
}
/*
==============
idPlayer::AttemptToBuyTeamPowerup
==============
*/
bool idPlayer::AttemptToBuyTeamPowerup( const char* itemName )
{
idStr itemNameStr = itemName;
if ( itemNameStr == "ammo_regen" ) {
gameLocal.mpGame.AddTeamPowerup(POWERUP_AMMOREGEN, SEC2MS(30), team);
UpdateTeamPowerups( true );
return true;
}
else if ( itemNameStr == "health_regen" ) {
gameLocal.mpGame.AddTeamPowerup(POWERUP_REGENERATION, SEC2MS(30), team);
UpdateTeamPowerups( true );
return true;
}
else if ( itemNameStr == "damage_boost" ) {
gameLocal.mpGame.AddTeamPowerup(POWERUP_TEAM_DAMAGE_MOD, SEC2MS(30), team);
UpdateTeamPowerups( true );
return true;
}
return false;
}
/*
==============
idPlayer::AttemptToBuyItem
==============
*/
bool idPlayer::AttemptToBuyItem( const char* itemName )
{
assert( !IsFakeClient() );
if ( gameLocal.isClient ) {
return false;
}
if( !itemName ) {
return false;
}
int itemCost = GetItemCost( itemName );
/// Check if the player is allowed to buy this item
if( !CanBuyItem( itemName ) )
{
return false;
}
const char* playerName = GetUserInfo()->GetString( "ui_name" );
common->DPrintf( "Player %s about to buy item %s; player has %d (%g) credits, cost is %d\n", playerName, itemName, (int)buyMenuCash, buyMenuCash, itemCost );
buyMenuCash -= (float)itemCost;
common->DPrintf( "Player %s just bought item %s; player now has %d (%g) credits, cost was %d\n", playerName, itemName, (int)buyMenuCash, buyMenuCash, itemCost );
// Team-based effects
idStr itemNameStr = itemName;
if ( itemNameStr == "ammo_regen" || itemNameStr == "health_regen" || itemNameStr == "damage_boost" ) {
return AttemptToBuyTeamPowerup(itemName);
}
GiveStuffToPlayer( this, itemName, NULL );
gameLocal.mpGame.RedrawLocalBuyMenu();
return true;
}
bool idPlayer::CanBuy( void ) {
bool ret = gameLocal.mpGame.IsBuyingAllowedRightNow();
if ( !ret ) {
return false;
}
return !spectating;
}
void idPlayer::GenerateImpulseForBuyAttempt( const char* itemName ) {
if ( !CanBuy() )
return;
int itemBuyImpulse = GetItemBuyImpulse( itemName );
PerformImpulse( itemBuyImpulse );
}
// RITUAL END
/*
==============
idPlayer::PerformImpulse
==============
*/
void idPlayer::PerformImpulse( int impulse ) {
if ( gameLocal.isClient ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
assert( IsLocalClient() );
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.BeginWriting();
msg.WriteBits( impulse, IMPULSE_NUMBER_OF_BITS );
ClientSendEvent( EVENT_IMPULSE, &msg );
}
if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 ) {
SelectWeapon( impulse, false );
return;
}
switch( impulse ) {
case IMPULSE_13: {
Reload();
break;
}
case IMPULSE_14: {
NextWeapon();
if( gameLocal.isServer && spectating && gameLocal.gameType == GAME_TOURNEY ) {
((rvTourneyGameState*)gameLocal.mpGame.GetGameState())->SpectateCycleNext( this );
}
break;
}
case IMPULSE_15: {
PrevWeapon();
if( gameLocal.isServer && spectating && gameLocal.gameType == GAME_TOURNEY ) {
((rvTourneyGameState*)gameLocal.mpGame.GetGameState())->SpectateCyclePrev( this );
}
break;
}
case IMPULSE_17: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.ToggleReady( );
}
break;
}
case IMPULSE_18: {
centerView.Init(gameLocal.time, 200, viewAngles.pitch, 0);
break;
}
case IMPULSE_19: {
/*
// when we're not in single player, IMPULSE_19 is used for showScores
// otherwise it does IMPULSE_12 (PDA)
if ( !gameLocal.isMultiplayer ) {
if ( !objectiveSystemOpen ) {
if ( weapon ) {
weapon->Hide ();
}
}
ToggleMap();
}
*/
break;
}
case IMPULSE_20: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.ToggleTeam( );
}
break;
}
case IMPULSE_21: {
if( gameLocal.isServer && gameLocal.gameType == GAME_TOURNEY ) {
// only allow a client to join the waiting arena if they are not currently assigned to an arena
// removed waiting arena functionality for now
/*rvTourneyArena& arena = ((rvTourneyGameState*)gameLocal.mpGame.GetGameState())->GetArena( GetArena() );
if( this != arena.GetPlayers()[ 0 ] && this != arena.GetPlayers()[ 1 ] ) {
if( instance == MAX_ARENAS && !spectating ) {
ServerSpectate( true );
JoinInstance( ((rvTourneyGameState*)gameLocal.mpGame.GetGameState())->GetNextActiveArena( 0 ) );
} else if( spectating ) {
JoinInstance( MAX_ARENAS );
ServerSpectate( false );
}
}*/
}
break;
}
case IMPULSE_22: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.ToggleSpectate( );
}
break;
}
case IMPULSE_28: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.CastVote( gameLocal.localClientNum, true );
}
break;
}
case IMPULSE_29: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.CastVote( gameLocal.localClientNum, false );
}
break;
}
case IMPULSE_40: {
idFuncRadioChatter::RepeatLast();
break;
}
// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
case IMPULSE_100: AttemptToBuyItem( "weapon_shotgun" ); break;
case IMPULSE_101: AttemptToBuyItem( "weapon_machinegun" ); break;
case IMPULSE_102: AttemptToBuyItem( "weapon_hyperblaster" ); break;
case IMPULSE_103: AttemptToBuyItem( "weapon_grenadelauncher" ); break;
case IMPULSE_104: AttemptToBuyItem( "weapon_nailgun" ); break;
case IMPULSE_105: AttemptToBuyItem( "weapon_rocketlauncher" ); break;
case IMPULSE_106: AttemptToBuyItem( "weapon_railgun" ); break;
case IMPULSE_107: AttemptToBuyItem( "weapon_lightninggun" ); break;
case IMPULSE_108: break; // Unused
case IMPULSE_109: AttemptToBuyItem( "weapon_napalmgun" ); break;
case IMPULSE_110: /* AttemptToBuyItem( "weapon_dmg" );*/ break;
case IMPULSE_111: break; // Unused
case IMPULSE_112: break; // Unused
case IMPULSE_113: break; // Unused
case IMPULSE_114: break; // Unused
case IMPULSE_115: break; // Unused
case IMPULSE_116: break; // Unused
case IMPULSE_117: break; // Unused
case IMPULSE_118: AttemptToBuyItem( "item_armor_small" ); break;
case IMPULSE_119: AttemptToBuyItem( "item_armor_large" ); break;
case IMPULSE_120: AttemptToBuyItem( "ammorefill" ); break;
case IMPULSE_121: break; // Unused
case IMPULSE_122: break; // Unused
case IMPULSE_123: AttemptToBuyItem( "ammo_regen" ); break;
case IMPULSE_124: AttemptToBuyItem( "health_regen" ); break;
case IMPULSE_125: AttemptToBuyItem( "damage_boost" ); break;
case IMPULSE_126: break; // Unused
case IMPULSE_127: break; // Unused
// RITUAL END
case IMPULSE_50: {
ToggleFlashlight ( );
break;
}
case IMPULSE_51: {
LastWeapon();
break;
}
}
//RAVEN BEGIN
//asalmon: route d-pad input to the active gui.
#ifdef _XBOX
if (ui && ev.evValue != 0 && !objectiveSystemOpen ) {
command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
if ( updateVisuals && focusEnt && ui == focusUI ) {
focusEnt->UpdateVisuals();
}
if ( gameLocal.isClient ) {
// we predict enough, but don't want to execute commands
return;
}
if ( focusEnt ) {
HandleGuiCommands( focusEnt, command );
} else {
HandleGuiCommands( this, command );
}
}
#endif
//RAVEN END
}
/*
==============
idPlayer::HandleESC
==============
*/
bool idPlayer::HandleESC( void ) {
// jdischler: Straight from the top, cinematic skipping on xenon is OFFICIALLY OUT. Too many problems with it and not enough time to properly address them.
#ifndef _XENON
if ( gameLocal.inCinematic ) {
return SkipCinematic();
}
#endif
return false;
}
/*
==============
idPlayer::HandleObjectiveInput
==============
*/
void idPlayer::HandleObjectiveInput() {
#ifdef _XENON
if ( gameLocal.inCinematic ) {
return;
}
if ( !objectiveButtonReleased ) {
if ( ( usercmd.buttons & BUTTON_SCORES ) == 0 ) {
objectiveButtonReleased = true;
}
} else {
if ( ( usercmd.buttons & BUTTON_SCORES ) != 0 ) {
ToggleObjectives ( );
g_ObjectiveSystemOpen = objectiveSystemOpen;
objectiveButtonReleased = false;
}
}
#else
if ( ( usercmd.buttons & BUTTON_SCORES ) != 0 && !objectiveSystemOpen && !gameLocal.inCinematic ) {
ToggleObjectives ( );
} else if ( ( usercmd.buttons & BUTTON_SCORES ) == 0 && objectiveSystemOpen ) {
ToggleObjectives ( );
} else if ( objectiveSystemOpen && gameLocal.inCinematic ) {
ToggleObjectives ( );
}
#endif
}
/*
==============
idPlayer::EvaluateControls
==============
*/
void idPlayer::EvaluateControls( void ) {
// check for respawning
if ( pfl.dead || pfl.objectiveFailed ) {
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
if( allowedToRespawn ) {
if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
forceRespawn = true;
} else if ( gameLocal.time > maxRespawnTime ) {
forceRespawn = true;
}
}
else
{
Spectate(true);
}
// RITUAL END
}
// in MP, idMultiplayerGame decides spawns
if ( forceRespawn && !gameLocal.isMultiplayer && !g_testDeath.GetBool() ) {
// in single player, we let the session handle restarting the level or loading a game
gameLocal.sessionCommand = "died";
}
if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
assert( false ); // unused in multiplayer?
PerformImpulse( usercmd.impulse );
}
if ( forceScoreBoard && forceScoreBoardTime && gameLocal.time > forceScoreBoardTime ) {
forceScoreBoardTime = 0;
forceScoreBoard = false;
}
scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
oldFlags = usercmd.flags;
AdjustSpeed();
// update the viewangles
UpdateViewAngles();
}
/*
==============
idPlayer::AdjustSpeed
==============
*/
void idPlayer::AdjustSpeed( void ) {
float speed;
if ( spectating ) {
speed = pm_spectatespeed.GetFloat();
bobFrac = 0.0f;
} else if ( noclip ) {
speed = pm_noclipspeed.GetFloat();
bobFrac = 0.0f;
} else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && ( usercmd.upmove >= 0 ) ) {
bobFrac = 1.0f;
speed = pm_speed.GetFloat();
} else {
speed = pm_walkspeed.GetFloat();
bobFrac = 0.0f;
}
speed *= PowerUpModifier(PMOD_SPEED);
if ( influenceActive == INFLUENCE_LEVEL3 ) {
speed *= 0.33f;
}
physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
}
/*
==============
idPlayer::AdjustBodyAngles
==============
*/
void idPlayer::AdjustBodyAngles( void ) {
idMat3 lookAxis;
idMat3 legsAxis;
bool blend;
float diff;
float upBlend;
float forwardBlend;
float downBlend;
if ( health < 0 ) {
return;
}
blend = true;
if ( !physicsObj.HasGroundContacts() ) {
idealLegsYaw = 0.0f;
legsForward = true;
} else if ( usercmd.forwardmove < 0 ) {
idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
legsForward = false;
} else if ( usercmd.forwardmove > 0 ) {
idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
legsForward = true;
} else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
if ( !legsForward ) {
idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
} else {
idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
}
} else if ( usercmd.rightmove != 0 ) {
idealLegsYaw = 0.0f;
legsForward = true;
} else {
legsForward = true;
diff = idMath::Fabs( idealLegsYaw - legsYaw );
idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
if ( diff < 0.1f ) {
legsYaw = idealLegsYaw;
blend = false;
}
}
if ( !physicsObj.IsCrouching() ) {
legsForward = true;
}
oldViewYaw = viewAngles.yaw;
pfl.turnLeft = false;
pfl.turnRight = false;
if ( idealLegsYaw < -45.0f ) {
idealLegsYaw = 0;
pfl.turnRight = true;
blend = true;
} else if ( idealLegsYaw > 45.0f ) {
idealLegsYaw = 0;
pfl.turnLeft = true;
blend = true;
}
if ( blend ) {
legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
}
legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
// ==============================================================================================
// leaning - orient parts of the body towards the movement direction
// we control the chest, neck and hip orientations
float leanGunCorrection = 0.0f;
if ( g_playerLean.GetFloat() != 0.0f ) {
idVec3 velocity = physicsObj.GetLinearVelocity();
velocity.z = 0.0f;
// clamp and scale max speed to unit velocity
float horizSpeed = velocity.Normalize();
if ( horizSpeed > leanMaxSpeed ) {
horizSpeed = leanMaxSpeed;
}
velocity *= horizSpeed / leanMaxSpeed;
// now express this velocity in the view's axis base
velocity *= viewAxis.Transpose();
// blend
leanVelocity = velocity * leanBlendRatio + leanVelocity * ( 1.0f - leanBlendRatio );
// build the rotation
float t = fabs( leanVelocity.x ) + fabs( leanVelocity.y );
if ( t > 1e-3f ) {
float maxAngle = ( leanMaxForwardAngle * fabs( leanVelocity.x ) + leanMaxLateralAngle * fabs( leanVelocity.y ) ) / t;
maxAngle *= g_playerLean.GetFloat();
idVec3 rotate;
rotate.Cross( leanVelocity, idVec3( 0.0f, 0.0f, 1.0f ) );
float chestAngle = maxAngle * rotate.Normalize(); // doing the blend on the vectors, but idRotation wants a normalized rotation
idRotation chestRotate( vec3_origin, rotate, chestAngle );
animator.SetJointAxis( chestJoint, JOINTMOD_WORLD, chestRotate.ToMat3() );
// extract a rotation angle in the forward plane, this is used to compensate the gun later
idVec3 p( 1.0f, 0.0f, 0.0f );
chestRotate.RotatePoint( p );
leanGunCorrection = RAD2DEG( idMath::ATan( p.z, p.x ) );
float headAngle = chestAngle * leanHeadRatio;
idRotation neckRotate( vec3_origin, rotate, headAngle );
animator.SetJointAxis( neckLeanJoint, JOINTMOD_WORLD, neckRotate.ToMat3() );
float hipAngle = chestAngle * leanHipRatio;
idRotation hipRotate( vec3_origin, rotate, hipAngle );
animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, hipRotate.ToMat3() );
} else {
leanVelocity = vec3_origin;
leanGunCorrection = 0.0f;
}
}
// ==============================================================================================
// calculate the blending between down, straight, and up. account for the chest lean
float leanFrac = idMath::ClampFloat( -1.0f, 1.0f, ( viewAngles.pitch + leanGunCorrection ) / 90.0f );
if ( leanFrac > 0.0f ) {
downBlend = leanFrac;
forwardBlend = 1.0f - leanFrac;
upBlend = 0.0f;
} else {
downBlend = 0.0f;
forwardBlend = 1.0f + leanFrac;
upBlend = -leanFrac;
}
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
}
/*
==============
idPlayer::InitAASLocation
==============
*/
void idPlayer::InitAASLocation( void ) {
int i;
int num;
idVec3 size;
idBounds bounds;
idAAS *aas;
idVec3 origin;
GetFloorPos( 64.0f, origin );
num = gameLocal.NumAAS();
aasLocation.SetGranularity( 1 );
aasLocation.SetNum( num );
for( i = 0; i < aasLocation.Num(); i++ ) {
aasLocation[ i ].areaNum = 0;
aasLocation[ i ].pos = origin;
aas = gameLocal.GetAAS( i );
if ( aas && aas->GetSettings() ) {
size = aas->GetSettings()->boundingBoxes[0][1];
bounds[0] = -size;
size.z = 32.0f;
bounds[1] = size;
aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
}
}
}
/*
==============
idPlayer::SetAASLocation
==============
*/
void idPlayer::SetAASLocation( void ) {
int i;
int areaNum;
idVec3 size;
idBounds bounds;
idAAS *aas;
idVec3 origin;
if ( !GetFloorPos( 64.0f, origin ) ) {
return;
}
for( i = 0; i < aasLocation.Num(); i++ ) {
aas = gameLocal.GetAAS( i );
if ( !aas ) {
continue;
}
size = aas->GetSettings()->boundingBoxes[0][1];
bounds[0] = -size;
size.z = 32.0f;
bounds[1] = size;
areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
if ( areaNum ) {
aasLocation[ i ].pos = origin;
aasLocation[ i ].areaNum = areaNum;
}
}
}
/*
==============
idPlayer::GetAASLocation
==============
*/
void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
int i;
if ( aas != NULL ) {
for( i = 0; i < aasLocation.Num(); i++ ) {
if ( aas == gameLocal.GetAAS( i ) ) {
areaNum = aasLocation[ i ].areaNum;
pos = aasLocation[ i ].pos;
return;
}
}
}
areaNum = 0;
pos = physicsObj.GetOrigin();
}
/*
==============
idPlayer::Move
==============
*/
void idPlayer::Move( void ) {
float newEyeOffset;
idVec3 oldOrigin;
idVec3 oldVelocity;
idVec3 pushVelocity;
// save old origin and velocity for crashlanding
oldOrigin = physicsObj.GetOrigin();
oldVelocity = physicsObj.GetLinearVelocity();
pushVelocity = physicsObj.GetPushedLinearVelocity();
// set physics variables
physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
if ( noclip ) {
physicsObj.SetContents( 0 );
physicsObj.SetMovementType( PM_NOCLIP );
} else if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
physicsObj.SetContents( 0 );
physicsObj.SetMovementType( PM_SPECTATOR );
} else if ( health <= 0 ) {
physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
physicsObj.SetMovementType( PM_DEAD );
} else if ( gameLocal.inCinematic || pfl.objectiveFailed || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
physicsObj.SetContents( CONTENTS_BODY | (use_combat_bbox?CONTENTS_SOLID:0) );
physicsObj.SetMovementType( PM_FREEZE );
} else {
physicsObj.SetContents( CONTENTS_BODY | (use_combat_bbox?CONTENTS_SOLID:0) );
physicsObj.SetMovementType( PM_NORMAL );
}
if ( spectating || ( gameLocal.isClient && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() != instance ) ) {
physicsObj.SetClipMask( MASK_DEADSOLID );
} else if ( health <= 0 ) {
physicsObj.SetClipMask( MASK_DEADSOLID );
} else {
physicsObj.SetClipMask( MASK_PLAYERSOLID );
}
physicsObj.SetDebugLevel( g_debugMove.GetBool() );
physicsObj.SetPlayerInput( usercmd, viewAngles );
// FIXME: physics gets disabled somehow
BecomeActive( TH_PHYSICS );
// If the player is dead then only run physics on new
// frames since articulated figures are not synchronized over the network
if ( health <= 0 ) {
if ( gameLocal.isNewFrame ) {
DeathPush();
RunPhysics();
}
} else {
RunPhysics();
}
// update our last valid AAS location for the AI
if ( !gameLocal.isMultiplayer ) {
SetAASLocation();
}
if ( spectating ) {
newEyeOffset = 0.0f;
} else if ( health <= 0 ) {
newEyeOffset = pm_deadviewheight.GetFloat();
} else if ( physicsObj.IsCrouching() ) {
newEyeOffset = pm_crouchviewheight.GetFloat();
} else if ( IsInVehicle ( ) ) {
newEyeOffset = 0.0f;
} else {
newEyeOffset = pm_normalviewheight.GetFloat();
}
if ( EyeHeight() != newEyeOffset ) {
if ( spectating ) {
SetEyeHeight( newEyeOffset );
} else {
// smooth out duck height changes
SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
}
}
if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
pfl.crouch = false;
pfl.onGround = ( influenceActive == INFLUENCE_LEVEL2 );
pfl.onLadder = false;
pfl.jump = false;
} else {
pfl.crouch = physicsObj.IsCrouching();
pfl.onGround = physicsObj.OnGround();
pfl.onLadder = physicsObj.OnLadder();
pfl.jump = physicsObj.HasJumped();
// check if we're standing on top of a monster and give a push if we are
idEntity *groundEnt = physicsObj.GetGroundEntity();
if ( groundEnt && groundEnt->IsType( idAI::GetClassType() ) ) {
idVec3 vel = physicsObj.GetLinearVelocity();
if ( vel.ToVec2().LengthSqr() < 0.1f ) {
vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
vel.ToVec2().NormalizeFast();
vel.ToVec2() *= pm_speed.GetFloat();
} else {
// give em a push in the direction they're going
vel *= 1.1f;
}
physicsObj.SetLinearVelocity( vel );
}
}
if ( pfl.jump ) {
loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
currentLoggedAccel++;
acc->time = gameLocal.time;
acc->dir[2] = 200;
acc->dir[0] = acc->dir[1] = 0;
// PMF_JUMP can get stuck when dead, which causes some bad spamming
// a cleaner fix would be to make sure PMF_JUMP gets cleared, but this close to release, that will do
// (the AF becomes the active physics object, so physics don't run on idPlayer_Physics and that flag never gets cleared)
if ( health > 0 ) {
// don't use the sound broadcasting facility in StartSound, we only need unreliable, and we need to filter for local client prediction
if ( gameLocal.isServer || IsLocalClient() ) {
StartSound( "snd_jump", (s_channelType)FC_SOUND, 0, false, NULL );
}
if ( gameLocal.isServer ) {
idBitMsg msg;
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EVENT );
msg.WriteBits( gameLocal.GetSpawnId( this ), 32 );
msg.WriteByte( EVENT_JUMP );
gameLocal.SendUnreliableMessagePVS( msg, this, gameRenderWorld->PointInArea( physicsObj.GetOrigin() ) );
}
}
}
if ( pfl.onLadder ) {
int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
if ( old_rung != new_rung ) {
StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
}
}
UpdateIntentDir( );
BobCycle( pushVelocity );
if( !noclip ) {
CrashLand( oldOrigin, oldVelocity );
}
}
/*
==================
idPlayer::BiasIntentDir
Called when a weapon fires, generates head twitches, etc
==================
*/
void idPlayer::BiasIntentDir( idVec3 newIntentDir, float prevBias ) {
if ( !newIntentDir.Compare( vec3_origin ) ) {
if ( intentDir.Compare( vec3_origin ) ) {
//initialize it
intentDir = newIntentDir;
} else {
intentDir = ((intentDir*prevBias)+newIntentDir)/(prevBias+1.0f);
float iDirLen = idMath::ClampFloat(0.0f,1024.0f,intentDir.Normalize());
intentDir *= iDirLen;
}
}
}
/*
==============
idPlayer::UpdateIntentDir
==============
*/
void idPlayer::UpdateIntentDir( void ) {
idVec3 newIntentDir;
idVec3 viewDir = viewAxis[0];
viewDir.z = 0;
viewDir.Normalize();
float prevBias = 199.0f;
if ( intentDir.Compare( vec3_origin ) ) {
newIntentDir = viewDir*50.0f;
} else {
newIntentDir = viewDir*intentDir.Length();
if ( flashlightOn ) {
// bias them more heavily to looking where I'm looking
prevBias = 19.0f;
}
}
if ( pfl.onGround ) {
idVec3 moveDir = physicsObj.GetLinearVelocity();
if ( moveDir.x || moveDir.y ) {
// moving, too
moveDir.z = 0;
newIntentDir = moveDir;
prevBias = 39.0f;
}
}
BiasIntentDir( newIntentDir, prevBias );
if ( ai_debugSquad.GetBool() ) {
idVec4 color = colorCyan;
if ( pfl.weaponFired ) {
color = colorRed;
}
gameRenderWorld->DebugArrow( color, GetPhysics()->GetOrigin(), GetPhysics()->GetOrigin() + intentDir*4.0f, 8, 0 );
}
}
/*
==============
idPlayer::UpdateHud
==============
*/
void idPlayer::UpdateHud( void ) {
if ( !hud ) {
return;
}
if ( !IsLocalClient() ) {
return;
}
// FIXME: this is here for level ammo balancing to see pct of hits
hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
if ( numProjectilesFired ) {
hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
} else {
hud->SetStateString( "projectilepct", "Hit % 0.0" );
}
if ( isLagged && gameLocal.isMultiplayer && IsLocalClient() ) {
hud->SetStateString( "hudLag", "1" );
} else {
hud->SetStateString( "hudLag", "0" );
}
}
/*
==============
idPlayer::UpdateDeathSkin
==============
*/
void idPlayer::UpdateDeathSkin( bool state_hitch ) {
if ( !( gameLocal.isMultiplayer || g_testDeath.GetBool() ) ) {
return;
}
if ( health <= 0 ) {
if ( !doingDeathSkin && !deathSkinTime ) {
deathSkinTime = gameLocal.time + 1000;
deathStateHitch = state_hitch;
}
// wait a bit before switching off the content
if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
SetCombatContents( false );
deathClearContentsTime = 0;
}
} else {
renderEntity.noShadow = false;
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
if( gameLocal.isMultiplayer ) {
if( clientHead ) {
clientHead->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
}
} else {
if( head ) {
head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
}
}
UpdateVisuals();
doingDeathSkin = false;
deathClearContentsTime = 0;
}
}
/*
===============
idPlayer::LoadDeferredModel
===============
*/
void idPlayer::LoadDeferredModel( void ) {
if( !modelDecl ) {
gameLocal.Warning( "idPlayer::LoadDeferredModel() - reloadModel without vaid modelDict\n" );
return;
}
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
UpdateState();
if( weapon ) {
weapon->NetCatchup();
}
if( !modelDecl->skin.Length() ) {
skin = NULL;
} else {
skin = declManager->FindSkin( modelDecl->skin.c_str(), false );
}
SetModel( modelDecl->model );
if( !modelDecl->head.Length() ) {
if( clientHead ) {
delete clientHead;
clientHead = NULL;
}
} else {
SetupHead( modelDecl->head.c_str(), modelDecl->headOffset );
}
if( clientHead && health <= 0 ) {
// update death shader for new head
clientHead.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ];
clientHead.GetEntity()->GetRenderEntity()->noShadow = renderEntity.noShadow;
}
if( powerUpSkin != NULL ) {
SetSkin( powerUpSkin );
if( clientHead ) {
clientHead->SetSkin( powerUpSkin );
}
} else {
SetSkin( skin );
if( clientHead ) {
clientHead->SetSkin( headSkin );
}
}
}
/*
==============
idPlayer::Think
Called every tic for each player
==============
*/
void idPlayer::Think( void ) {
renderEntity_t *headRenderEnt;
if ( talkingNPC ) {
if ( !talkingNPC.IsValid() ) {
talkingNPC = NULL;
} else {
idAI *talkingNPCAI = (idAI*)(talkingNPC.GetEntity());
if ( !talkingNPCAI ) {
//wtf?
talkingNPC = NULL;
} else if ( talkingNPCAI->talkTarget != this || !talkingNPCAI->IsSpeaking() || DistanceTo( talkingNPCAI ) > 256.0f ) {
//forget about them, okay to talk to someone else now
talkingNPC = NULL;
}
}
}
if ( !gameLocal.usercmds ) {
return;
}
#ifdef _XENON
// change the crosshair if it's modified
if ( cursor && weapon && g_crosshairColor.IsModified() ) {
weapon->UpdateCrosshairGUI( cursor );
cursor->HandleNamedEvent( "weaponChange" );
g_crosshairColor.ClearModified();
}
#endif
// Dont do any thinking if we are in modview
if ( gameLocal.editors & EDITOR_MODVIEW || gameEdit->PlayPlayback() ) {
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPerson / camera view
CalculateRenderView();
FreeModelDef();
if ( weapon ) {
weapon->GetWorldModel()->FreeModelDef();
}
if ( head.GetEntity() ) {
head->FreeModelDef();
}
if ( clientHead ) {
clientHead->FreeEntityDef();
}
return;
}
if( reloadModel ) {
LoadDeferredModel();
reloadModel = false;
}
gameEdit->RecordPlayback( usercmd, this );
// latch button actions
oldButtons = usercmd.buttons;
// grab out usercmd
usercmd_t oldCmd = usercmd;
usercmd = gameLocal.usercmds[ IsFakeClient() ? MAX_CLIENTS : entityNumber ];
buttonMask &= usercmd.buttons;
usercmd.buttons &= ~buttonMask;
HandleObjectiveInput();
if ( objectiveSystemOpen ) {
HandleCheats();
} else {
ClearCheatState();
}
aasSensor->Update();
if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
// we need to let the camera think inside of this routine
CalculateRenderView();
return;
}
// clear the ik before we do anything else so the skeleton doesn't get updated twice
walkIK.ClearJointMods();
// if this is the very first frame of the map, set the delta view angles
// based on the usercmd angles
if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
spawnAnglesSet = true;
SetViewAngles( spawnAngles );
oldFlags = usercmd.flags;
}
if ( gameLocal.inCinematic || influenceActive
#ifdef _XENON
|| objectiveSystemOpen
#endif
) {
usercmd.forwardmove = 0;
usercmd.rightmove = 0;
usercmd.upmove = 0;
}
if( gameLocal.GetIsFrozen() && gameLocal.gameType == GAME_DEADZONE )
{
usercmd.forwardmove = 0;
usercmd.rightmove = 0;
usercmd.upmove = 0;
}
// zooming
bool zoom = (usercmd.buttons & BUTTON_ZOOM) && CanZoom();
if ( zoom != zoomed ) {
if ( zoom ) {
ProcessEvent ( &EV_Player_ZoomIn );
} else {
ProcessEvent ( &EV_Player_ZoomOut );
}
if ( vehicleController.IsDriving( ) ) {
#ifdef _XENON
usercmdGen->SetSlowJoystick( zoom ? pm_zoomedSlow.GetInteger() : 100 );
#else
cvarSystem->SetCVarInteger( "pm_isZoomed", zoom ? pm_zoomedSlow.GetInteger() : 0 );
#endif
}
}
if ( IsInVehicle ( ) ) {
vehicleController.SetInput ( usercmd, viewAngles );
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPeoson / camera view
CalculateRenderView();
thinkFlags |= TH_PHYSICS;
RunPhysics();
if ( health > 0 ) {
TouchTriggers();
}
UpdateLocation();
if ( !fl.hidden ) {
UpdateAnimation();
Present();
LinkCombat();
} else {
UpdateModel();
}
// I don't want to have to add this but if you are in a locked vehicle and you fail your objective, you won't be
// ejected from the vehicle (and I don't think we'd want that even though we do have the option of forcing it..)
// and since you are still in the vehicle, EvaluateControls (which covers the logic below for player usercmds)
// will never get called.
if ( pfl.objectiveFailed )
{
if ( ( gameLocal.time > minRespawnTime && (usercmd.buttons & BUTTON_ATTACK)) ||
gameLocal.time > maxRespawnTime )
{
gameLocal.sessionCommand = "died";
}
}
return;
}
// log movement changes for weapon bobbing effects
if ( usercmd.forwardmove != oldCmd.forwardmove ) {
loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
currentLoggedAccel++;
acc->time = gameLocal.time;
acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
acc->dir[1] = acc->dir[2] = 0;
}
if ( usercmd.rightmove != oldCmd.rightmove ) {
loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
currentLoggedAccel++;
acc->time = gameLocal.time;
acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
acc->dir[0] = acc->dir[2] = 0;
}
// freelook centering
if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_MLOOK ) {
centerView.Init( gameLocal.time, 200, viewAngles.pitch, 0 );
}
// if we have an active gui, we will unrotate the view angles as
// we turn the mouse movements into gui events
idUserInterface *gui = ActiveGui();
if ( gui && gui != focusUI ) {
RouteGuiMouse( gui );
}
// set the push velocity on the weapon before running the physics
if ( weapon ) {
weapon->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
}
EvaluateControls();
// RAVEN BEGIN
// abahr
if( !noclip && !spectating ) {
UpdateGravity();
}
// RAVEN END
Move();
if ( !g_stopTime.GetBool() ) {
if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
TouchTriggers();
} else if ( spectating && !noclip ) {
TouchTriggers( &idTrigger_Multi::GetClassType() );
}
// not done on clients for various reasons. don't do it on server and save the sound channel for other things
if ( !gameLocal.isMultiplayer ) {
if ( g_useDynamicProtection.GetBool() && dynamicProtectionScale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
if ( dynamicProtectionScale < 1.0f ) {
dynamicProtectionScale += 0.05f;
}
if ( dynamicProtectionScale > 1.0f ) {
dynamicProtectionScale = 1.0f;
}
}
}
// update GUIs, Items, and character interactions
UpdateFocus();
UpdateLocation();
// update player script
UpdateState();
// service animations
if ( !spectating && !af.IsActive() ) {
UpdateConditions();
UpdateAnimState();
CheckBlink();
}
// clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
pfl.pain = false;
}
if ( !af.IsActive() ) {
AdjustBodyAngles();
}
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPeroson / camera view
CalculateRenderView();
if ( spectating ) {
UpdateSpectating();
} else if ( health > 0 && !gameLocal.inCinematic ) {
UpdateWeapon();
}
UpdateAir();
UpdateHud();
UpdatePowerUps();
UpdateDeathSkin( false );
UpdateDeathShader( deathStateHitch );
if( gameLocal.isMultiplayer ) {
if( clientHead.GetEntity() ) {
headRenderEnt = clientHead.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
} else {
if ( head.GetEntity() ) {
headRenderEnt = head.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
}
if ( headRenderEnt ) {
if ( powerUpSkin ) {
headRenderEnt->customSkin = powerUpSkin;
} else if ( influenceSkin ) {
headRenderEnt->customSkin = influenceSkin;
} else {
headRenderEnt->customSkin = headSkin;
}
headRenderEnt->suppressSurfaceInViewID = entityNumber + 1;
}
// always show your own shadow
if( entityNumber == gameLocal.localClientNum ) {
renderEntity.suppressLOD = 1;
if( headRenderEnt ) {
headRenderEnt->suppressLOD = 1;
}
} else {
renderEntity.suppressLOD = 0;
if( headRenderEnt ) {
headRenderEnt->suppressLOD = 0;
}
}
DrawShadow( headRenderEnt );
// never cast shadows from our first-person muzzle flashes
// FIXME: get first person flashlight into this
renderEntity.suppressShadowInLightID = rvWeapon::WPLIGHT_MUZZLEFLASH * 100 + entityNumber;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInLightID = rvWeapon::WPLIGHT_MUZZLEFLASH * 100 + entityNumber;
}
if ( !g_stopTime.GetBool() ) {
UpdateAnimation();
Present();
LinkCombat();
}
if ( !( thinkFlags & TH_THINK ) ) {
common->DPrintf( "player %d not thinking?\n", entityNumber );
}
if ( g_showEnemies.GetBool() ) {
idActor *ent;
int num = 0;
for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
common->DPrintf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
num++;
}
common->DPrintf( "%d: enemies\n", num );
}
if ( !inBuyZonePrev )
inBuyZone = false;
inBuyZonePrev = false;
}
/*
=================
idPlayer::RouteGuiMouse
=================
*/
void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
sysEvent_t ev;
const char *command;
if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
command = gui->HandleEvent( &ev, gameLocal.time );
oldMouseX = usercmd.mx;
oldMouseY = usercmd.my;
}
}
bool idPlayer::CanZoom( void )
{
if ( vehicleController.IsDriving() ) {
rvVehicle * vehicle = vehicleController.GetVehicle();
rvVehiclePosition * position = vehicle ? vehicle->GetPosition( vehicleController.GetPosition() ) : 0;
rvVehicleWeapon * weapon = position ? position->GetActiveWeapon() : 0;
return weapon && weapon->CanZoom();
}
return weapon && weapon->CanZoom() && !weapon->IsReloading ( );
}
/*
==================
idPlayer::LookAtKiller
==================
*/
void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
idVec3 dir;
if ( attacker && attacker != this ) {
dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
} else if ( inflictor && inflictor != this ) {
dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
} else {
dir = viewAxis[ 0 ];
}
idAngles ang( 0, dir.ToYaw(), 0 );
SetViewAngles( ang );
}
/*
==============
idPlayer::Kill
==============
*/
void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
if ( spectating ) {
SpectateFreeFly( false );
} else if ( health > 0 ) {
godmode = false;
if ( !g_forceUndying.GetBool() ) {
undying = false;
}
if ( nodamage ) {
ServerSpectate( true );
forceRespawn = true;
} else {
Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
if ( delayRespawn ) {
forceRespawn = false;
int delay = spawnArgs.GetFloat( "respawn_delay" );
minRespawnTime = gameLocal.time + SEC2MS( delay );
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
}
}
}
}
/*
==================
idPlayer::Killed
==================
*/
void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
float delay;
assert( !gameLocal.isClient );
// stop taking knockback once dead
fl.noknockback = true;
if ( health < -999 ) {
health = -999;
}
if ( pfl.dead ) {
pfl.pain = true;
return;
}
// squirrel: Mode-agnostic buymenus
if ( gameLocal.isMultiplayer ) {
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) {
if ( gameLocal.mpGame.GetGameState()->GetMPGameState() != WARMUP ) {
/// Remove the player's armor
inventory.armor = 0;
/// Preserve this player's weapons at the state of his death, to be restored on respawn
carryOverCurrentWeapon = currentWeapon;
inventory.carryOverWeapons = inventory.weapons;
if ( attacker ) {
idPlayer* killer = NULL;
if ( attacker->IsType( idPlayer::Type ) ) {
killer = static_cast<idPlayer*>(attacker);
if ( killer == this ) {
// Killed by self
float cashAward = (float) gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_killingSelf", 0 );
killer->GiveCash( cashAward );
}
else if ( gameLocal.IsTeamGame() && killer->team == team ) {
// Killed by teammate
float cashAward = (float) gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_killingTeammate", 0 );
killer->GiveCash( cashAward );
} else {
// Killed by enemy
float cashAward = (float) gameLocal.mpGame.mpBuyingManager.GetOpponentKillCashAward();
killer->GiveCash( cashAward );
}
}
}
}
}
}
// RITUAL END
bool noDrop = false;
if ( inflictor
&& inflictor->IsType( idTrigger_Hurt::GetClassType() )
&& inflictor->spawnArgs.GetBool( "nodrop" ) ) {
//don't drop weapon or items here, flag auto-returns.
noDrop = true;
}
if ( !g_testDeath.GetBool() && !gameLocal.isMultiplayer ) {
#ifdef _XENON
playerView.Fade( colorBlack, MAX_RESPAWN_TIME_XEN_SP );
#else
playerView.Fade( colorBlack, 12000 );
#endif
}
pfl.dead = true;
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Dead", 4 );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Dead", 4 );
animator.ClearAllJoints();
if ( StartRagdoll() ) {
pm_modelView.SetInteger( 0 );
minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
} else {
// don't allow respawn until the death anim is done
// g_forcerespawn may force spawning at some later time
delay = spawnArgs.GetFloat( "respawn_delay" );
minRespawnTime = gameLocal.time + SEC2MS( delay );
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
}
physicsObj.SetMovementType( PM_DEAD );
StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
StopSound( SND_CHANNEL_BODY2, false );
fl.takedamage = true; // can still be gibbed
if ( weapon ) { // cnicholson: Fix for crash if player dies while in vehicle
weapon->OwnerDied(); // get rid of weapon
if ( !noDrop ) {
DropWeapon( ); // drop the weapon as an item
}
delete weapon;
}
weapon = NULL;
currentWeapon = -1;
if ( !g_testDeath.GetBool() ) {
LookAtKiller( inflictor, attacker );
}
if ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) {
idPlayer *killer = NULL;
int methodOfDeath = MAX_WEAPONS + isTelefragged;
if ( attacker->IsType( idPlayer::Type ) ) {
killer = static_cast<idPlayer*>(attacker);
lastKiller = killer;
if ( gameLocal.IsTeamGame() && killer->team == team ) {
// don't worry about team killers
lastKiller = NULL;
}
if ( killer == this ) {
// don't worry about yourself
lastKiller = NULL;
}
if ( health < -20 || killer->PowerUpActive( POWERUP_QUADDAMAGE ) ) {
gibDeath = true;
gibDir = dir;
gibsLaunched = false;
if( gameLocal.isMultiplayer && gameLocal.isListenServer && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetInstance() == instance ) {
ClientGib( dir );
}
}
if ( !isTelefragged ) {
if ( inflictor->IsType( idProjectile::GetClassType() ) ) {
methodOfDeath = static_cast<idProjectile*>(inflictor)->methodOfDeath;
} else if ( inflictor->IsType( idPlayer::Type ) ) {
// hitscan weapon
methodOfDeath = static_cast<idPlayer*>(inflictor)->GetCurrentWeapon();
}
}
if ( methodOfDeath == -1 ) {
methodOfDeath = MAX_WEAPONS + isTelefragged;
}
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
} else if ( attacker->IsType( idWorldspawn::GetClassType() ) ) {
// RAVEN END
if ( lastImpulseTime > gameLocal.time && lastImpulsePlayer ) {
killer = lastImpulsePlayer;
}
}
gameLocal.mpGame.PlayerDeath( this, killer, methodOfDeath );
} else {
physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
}
if ( gameLocal.isMultiplayer && gameLocal.IsFlagGameType() ) {
if ( PowerUpActive( POWERUP_CTF_MARINEFLAG ) ) {
RemoveClientModel( "mp_ctf_flag_pole" );
RemoveClientModel( "mp_ctf_marine_flag_world" );
StopEffect( "fx_ctf_marine_flag_world" );
} else if ( PowerUpActive( POWERUP_CTF_STROGGFLAG ) ) {
RemoveClientModel( "mp_ctf_flag_pole" );
RemoveClientModel( "mp_ctf_strogg_flag_world" );
StopEffect( "fx_ctf_strogg_flag_world" );
} else if ( PowerUpActive( POWERUP_CTF_ONEFLAG ) ) {
RemoveClientModel( "mp_ctf_one_flag" );
}
if( PowerUpActive( POWERUP_CTF_STROGGFLAG ) || PowerUpActive( POWERUP_CTF_MARINEFLAG ) || PowerUpActive( POWERUP_CTF_ONEFLAG ) ) {
idPlayer* killer = (idPlayer*)attacker;
if ( killer != NULL && killer->IsType( idPlayer::GetClassType() ) && killer != this ) {
if ( killer->team != team ) {
// killing the flag carrier gives killer double cash
killer->GiveCash( (float) gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_killingOpponentFlagCarrier", 0 ) );
}
}
statManager->FlagDropped( this, attacker );
}
}
DropPowerups();
ClearPowerUps();
UpdateVisuals();
// AI sometimes needs to respond to having killed someone.
// Note: Would it be better to make this a virtual funciton of... something?
aiManager.RemoveTeammate( this );
isChatting = false;
}
/*
=================
CalcDamagePoints
Calculates how many health and armor points will be inflicted, but
doesn't actually do anything with them. This is used to tell when an attack
would have killed the player, possibly allowing a "saving throw"
=================
*/
void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
const float damageScale, const int location, int *health, int *armor ) {
int damage;
int armorSave;
float pDmgScale;
damageDef->GetInt( "damage", "20", damage );
damage = GetDamageForLocation( damage, location );
// optional different damage in team games
if ( gameLocal.isMultiplayer && gameLocal.IsTeamGame() && damageDef->GetInt( "damage_team" ) ) {
damage = damageDef->GetInt( "damage_team" );
}
idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
if ( !gameLocal.isMultiplayer ) {
if ( inflictor != gameLocal.world ) {
switch ( g_skill.GetInteger() ) {
case 0:
damage = ceil(0.80f*(float)damage);
break;
case 2:
damage *= 1.7f;
break;
case 3:
damage *= 3.5f;
break;
default:
//damage *= 1.1f; reverted to 1.0 for default damage... as per Biessman's request.
break;
}
}
}
damage = ceil(damageScale*(float)damage);
pDmgScale = damageDef->GetFloat( "playerScale", "1" );
damage = ceil(pDmgScale*(float)damage);
// check for completely getting out of the damage
if ( !damageDef->GetBool( "noGod" ) ) {
// check for godmode
if ( godmode ) {
godmodeDamage += damage;
damage = 0;
}
}
// reduce damage by handicap, except to self
if( player && player != this ) {
damage = ceil(player->handicap*(float)damage);
}
// save some from armor
if ( !damageDef->GetBool( "noArmor" ) ) {
float armor_protection;
armor_protection = ( gameLocal.isMultiplayer ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
armorSave = ceil( damage * armor_protection );
if ( armorSave >= inventory.armor ) {
armorSave = inventory.armor;
}
if ( !damage ) {
armorSave = 0;
} else if ( armorSave >= damage ) {
armorSave = damage - 1;
damage = 1;
} else {
damage -= armorSave;
}
} else {
armorSave = 0;
}
// check for team damage
if ( gameLocal.IsTeamGame()
&& !gameLocal.serverInfo.GetBool( "si_teamDamage" )
&& !damageDef->GetBool( "noTeam" )
&& player
&& player != this // you get self damage no matter what
&& player->team == team ) {
damage = 0;
}
*health = damage;
*armor = armorSave;
}
/*
============
Damage
this entity that is being damaged
inflictor entity that is causing the damage
attacker entity that caused the inflictor to damage targ
example: this=monster, inflictor=rocket, attacker=player
dir direction of the attack for knockback in global space
damageDef an idDict with all the options for damage effects
inflictor, attacker, dir, and point can be NULL for environmental effects
============
*/
void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
const char *damageDefName, const float damageScale, int location ) {
idVec3 kick;
int damage;
int armorSave;
int knockback;
idVec3 damage_from;
float attackerPushScale;
float modifiedDamageScale = damageScale;
if ( !gameLocal.isMultiplayer ) {
if ( inflictor != gameLocal.world ) {
modifiedDamageScale *= ( 1.0f + gameLocal.GetDifficultyModifier() );
}
}
if ( forwardDamageEnt.IsValid() ) {
forwardDamageEnt->Damage( inflictor, attacker, dir, damageDefName, modifiedDamageScale, location );
return;
}
// damage is only processed on server
if ( gameLocal.isClient ) {
return;
}
if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
// If in vehicle let it know that something is trying to hurt the invisible player
if ( IsInVehicle ( ) ) {
const idDict *damageDict = gameLocal.FindEntityDefDict( damageDefName, false );
if ( !damageDict ) {
gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
return;
}
// If the damage def is marked as a hazard then issue a warning to the vehicle
if ( damageDict->GetBool ( "hazard", "0" ) ) {
vehicleController.GetVehicle()->IssueHazardWarning ( );
}
}
return;
}
if ( !inflictor ) {
inflictor = gameLocal.world;
}
if ( !attacker ) {
attacker = gameLocal.world;
}
// MCG: player doesn't take friendly fire damage, except from self!
if ( !gameLocal.isMultiplayer && attacker != this ) {
if ( attacker->IsType ( idActor::GetClassType() ) && static_cast<idActor*>(attacker)->team == team ) {
return;
}
}
const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( !damageDef ) {
gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
return;
}
if ( damageDef->dict.GetBool( "ignore_player" ) ) {
return;
}
if ( damageDef->dict.GetBool( "lightning_damage_effect" ) ) {
lightningEffects = 0;
lightningNextTime = gameLocal.GetTime();
}
// We pass in damageScale, because this function calculates a modified damageScale
// based on g_skill, and we don't want to compensate for skill level twice.
CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
//
// determine knockback
//
damageDef->dict.GetInt( "knockback", "0", knockback );
if( gameLocal.isMultiplayer && gameLocal.IsTeamGame() ) {
damageDef->dict.GetInt( "knockback_team", va( "%d", knockback ), knockback );
}
knockback *= damageScale;
if ( knockback != 0 && !fl.noknockback ) {
if ( !gameLocal.isMultiplayer && attacker == this ) {
//In SP, no knockback from your own stuff
knockback = 0;
} else {
if ( attacker != this ) {
attackerPushScale = 1.0f;
} else {
// since default attackerDamageScale is 0.5, default attackerPushScale should be 2
damageDef->dict.GetFloat( "attackerPushScale", "2", attackerPushScale );
}
kick = dir;
kick.Normalize();
kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
// set the timer so that the player can't cancel out the movement immediately
physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
}
}
if ( damageDef->dict.GetBool( "burn" ) ) {
StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
} else if ( damageDef->dict.GetBool( "no_air" ) ) {
if ( !armorSave && health > 0 ) {
StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
}
}
// give feedback on the player view and audibly when armor is helping
inventory.armor -= armorSave;
if ( g_debugDamage.GetInteger() ) {
gameLocal.Printf( "client:%i health:%i damage:%i armor:%i\n",
entityNumber, health, damage, armorSave );
}
// move the world direction vector to local coordinates
ClientDamageEffects( damageDef->dict, dir, damage );
// inform the attacker that they hit someone
attacker->DamageFeedback( this, inflictor, damage );
if( gameLocal.isMultiplayer ) {
idEntity* attacker = NULL;
int methodOfDeath = -1;
if ( inflictor->IsType( idProjectile::GetClassType() ) ) {
methodOfDeath = static_cast<idProjectile*>(inflictor)->methodOfDeath;
attacker = static_cast<idProjectile*>(inflictor)->GetOwner();
} else if ( inflictor->IsType( idPlayer::Type ) ) {
// hitscan weapon
methodOfDeath = static_cast<idPlayer*>(inflictor)->GetCurrentWeapon();
attacker = inflictor;
}
statManager->Damage( attacker, this, methodOfDeath, damage );
}
// RAVEN BEGIN
// MCG - added damage over time
if ( !inDamageEvent ) {
if ( damageDef->dict.GetFloat( "dot_duration" ) ) {
int endTime;
if ( damageDef->dict.GetFloat( "dot_duration" ) == -1 ) {
endTime = -1;
} else {
endTime = gameLocal.GetTime() + SEC2MS(damageDef->dict.GetFloat( "dot_duration" ));
}
int interval = SEC2MS(damageDef->dict.GetFloat( "dot_interval", "0" ));
if ( endTime == -1 || gameLocal.GetTime() + interval <= endTime ) {//post it again
PostEventMS( &EV_DamageOverTime, interval, endTime, interval, inflictor, attacker, dir, damageDefName, damageScale, location );
}
if ( damageDef->dict.GetString( "fx_dot", NULL ) ) {
ProcessEvent( &EV_DamageOverTimeEffect, endTime, interval, damageDefName );
}
if ( damageDef->dict.GetString( "snd_dot_start", NULL ) ) {
StartSound ( "snd_dot_start", SND_CHANNEL_ANY, 0, false, NULL );
}
}
}
// RAVEN END
// do the damage
if ( damage > 0 ) {
if ( !gameLocal.isMultiplayer ) {
if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
if ( gameLocal.time > lastDmgTime + 500 && dynamicProtectionScale > 0.25f ) {
dynamicProtectionScale -= 0.05f;
}
}
if ( dynamicProtectionScale > 0.0f ) {
damage *= dynamicProtectionScale;
}
}
if ( damage < 1 ) {
damage = 1;
}
int oldHealth = health;
health -= damage;
GAMELOG_ADD ( va("player%d_damage_taken", entityNumber ), damage );
GAMELOG_ADD ( va("player%d_damage_%s", entityNumber, damageDefName), damage );
// Check undying mode
if ( !damageDef->dict.GetBool( "noGod" ) ) {
if ( undying ) {
if ( health < 1 ) {
health = 1;
}
}
}
if ( health <= 0 ) {
if ( health < -999 ) {
health = -999;
}
isTelefragged = damageDef->dict.GetBool( "telefrag" );
lastDmgTime = gameLocal.time;
Killed( inflictor, attacker, damage, dir, location );
if ( oldHealth > 0 ) {
float pushScale = 1.0f;
if ( inflictor && inflictor->IsType ( idPlayer::Type ) ) {
pushScale = static_cast<idPlayer*>(inflictor)->PowerUpModifier ( PMOD_PROJECTILE_DEATHPUSH );
}
InitDeathPush ( dir, location, &damageDef->dict, pushScale );
}
} else {
// force a blink
blink_time = 0;
// let the anim script know we took damage
pfl.pain = Pain( inflictor, attacker, damage, dir, location );
if ( !g_testDeath.GetBool() ) {
lastDmgTime = gameLocal.time;
}
}
} else {
// don't accumulate impulses
if ( af.IsLoaded() ) {
// clear impacts
af.Rest();
// physics is turned off by calling af.Rest()
BecomeActive( TH_PHYSICS );
}
}
lastDamageDir = dir;
lastDamageDir.Normalize();
lastDamageDef = damageDef->Index();
lastDamageLocation = location;
}
/*
=====================
idPlayer::CanPlayImpactEffect
=====================
*/
bool idPlayer::CanPlayImpactEffect( idEntity* attacker, idEntity* target )
{
if ( !gameLocal.isMultiplayer && attacker->IsType( idAI::GetClassType()) && target->IsType( idPlayer::GetClassType()))
{
// don't display impact effects when marines on our team shoot us...
idPlayer *player = static_cast<idPlayer *>( target );
idAI *ai = static_cast<idAI *>( attacker );
if ( player->team == ai->team )
{
return false;
}
}
return idAFEntity_Base::CanPlayImpactEffect( attacker, target );
}
/*
=====================
idPlayer::AddDamageEffect
=====================
*/
void idPlayer::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) {
if( gameLocal.isMultiplayer ) {
if( ! cvarSystem->GetCVarBool("si_teamDamage") && inflictor && inflictor->IsType( idPlayer::GetClassType() ) && gameLocal.IsTeamGame() && ((idPlayer*)inflictor)->team == team ) {
return;
}
}
idActor::AddDamageEffect ( collision, velocity, damageDefName, inflictor );
}
/*
===========
idPlayer::Teleport
============
*/
void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
idVec3 org;
if ( weapon ) {
weapon->LowerWeapon();
}
if ( !spectating ) {
SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
} else {
SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) + idVec3( 0, 0, SPECTATE_RAISE ) );
}
if ( !gameLocal.isMultiplayer && GetFloorPos( 16.0f, org ) ) {
SetOrigin( org );
}
// clear the ik heights so model doesn't appear in the wrong place
walkIK.EnableAll();
GetPhysics()->SetLinearVelocity( vec3_origin );
SetViewAngles( angles );
legsYaw = 0.0f;
idealLegsYaw = 0.0f;
oldViewYaw = viewAngles.yaw;
leanVelocity = vec3_zero;
if ( gameLocal.isMultiplayer ) {
playerView.Flash( colorWhite, 140 );
}
// don't do any smoothing with this snapshot
predictedFrame = gameLocal.framenum;
UpdateVisuals();
teleportEntity = destination;
if ( !gameLocal.isClient && !noclip && !spectating ) {
if ( gameLocal.isMultiplayer ) {
// kill anything at the new position or mark for kill depending on immediate or delayed teleport
gameLocal.KillBox( this, destination != NULL );
} else {
// kill anything at the new position
gameLocal.KillBox( this, true );
}
}
}
/*
====================
idPlayer::SetPrivateCameraView
====================
*/
void idPlayer::SetPrivateCameraView( idCamera *camView ) {
privateCameraView = camView;
if ( camView ) {
StopFiring();
Hide();
} else {
if ( !spectating ) {
Show();
}
}
}
/*
====================
idPlayer::DefaultFov
Returns the base FOV
====================
*/
float idPlayer::DefaultFov( void ) const {
float fov;
fov = g_fov.GetFloat();
if ( gameLocal.isMultiplayer ) {
if ( fov < 90.0f ) {
return 90.0f;
} else if ( fov > 175.0f ) {
return 175.0f;
}
}
return fov;
}
/*
====================
idPlayer::CalcFov
Fixed fov at intermissions, otherwise account for fov variable and zooms.
====================
*/
float idPlayer::CalcFov( bool honorZoom ) {
float fov;
if ( fxFov ) {
return DefaultFov() + 10.0f + idMath::Cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
}
if ( influenceFov ) {
return influenceFov;
}
if ( vehicleController.IsDriving() ) {
rvVehicle * vehicle = vehicleController.GetVehicle();
rvVehiclePosition * position = vehicle ? vehicle->GetPosition( vehicleController.GetPosition() ) : 0;
rvVehicleWeapon * weapon = position ? position->GetActiveWeapon() : 0;
if ( zoomFov.IsDone( gameLocal.time ) ) {
fov = ( honorZoom && zoomed && weapon ) ? weapon->GetZoomFov() : DefaultFov();
} else {
fov = zoomFov.GetCurrentValue( gameLocal.time );
}
} else {
if ( zoomFov.IsDone( gameLocal.time ) ) {
fov = ( honorZoom && zoomed && weapon ) ? weapon->GetZoomFov() : DefaultFov();
} else {
fov = zoomFov.GetCurrentValue( gameLocal.time );
}
}
// bound normal viewsize
if ( fov < 1 ) {
fov = 1;
} else if ( fov > 179 ) {
fov = 179;
}
return fov;
}
/*
==============
idPlayer::GunTurningOffset
generate a rotational offset for the gun based on the view angle
history in loggedViewAngles
==============
*/
idAngles idPlayer::GunTurningOffset( void ) {
idAngles a;
a.Zero();
if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
return a;
}
idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
idAngles av, base;
int weaponAngleOffsetAverages;
float weaponAngleOffsetScale, weaponAngleOffsetMax;
weapon->GetAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
av = current;
// calcualte this so the wrap arounds work properly
for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
idAngles delta = a2 - current;
if ( delta[1] > 180 ) {
delta[1] -= 360;
} else if ( delta[1] < -180 ) {
delta[1] += 360;
}
av += delta * ( 1.0f / weaponAngleOffsetAverages );
}
a = ( av - current ) * weaponAngleOffsetScale;
for ( int i = 0 ; i < 3 ; i++ ) {
if ( a[i] < -weaponAngleOffsetMax ) {
a[i] = -weaponAngleOffsetMax;
} else if ( a[i] > weaponAngleOffsetMax ) {
a[i] = weaponAngleOffsetMax;
}
}
return a;
}
/*
==============
idPlayer::GunAcceleratingOffset
generate a positional offset for the gun based on the movement
history in loggedAccelerations
==============
*/
idVec3 idPlayer::GunAcceleratingOffset( void ) {
idVec3 ofs;
float weaponOffsetTime;
float weaponOffsetScale;
ofs.Zero();
weapon->GetTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
if ( stop < 0 ) {
stop = 0;
}
for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
float f;
float t = gameLocal.time - acc->time;
if ( t >= weaponOffsetTime ) {
break; // remainder are too old to care about
}
f = t / weaponOffsetTime;
f = ( idMath::Cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
ofs += f * weaponOffsetScale * acc->dir;
}
return ofs;
}
/*
==============
idPlayer::CalculateViewWeaponPos
Calculate the bobbing position of the view weapon
==============
*/
void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
float scale;
float fracsin;
idAngles angles;
int delta;
// CalculateRenderView must have been called first
const idVec3 &viewOrigin = firstPersonViewOrigin;
const idMat3 &viewAxis = firstPersonViewAxis;
// the constant was -0.2 in quake3, but -.1 seems closest to the same visual effect in quake4
float fovOffset = 0;
float curfov = g_fov.GetFloat();
if ( g_weaponFovEffect.GetBool() && curfov > 90.0f )
fovOffset = -0.1f * ( curfov - 90.0f );
// these cvars are just for hand tweaking before moving a value to the weapon def
idVec3 gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() + fovOffset );
const idPlayer* player = gameLocal.GetLocalPlayer();
if (player && (this == player || player->spectating && player->spectator == this->entityNumber) ) {
gunpos += weapon->GetViewModelOffset();
} else {
gunpos = weapon->GetViewModelOffset();
}
// as the player changes direction, the gun will take a small lag
idVec3 gunOfs = GunAcceleratingOffset();
origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
// on odd legs, invert some angles
if ( noclip || 1 ) {
scale = 0;
} else if ( bobCycle & 128 ) {
scale = -xyspeed;
} else {
scale = xyspeed;
}
// gun angles from bobbing
angles.roll = scale * bobfracsin * 0.005f + g_gun_roll.GetFloat();
angles.yaw = scale * bobfracsin * 0.01f + g_gun_yaw.GetFloat();
angles.pitch = xyspeed * bobfracsin * 0.005f + g_gun_pitch.GetFloat();
angles += weapon->GetViewModelAngles();
// gun angles from turning
if ( gameLocal.isMultiplayer ) {
idAngles offset = GunTurningOffset();
offset *= g_mpWeaponAngleScale.GetFloat();
angles += offset;
} else {
angles += GunTurningOffset();
}
idVec3 gravity = physicsObj.GetGravityNormal();
// RAVEN BEGIN
// abahr: when looking down, really large deflections cause back of weapons to show
float landChangeFrac = idMath::Lerp( 0.25f, 0.05f, viewAngles.ToForward() * gravity);
// RAVEN ABAHR
// drop the weapon when landing after a jump / fall
delta = gameLocal.time - landTime;
if ( delta < LAND_DEFLECT_TIME ) {
// RAVEN BEGIN
// abahr: changed to use landChangeFrac
origin -= gravity * ( landChange*landChangeFrac * delta / LAND_DEFLECT_TIME );
} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
origin -= gravity * ( landChange*landChangeFrac * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
// RAVEN END
}
// speed sensitive idle drift
if ( !noclip ) {
scale = xyspeed * 0.5f + 40.0f;
fracsin = scale * idMath::Sin( MS2SEC( gameLocal.time ) ) * 0.01f;
angles.roll += fracsin;
angles.yaw += fracsin;
angles.pitch += fracsin;
}
axis = angles.ToMat3() * viewAxis;
}
/*
===============
idPlayer::OffsetThirdPersonVehicleView
===============
*/
// RAVEN BEGIN
// jnewquist: option to avoid clipping against world
void idPlayer::OffsetThirdPersonVehicleView( bool clip ) {
// RAVEN END
idVec3 view;
idVec3 focusAngles;
trace_t trace;
idVec3 focusPoint;
float focusDist;
idVec3 origin;
idAngles angles, angles2;
idEntity* vehicle;
assert ( IsInVehicle ( ) );
vehicle = vehicleController.GetVehicle();
origin = vehicle->GetRenderEntity()->origin;
angles = vehicle->GetRenderEntity()->axis.ToAngles();
angles.yaw += pm_thirdPersonAngle.GetFloat();
angles.pitch += 25.0f;
// angles.pitch += viewAngles.pitch;
// angles.yaw += viewAngles.yaw;
focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
view = origin;
// RAVEN BEGIN
// abahr: taking into account gravity
view += physicsObj.GetGravityAxis()[2] * 8.0f;
// RAVEN END
renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
float speed = vehicle->GetPhysics()->GetLinearVelocity() *
vehicle->GetPhysics()->GetAxis()[0];
speed = idMath::Fabs( speed );
speed *= pm_vehicleCameraSpeedScale.GetFloat();
if( speed > pm_vehicleCameraScaleMax.GetFloat() )
{
speed = pm_vehicleCameraScaleMax.GetFloat();
}
vehicleCameraDist += ( MS2SEC( gameLocal.GetMSec() ) * ( ( pm_vehicleCameraMinDist.GetFloat() + speed ) - vehicleCameraDist ) );
view -= vehicleCameraDist * renderView->viewaxis[ 0 ];
// RAVEN BEGIN
// jnewquist: option to avoid clipping against world
if ( clip ) {
// trace a ray from the origin to the viewpoint to make sure the view isn't
// in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
const idVec3 clip_mins( -4.0f, -4.0f, -4.0f );
const idVec3 clip_maxs( 4.0f, 4.0f, 4.0f );
const idBounds clip_bounds( clip_mins, clip_maxs );
idClipModel clipBounds( clip_bounds );// We clip when using a tram gun in the tram car
// ddynerman: multiple clip worlds
gameLocal.Translation( this, trace, origin, view, &clipBounds, vehicle->GetPhysics()->GetAxis(), MASK_SOLID, vehicle, vehicle->GetBindMaster() );
if ( trace.fraction != 1.0 )
{
view = trace.endpos;
// abahr: taking into account gravity
view += physicsObj.GetGravityAxis()[2] * ( 1.0f - trace.fraction ) * 32;
// try another trace to this position, because a tunnel may have the ceiling
// close enough that this is poking out
// ddynerman: multiple clip worlds
gameLocal.Translation( this, trace, origin, view, &clipBounds, vehicle->GetPhysics()->GetAxis(), MASK_SOLID, vehicle, vehicle->GetBindMaster() );
view = trace.endpos;
}
}
// RAVEN END
// select pitch to look at focus point from vieword
focusPoint -= view;
focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
if ( focusDist < 1 )
{
focusDist = 1; // should never happen
}
angles.pitch = - RAD2DEG( idMath::ATan( focusPoint.z, focusDist ) );
renderView->vieworg = view;
renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
renderView->viewID = 0;
}
/*
===============
idPlayer::OffsetThirdPersonView
===============
*/
void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
idVec3 view;
idVec3 focusAngles;
trace_t trace;
idVec3 focusPoint;
float focusDist;
float forwardScale, sideScale;
idVec3 origin;
idAngles angles;
idMat3 axis;
idBounds bounds;
angles = viewAngles;
GetViewPos( origin, axis );
if ( angle ) {
angles.pitch = 0.0f;
}
if ( angles.pitch > 45.0f ) {
angles.pitch = 45.0f; // don't go too far overhead
}
focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
focusPoint.z += height;
view = origin;
// RAVEN BEGIN
// abahr: taking into account gravity
view += physicsObj.GetGravityAxis()[2] * (8.0f + height);
// RAVEN END
angles.pitch *= 0.5f;
renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
view -= range * forwardScale * renderView->viewaxis[ 0 ];
view += range * sideScale * renderView->viewaxis[ 1 ];
if ( clip ) {
// trace a ray from the origin to the viewpoint to make sure the view isn't
// in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
// RAVEN BEGIN
// ddynerman: multiple clip worlds
gameLocal.TraceBounds( this, trace, origin, view, bounds, MASK_SOLID, this );
// RAVEN END
if ( trace.fraction != 1.0f ) {
view = trace.endpos;
// RAVEN BEGIN
// abahr: taking into account gravity
view += physicsObj.GetGravityAxis()[2] * ( 1.0f - trace.fraction ) * 32.0f;
// RAVEN END
// try another trace to this position, because a tunnel may have the ceiling
// close enough that this is poking out
// RAVEN BEGIN
// ddynerman: multiple clip worlds
gameLocal.TraceBounds( this, trace, origin, view, bounds, MASK_SOLID, this );
// RAVEN END
view = trace.endpos;
}
}
// select pitch to look at focus point from vieword
focusPoint -= view;
focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
if ( focusDist < 1.0f ) {
focusDist = 1.0f; // should never happen
}
angles.pitch = - RAD2DEG( idMath::ATan( focusPoint.z, focusDist ) );
angles.yaw -= angle;
renderView->vieworg = view;
renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
renderView->viewID = 0;
}
/*
===============
idPlayer::GetEyePosition
===============
*/
idVec3 idPlayer::GetEyePosition( void ) const {
idVec3 org;
if ( WantSmoothing() ) {
org = predictedOrigin;
} else {
org = GetPhysics()->GetOrigin();
}
return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
}
/*
===============
idPlayer::GetViewPos
===============
*/
void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
idAngles angles;
// if dead, fix the angle and don't add any kick
if ( health <= 0 ) {
angles.yaw = viewAngles.yaw;
angles.roll = 40;
angles.pitch = -15;
axis = angles.ToMat3();
origin = GetEyePosition();
} else if ( IsInVehicle ( ) ) {
vehicleController.GetEyePosition ( origin, axis );
idVec3 shakeOffset;
idAngles shakeAngleOffset;
idBounds relBounds(idVec3(0, 0, 0), idVec3(0, 0, 0));
playerView.ShakeOffsets( shakeOffset, shakeAngleOffset, relBounds );
origin += shakeOffset;
axis = (shakeAngleOffset + playerView.AngleOffset()).ToMat3() * axis;
} else {
idVec3 shakeOffset;
idAngles shakeAngleOffset;
idBounds relBounds(idVec3(0, 0, 0), idVec3(0, 0, 0));
playerView.ShakeOffsets( shakeOffset, shakeAngleOffset, relBounds );
origin = GetEyePosition() + viewBob + shakeOffset;
angles = viewAngles + viewBobAngles + shakeAngleOffset + playerView.AngleOffset();
axis = angles.ToMat3() * physicsObj.GetGravityAxis();
// adjust the origin based on the camera nodal distance (eye distance from neck)
origin += physicsObj.GetGravityNormal() * g_viewNodalZ.GetFloat();
origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
}
}
/*
===============
idPlayer::CalculateFirstPersonView
===============
*/
void idPlayer::CalculateFirstPersonView( void ) {
if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
// Displays the view from the point of view of the "camera" joint in the player model
idMat3 axis;
idVec3 origin;
idAngles ang;
ang = viewBobAngles + playerView.AngleOffset();
ang.yaw += viewAxis[ 0 ].ToYaw();
jointHandle_t joint = animator.GetJointHandle( "camera" );
animator.GetJointTransform( joint, gameLocal.time, origin, axis );
firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
} else {
// offset for local bobbing and kicks
GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
}
}
/*
==================
idPlayer::GetRenderView
Returns the renderView that was calculated for this tic
==================
*/
renderView_t *idPlayer::GetRenderView( void ) {
return renderView;
}
/*
==================
idPlayer::SmoothenRenderView
various situations where the view angles need smoothing:
demo replay:
On a slow client with low fps multiple game frames are run in quick succession
inbetween rendered frames. As a result the usercmds are not recorded at fixed
time intervals but in small bursts. This routine interpolates the view angles
based on the real time at which the usercmds were recorded to make a demo
recorded on a slow client play back smoothly on a fast client.
spectate follow?
==================
*/
void idPlayer::SmoothenRenderView( bool firstPerson ) {
int d1, d2;
idAngles angles, anglesDelta, newAngles;
if ( gameLocal.IsServerDemoPlaying() ) {
d1 = usercmd.gameTime - demoViewAngleTime;
if ( d1 < 0 ) {
return;
}
d2 = usercmd.realTime - demoViewAngleTime;
if ( d2 <= 0 ) {
return;
}
if ( d1 >= d2 ) {
return;
}
angles = renderView->viewaxis.ToAngles();
anglesDelta = angles - demoViewAngles;
anglesDelta.Normalize180();
newAngles = demoViewAngles + ( (float) d1 / d2 ) * anglesDelta;
renderView->viewaxis = newAngles.ToMat3();
if ( usercmd.gameTime + gameLocal.msec > usercmd.realTime ) {
demoViewAngleTime = usercmd.realTime;
demoViewAngles = angles;
}
if ( firstPerson ) {
// make sure the view weapon moves smoothly
firstPersonViewAxis = renderView->viewaxis;
}
}
}
/*
==================
idPlayer::CalculateRenderView
create the renderView for the current tic
==================
*/
void idPlayer::CalculateRenderView( void ) {
int i;
float range;
if ( !renderView ) {
// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
RV_PUSH_HEAP_MEM_AUTO(p0,this);
// RAVEN END
renderView = new renderView_t;
}
memset( renderView, 0, sizeof( *renderView ) );
// copy global shader parms
for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
}
renderView->globalMaterial = gameLocal.GetGlobalMaterial();
renderView->time = gameLocal.time;
// calculate size of 3D view
renderView->x = 0;
renderView->y = 0;
renderView->width = SCREEN_WIDTH;
renderView->height = SCREEN_HEIGHT;
renderView->viewID = 0;
// check if we should be drawing from a camera's POV
if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
// get origin, axis, and fov
if ( privateCameraView ) {
privateCameraView->GetViewParms( renderView );
} else {
gameLocal.GetCamera()->GetViewParms( renderView );
}
} else {
bool cameraIsSet = false;
// First try out any camera views that can possibly fail.
if( !cameraIsSet ){
if ( g_stopTime.GetBool() ) {
renderView->vieworg = firstPersonViewOrigin;
renderView->viewaxis = firstPersonViewAxis;
SmoothenRenderView( true );
if ( !pm_thirdPerson.GetBool() ) {
// set the viewID to the clientNum + 1, so we can suppress the right player bodies and
// allow the right player view weapons
renderView->viewID = entityNumber + 1;
}
} else if ( pm_thirdPerson.GetBool() && IsInVehicle ( ) ) {
// RAVEN BEGIN
// jnewquist: option to avoid clipping against world
OffsetThirdPersonVehicleView( pm_thirdPersonClip.GetBool() );
// RAVEN END
SmoothenRenderView( false );
} else if ( pm_thirdPerson.GetBool() ) {
OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
SmoothenRenderView( false );
} else if ( pm_thirdPersonDeath.GetBool() ) {
range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
SmoothenRenderView( false );
} else {
renderView->vieworg = firstPersonViewOrigin;
renderView->viewaxis = firstPersonViewAxis;
SmoothenRenderView( true );
// set the viewID to the clientNum + 1, so we can suppress the right player bodies and
// allow the right player view weapons
renderView->viewID = entityNumber + 1;
}
}
// field of view
gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
}
if ( renderView->fov_y == 0 ) {
common->Error( "renderView->fov_y == 0" );
}
if ( g_showviewpos.GetBool() ) {
gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
}
}
/*
==================
idPlayer::Event_EnableTarget
==================
*/
void idPlayer::Event_EnableTarget ( void ) {
fl.notarget = false;
}
/*
==================
idPlayer::Event_DisableTarget
==================
*/
void idPlayer::Event_DisableTarget ( void ) {
fl.notarget = true;
}
/*
==================
idPlayer::Event_GetViewPos
==================
*/
void idPlayer::Event_GetViewPos( void ) {
idVec3 viewOrigin;
idMat3 viewAxis;
GetViewPos(viewOrigin, viewAxis);
idThread::ReturnVector( viewOrigin );
}
/*
==================
idPlayer::Event_FinishHearingLoss
==================
*/
void idPlayer::Event_FinishHearingLoss ( float fadeTime ) {
if ( fadeTime <= 0.0f ) {
StopSound ( SND_CHANNEL_DEMONIC, false );
pfl.hearingLoss = false;
} else {
soundSystem->FadeSoundClasses( SOUNDWORLD_GAME, 0, 0.0f, fadeTime );
PostEventSec ( &EV_Player_FinishHearingLoss, fadeTime, 0.0f );
}
}
// RAVEN BEGIN
// twhitaker: added the event
/*
=============
idPlayer::Event_ApplyImpulse
=============
*/
void idPlayer::Event_ApplyImpulse ( idEntity* ent, idVec3 &point, idVec3 &impulse ) {
GetPhysics()->ApplyImpulse( 0, point, impulse );
}
// mekberg: added Event_EnableObjectives
/*
=============
idPlayer::Event_EnableObjectives
=============
*/
void idPlayer::Event_EnableObjectives ( void ) {
objectivesEnabled = true;
}
// mekberg: added Event_DisableObjectives
/*
=============
idPlayer::Event_DisableObjectives
=============
*/
void idPlayer::Event_DisableObjectives ( void ) {
// if it's open, it should be closed
if (objectiveSystemOpen ) {
ToggleObjectives();
}
objectivesEnabled = false;
}
// mekberg: added Event_AllowNewObjectives
void idPlayer::Event_AllowNewObjectives ( void ) {
showNewObjectives = true;
}
// mekberg: added sethealth
/*
=============
idPlayer::Event_SetHealth
=============
*/
void idPlayer::Event_SetHealth( float newHealth ) {
health = idMath::ClampInt( 1 , inventory.maxHealth, newHealth );
}
/*
=============
idPlayer::Event_SetArmor
=============
*/
void idPlayer::Event_SetArmor( float newArmor ) {
inventory.armor = idMath::ClampInt( 0 , inventory.maxarmor, newArmor );
}
/*
=============
idPlayer::Event_SetExtraProjPassEntity
=============
*/
void idPlayer::Event_SetExtraProjPassEntity( idEntity* _extraProjPassEntity ) {
extraProjPassEntity = _extraProjPassEntity;
}
// RAVEN END
/*
=============
idPlayer::AddProjectilesFired
=============
*/
void idPlayer::AddProjectilesFired( int count ) {
numProjectilesFired += count;
}
/*
=============
idPlayer::AddProjectileHites
=============
*/
void idPlayer::AddProjectileHits( int count ) {
numProjectileHits += count;
}
/*
=============
idPlayer::TriggerHitSound
=============
*/
void idPlayer::TriggerHitSound( bool armor ) {
if ( gameLocal.framenum <= lastHitFrame ) {
return;
}
lastHitFrame = gameLocal.framenum;
lastHitArmor = armor;
idUserInterface *cursor = idPlayer::cursor;
bool spectated = false;
idPlayer *p = gameLocal.GetLocalPlayer();
if ( p && p->spectating && p->spectator == entityNumber ) {
cursor = p->GetCursorGUI();
spectated = true;
}
if ( cursor ) {
cursor->HandleNamedEvent( "weaponHit" );
}
// spectated so we get blips for a client we're following
// localClientNum check so listen server plays only for local player
if ( spectated || IsLocalClient() ) {
const char* sound = NULL;
if ( armor ) {
spawnArgs.GetString( "snd_armorHit", "", &sound );
}
if ( sound == NULL ) {
spawnArgs.GetString( "snd_weaponHit", "", &sound );
}
if ( sound != NULL ) {
soundSystem->PlayShaderDirectly( SOUNDWORLD_GAME, sound );
}
if ( aimClientNum != -1 ) {
if ( mphud ) {
mphud->HandleNamedEvent( "aim_hit" );
}
}
}
}
/*
=============
idPlayer::SetInfluenceLevel
=============
*/
void idPlayer::SetInfluenceLevel( int level ) {
if ( level != influenceActive ) {
if ( level ) {
for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( ent->IsType( idProjectile::GetClassType() ) ) {
// RAVEN END
// remove all projectiles
ent->PostEventMS( &EV_Remove, 0 );
}
}
if ( weaponEnabled && weapon ) {
weapon->EnterCinematic();
}
} else {
physicsObj.SetLinearVelocity( vec3_origin );
if ( weaponEnabled && weapon ) {
weapon->ExitCinematic();
}
}
influenceActive = level;
}
}
/*
=============
idPlayer::SetInfluenceView
=============
*/
void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
influenceMaterial = NULL;
influenceEntity = NULL;
influenceSkin = NULL;
if ( mtr && *mtr ) {
influenceMaterial = declManager->FindMaterial( mtr );
}
if ( skinname && *skinname ) {
influenceSkin = declManager->FindSkin( skinname );
if ( head.GetEntity() ) {
head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
}
UpdateVisuals();
}
influenceRadius = radius;
if ( radius > 0.0f ) {
influenceEntity = ent;
}
}
/*
=============
idPlayer::SetInfluenceFov
=============
*/
void idPlayer::SetInfluenceFov( float fov ) {
influenceFov = fov;
}
/*
================
idPlayer::OnLadder
================
*/
bool idPlayer::OnLadder( void ) const {
return physicsObj.OnLadder();
}
/*
==================
idPlayer::Event_GetButtons
==================
*/
void idPlayer::Event_GetButtons( void ) {
idThread::ReturnInt( usercmd.buttons );
}
/*
==================
idPlayer::Event_GetMove
==================
*/
void idPlayer::Event_GetMove( void ) {
idVec3 move( usercmd.forwardmove, usercmd.rightmove, usercmd.upmove );
idThread::ReturnVector( move );
}
/*
================
idPlayer::Event_GetViewAngles
================
*/
void idPlayer::Event_GetViewAngles( void ) {
idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
}
/*
================
idPlayer::Event_SetViewAngles
================
*/
void idPlayer::Event_SetViewAngles( const idVec3 & vec ) {
idAngles ang;
ang.Set( vec.z, vec.y, vec.x );
SetViewAngles( ang );
}
/*
==================
idPlayer::Event_StopFxFov
==================
*/
void idPlayer::Event_StopFxFov( void ) {
fxFov = false;
}
/*
==================
idPlayer::StartFxFov
==================
*/
void idPlayer::StartFxFov( float duration ) {
fxFov = true;
PostEventSec( &EV_Player_StopFxFov, duration );
}
/*
==================
idPlayer::Event_EnableWeapon
==================
*/
void idPlayer::Event_EnableWeapon( void ) {
gameLocal.world->spawnArgs.SetBool( "no_Weapons", 0 );
Give( "weapon", spawnArgs.GetString( va( "def_weapon%d", 0 ) ) );
hiddenWeapon = false;
weaponEnabled = true;
if ( weapon ) {
weapon->ExitCinematic();
}
ShowCrosshair();
}
/*
==================
idPlayer::Event_DisableWeapon
==================
*/
void idPlayer::Event_DisableWeapon( void ) {
hiddenWeapon = true;
weaponEnabled = false;
if ( weapon ) {
weapon->EnterCinematic();
}
HideCrosshair();
}
/*
==================
idPlayer::Event_GetCurrentWeapon
==================
*/
void idPlayer::Event_GetCurrentWeapon( void ) {
if ( currentWeapon >= 0 ) {
idThread::ReturnString( spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) ) );
} else {
idThread::ReturnString( "" );
}
}
/*
==================
idPlayer::Event_GetPreviousWeapon
==================
*/
void idPlayer::Event_GetPreviousWeapon( void ) {
if ( previousWeapon >= 0 ) {
int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
idThread::ReturnString( spawnArgs.GetString( va( "def_weapon%d", pw) ) );
} else {
idThread::ReturnString( "def_weapon0" );
}
}
/*
==================
idPlayer::Event_SelectWeapon
==================
*/
void idPlayer::Event_SelectWeapon( const char *weaponName ) {
int i;
int weaponNum;
if ( gameLocal.isClient ) {
gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
return;
}
weaponNum = -1;
for( i = 0; i < MAX_WEAPONS; i++ ) {
if ( inventory.weapons & ( 1 << i ) ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( !idStr::Cmp( weap, weaponName ) ) {
if ( !inventory.HasAmmo( weap ) ) {
return;
}
weaponNum = i;
break;
}
}
}
if ( weaponNum < 0 ) {
gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
return;
}
hiddenWeapon = false;
idealWeapon = weaponNum;
UpdateHudWeapon();
}
/*
==================
idPlayer::Event_GetAmmoData
==================
*/
void idPlayer::Event_GetAmmoData( const char *ammoClass ) {
idVec3 weaponAmmo;
//ammo vector is this: current ammo count, max ammo count, and %
weaponAmmo.x = inventory.ammo[ inventory.AmmoIndexForAmmoClass( ammoClass) ];
weaponAmmo.y = inventory.MaxAmmoForAmmoClass( this, ammoClass );
if( weaponAmmo.y == 0)
weaponAmmo.z = 0;
else
weaponAmmo.z = (float)(weaponAmmo.x / weaponAmmo.y);
idThread::ReturnVector( weaponAmmo);
}
/*
==================
idPlayer::Event_RefillAmmo
==================
*/
void idPlayer::Event_RefillAmmo( void ) {
int a;
for ( int i = 0; i < MAX_WEAPONS; i++ ) {
if ( inventory.weapons & ( 1 << i ) ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( weap && *weap ) {
a = inventory.AmmoIndexForWeaponIndex( i );
inventory.ammo[ a ] = inventory.MaxAmmoForAmmoClass( this, rvWeapon::GetAmmoNameForIndex( a ) );
}
}
}
}
/*
==================
idPlayer::Event_AllowFallDamage
==================
*/
void idPlayer::Event_AllowFallDamage( int toggle ) {
if( toggle ) {
pfl.noFallingDamage = false;
} else {
pfl.noFallingDamage = true;
}
}
/*
==================
idPlayer::Event_GetWeaponEntity
==================
*/
void idPlayer::Event_GetWeaponEntity( void ) {
idThread::ReturnEntity( weaponViewModel );
}
/*
==================
idPlayer::Event_HideDatabaseEntry
==================
*/
void idPlayer::Event_HideDatabaseEntry ( void ) {
if ( hud ) {
hud->HandleNamedEvent( "closeDatabaseEntry" );
}
}
/*
==================
idPlayer::Event_ZoomIn
==================
*/
void idPlayer::Event_ZoomIn ( void ) {
float currentFov;
float t;
if ( zoomed ) {
return;
}
if ( vehicleController.IsDriving() ) {
rvVehicle * vehicle = vehicleController.GetVehicle();
rvVehiclePosition * position = vehicle ? vehicle->GetPosition( vehicleController.GetPosition() ) : 0;
rvVehicleWeapon * weapon = position ? position->GetActiveWeapon() : 0;
if( !weapon ) {
// this should only happen in fringe cases - zooming in while dead, etc
zoomFov.Init( gameLocal.time, 0, DefaultFov(), DefaultFov() );
zoomed = false;
return;
}
currentFov = CalcFov ( true );
t = currentFov - weapon->GetZoomFov();
t /= (DefaultFov() - weapon->GetZoomFov());
t *= weapon->GetZoomTime();
zoomFov.Init( gameLocal.time, SEC2MS(t), currentFov, weapon->GetZoomFov() );
zoomed = true;
if ( weapon->GetZoomGui() ) {
weapon->GetZoomGui()->HandleNamedEvent ( "zoomIn" );
weaponViewModel->StartSound ( "snd_zoomin", SND_CHANNEL_ANY, 0, false, NULL );
}
} else if ( weapon && this->weaponEnabled ) {
currentFov = CalcFov ( true );
t = currentFov - weapon->GetZoomFov();
t /= (DefaultFov() - weapon->GetZoomFov());
t *= weapon->GetZoomTime();
zoomFov.Init( gameLocal.time, SEC2MS(t), currentFov, weapon->GetZoomFov() );
zoomed = true;
if ( weapon->GetZoomGui() ) {
weapon->GetZoomGui()->HandleNamedEvent ( "zoomIn" );
weaponViewModel->StartSound ( "snd_zoomin", SND_CHANNEL_ANY, 0, false, NULL );
}
}
}
/*
==================
idPlayer::Event_ZoomOut
==================
*/
void idPlayer::Event_ZoomOut ( void ) {
float t;
float currentFov;
if ( !zoomed ) {
return;
}
if ( vehicleController.IsDriving() ) {
rvVehicle * vehicle = vehicleController.GetVehicle();
rvVehiclePosition * position = vehicle ? vehicle->GetPosition( vehicleController.GetPosition() ) : 0;
rvVehicleWeapon * weapon = position ? position->GetActiveWeapon() : 0;
if( !weapon ) {
// this should only happen in fringe cases - zooming out while dead, etc
zoomFov.Init( gameLocal.time, 0, DefaultFov(), DefaultFov() );
zoomed = false;
return;
}
currentFov = CalcFov ( true );
t = currentFov - weapon->GetZoomFov();
t /= (DefaultFov() - weapon->GetZoomFov());
t = (1.0f - t) * weapon->GetZoomTime();
zoomFov.Init( gameLocal.time, SEC2MS(t), currentFov, DefaultFov() );
zoomed = false;
if ( weapon->GetZoomGui() ) {
weaponViewModel->StartSound ( "snd_zoomout", SND_CHANNEL_ANY, 0, false, NULL );
}
} else {
if( !weapon ) {
// this should only happen in fringe cases - zooming out while dead, etc
zoomFov.Init( gameLocal.time, 0, DefaultFov(), DefaultFov() );
zoomed = false;
return;
}
currentFov = CalcFov ( true );
t = currentFov - weapon->GetZoomFov();
t /= (DefaultFov() - weapon->GetZoomFov());
t = (1.0f - t) * weapon->GetZoomTime();
zoomFov.Init( gameLocal.time, SEC2MS(t), currentFov, DefaultFov() );
zoomed = false;
if ( weapon->GetZoomGui() ) {
weaponViewModel->StartSound( "snd_zoomout", SND_CHANNEL_ANY, 0, false, NULL );
}
}
}
/*
==================
idPlayer::TeleportDeath
==================
*/
void idPlayer::TeleportDeath( int killer ) {
teleportKiller = killer;
}
/*
==================
idPlayer::Event_ExitTeleporter
==================
*/
void idPlayer::Event_ExitTeleporter( void ) {
idEntity *exitEnt;
float pushVel;
// verify and setup
exitEnt = teleportEntity;
if ( !exitEnt ) {
common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
return;
}
pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
if ( gameLocal.isServer && !spectating ) {
ServerSendInstanceEvent( EVENT_EXIT_TELEPORTER, NULL, false, -1 );
}
SetPrivateCameraView( NULL );
// setup origin and push according to the exit target
SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
physicsObj.ClearPushedVelocity( );
// teleport fx
playerView.Flash( colorWhite, 120 );
// clear the ik heights so model doesn't appear in the wrong place
walkIK.EnableAll();
UpdateVisuals();
gameLocal.PlayEffect( spawnArgs, "fx_teleport", GetPhysics()->GetOrigin(), idVec3(0,0,1).ToMat3(), false, vec3_origin );
StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
if ( teleportKiller != -1 ) {
// we got killed while being teleported
Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
teleportKiller = -1;
} else {
// kill anything that would have waited at teleport exit
gameLocal.KillBox( this );
}
teleportEntity = NULL;
}
/*
===============
idPlayer::Event_DamageOverTimeEffect
===============
*/
void idPlayer::Event_DamageOverTimeEffect( int endTime, int interval, const char *damageDefName ) {
const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( damageDef ) {
rvClientCrawlEffect* effect;
// mwhitlock: Dynamic memory consolidation
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_MULTIPLE_FRAME);
effect = new rvClientCrawlEffect( gameLocal.GetEffect ( damageDef->dict, "fx_dot" ), GetWeaponViewModel(), interval );
RV_POP_HEAP();
effect->Play ( gameLocal.time, false );
if ( endTime == -1 || gameLocal.GetTime() + interval <= endTime ) {
//post it again
PostEventMS( &EV_DamageOverTimeEffect, interval, endTime, interval, damageDefName );
}
}
}
/*
===============
idPlayer::LocalClientPredictionThink
===============
*/
void idPlayer::LocalClientPredictionThink( void ) {
renderEntity_t *headRenderEnt;
oldFlags = usercmd.flags;
oldButtons = usercmd.buttons;
usercmd = gameLocal.usercmds[ IsFakeClient() ? MAX_CLIENTS : entityNumber ];
buttonMask &= usercmd.buttons;
usercmd.buttons &= ~buttonMask;
if ( idealWeapon != currentWeapon ) {
usercmd.buttons &= ~BUTTON_ATTACK;
}
// clear the ik before we do anything else so the skeleton doesn't get updated twice
walkIK.ClearJointMods();
if ( gameLocal.isNewFrame ) {
if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
PerformImpulse( usercmd.impulse );
}
}
if ( forceScoreBoard && forceScoreBoardTime && gameLocal.time > forceScoreBoardTime ) {
forceScoreBoardTime = 0;
forceScoreBoard = false;
}
scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
// zooming
bool zoom = (usercmd.buttons & BUTTON_ZOOM) && CanZoom();
if ( zoom != zoomed ) {
if ( zoom ) {
ProcessEvent( &EV_Player_ZoomIn );
} else {
ProcessEvent( &EV_Player_ZoomOut );
}
}
if ( IsInVehicle( ) ) {
vehicleController.SetInput( usercmd, viewAngles );
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPeoson / camera view
CalculateRenderView();
UpdateLocation();
if ( !fl.hidden ) {
UpdateAnimation();
Present();
}
return;
}
AdjustSpeed();
UpdateViewAngles();
/*
// RAVEN BEGIN
// abahr
if( !noclip && !spectating ) {
UpdateGravity();
}
// RAVEN END
*/
if ( !isLagged ) {
// don't allow client to move when lagged
predictedUpdated = false;
Move();
// predict collisions with items
if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
TouchTriggers( &idItem::GetClassType() );
} else if ( spectating && !noclip ) {
// predict teleports for specs
TouchTriggers( &idTrigger_Multi::GetClassType() );
}
}
// update GUIs, Items, and character interactions
UpdateFocus();
// service animations
if ( !spectating && !af.IsActive() ) {
UpdateConditions();
UpdateAnimState();
CheckBlink();
}
// clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
pfl.pain = false;
if ( !af.IsActive() ) {
AdjustBodyAngles();
}
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPerson / camera view
CalculateRenderView();
if ( IsFakeClient() && spectating ) {
UpdateSpectating();
}
if ( !gameLocal.inCinematic && weaponViewModel && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
UpdateWeapon();
}
UpdateHud();
if ( gameLocal.isNewFrame ) {
UpdatePowerUps();
}
UpdateDeathSkin( false );
UpdateDeathShader( deathStateHitch );
if( gameLocal.isMultiplayer ) {
if ( clientHead.GetEntity() ) {
headRenderEnt = clientHead.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
} else {
if ( head.GetEntity() ) {
headRenderEnt = head.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
}
if ( headRenderEnt ) {
// in MP, powerup skin overrides influence
if ( powerUpSkin ) {
headRenderEnt->customSkin = powerUpSkin;
} else if ( influenceSkin ) {
headRenderEnt->customSkin = influenceSkin;
} else {
headRenderEnt->customSkin = headSkin;
}
headRenderEnt->suppressSurfaceInViewID = entityNumber + 1;
}
// always show your own shadow
renderEntity.suppressLOD = 1;
if ( headRenderEnt ) {
headRenderEnt->suppressLOD = 1;
}
DrawShadow( headRenderEnt );
// never cast shadows from our first-person muzzle flashes
// FIXME: flashlight too
renderEntity.suppressShadowInLightID = rvWeapon::WPLIGHT_MUZZLEFLASH * 100 + entityNumber;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInLightID = renderEntity.suppressShadowInLightID;
}
if ( !gameLocal.inCinematic ) {
UpdateAnimation();
}
Present();
LinkCombat();
}
/*
===============
idPlayer::NonLocalClientPredictionThink
===============
*/
#define LIMITED_PREDICTION 1
void idPlayer::NonLocalClientPredictionThink( void ) {
assert( !IsFakeClient() );
renderEntity_t *headRenderEnt;
oldFlags = usercmd.flags;
oldButtons = usercmd.buttons;
usercmd = gameLocal.usercmds[ entityNumber ];
buttonMask &= usercmd.buttons;
usercmd.buttons &= ~buttonMask;
//jshepard: added this to make sure clients can see other clients and the host switching weapons
if ( idealWeapon != currentWeapon ) {
usercmd.buttons &= ~BUTTON_ATTACK;
}
// clear the ik before we do anything else so the skeleton doesn't get updated twice
walkIK.ClearJointMods();
if ( gameLocal.isNewFrame ) {
if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
PerformImpulse( usercmd.impulse );
}
}
if ( forceScoreBoard && forceScoreBoardTime && gameLocal.time > forceScoreBoardTime ) {
forceScoreBoardTime = 0;
forceScoreBoard = false;
}
scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
// zooming
bool zoom = (usercmd.buttons & BUTTON_ZOOM) && CanZoom();
if ( zoom != zoomed ) {
if ( zoom ) {
ProcessEvent( &EV_Player_ZoomIn );
} else {
ProcessEvent( &EV_Player_ZoomOut );
}
}
#if !LIMITED_PREDICTION
if ( IsInVehicle ( ) ) {
vehicleController.SetInput ( usercmd, viewAngles );
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
// this may use firstPersonView, or a thirdPeoson / camera view
CalculateRenderView();
UpdateLocation();
if ( !fl.hidden ) {
UpdateAnimation();
Present();
}
return;
}
#endif
AdjustSpeed();
UpdateViewAngles();
if ( !isLagged ) {
// don't allow client to move when lagged
predictedUpdated = false;
// NOTE: only running on new frames causes prediction errors even when the input does not change!
if ( gameLocal.isNewFrame ) {
Move();
} else {
PredictionErrorDecay();
}
}
#if defined( _XENON ) || !LIMITED_PREDICTION
// update GUIs, Items, and character interactions
UpdateFocus();
#endif
// service animations
if ( !spectating && !af.IsActive() ) {
UpdateConditions();
UpdateAnimState();
CheckBlink();
}
// clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
pfl.pain = false;
if ( !af.IsActive() ) {
AdjustBodyAngles();
}
// calculate the exact bobbed view position, which is used to
// position the view weapon, among other things
CalculateFirstPersonView();
if ( !gameLocal.inCinematic && weaponViewModel && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
UpdateWeapon();
}
if ( gameLocal.isLastPredictFrame ) {
// this may use firstPersonView, or a thirdPerson / camera view
CalculateRenderView();
UpdateHud();
UpdatePowerUps();
}
//#if !LIMITED_PREDICTION
UpdateDeathSkin( false );
UpdateDeathShader( deathStateHitch );
//#endif
if( gameLocal.isMultiplayer ) {
if ( clientHead.GetEntity() ) {
headRenderEnt = clientHead.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
} else {
if ( head.GetEntity() ) {
headRenderEnt = head.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
}
if ( headRenderEnt ) {
// in MP, powerup skin overrides influence
if ( powerUpSkin ) {
headRenderEnt->customSkin = powerUpSkin;
} else if ( influenceSkin ) {
headRenderEnt->customSkin = influenceSkin;
} else {
headRenderEnt->customSkin = headSkin;
}
}
// always show your own shadow
renderEntity.suppressLOD = 1;
if ( headRenderEnt ) {
headRenderEnt->suppressLOD = 1;
}
DrawShadow( headRenderEnt );
// never cast shadows from our first-person muzzle flashes
// FIXME: flashlight too
renderEntity.suppressShadowInLightID = rvWeapon::WPLIGHT_MUZZLEFLASH * 100 + entityNumber;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInLightID = renderEntity.suppressShadowInLightID;
}
if ( !gameLocal.inCinematic ) {
UpdateAnimation();
}
Present();
LinkCombat();
}
/*
================
idPlayer::ClientPredictionThink
================
*/
void idPlayer::ClientPredictionThink( void ) {
// common code for both the local & non local clients
if ( reloadModel ) {
LoadDeferredModel();
reloadModel = false;
}
if ( entityNumber == gameLocal.GetDemoFollowClient() ) {
LocalClientPredictionThink();
return;
}
if ( IsLocalClient() ) {
LocalClientPredictionThink();
return;
}
assert( gameLocal.localClientNum >= 0 );
idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
if ( p && p->spectating && p->spectator == entityNumber ) {
LocalClientPredictionThink();
return;
}
NonLocalClientPredictionThink();
}
/*
================
idPlayer::GetMasterPosition
================
*/
bool idPlayer::GetMasterPosition( idVec3 &masterOrigin, idMat3 &masterAxis ) const {
if( !IsInVehicle() ) {
return idActor::GetMasterPosition( masterOrigin, masterAxis );
}
vehicleController.GetDriverPosition( masterOrigin, masterAxis );
return true;
}
/*
===============
idPlayer::PredictionErrorDecay
===============
*/
void idPlayer::PredictionErrorDecay( void ) {
if ( predictedUpdated ) {
return;
}
if ( net_predictionErrorDecay.GetFloat() <= 0.0f ) {
idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
predictedOrigin = renderOrigin;
predictedAngles = viewAngles;
return;
}
if ( gameLocal.framenum >= predictedFrame ) {
idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
if ( gameLocal.framenum == predictedFrame ) {
predictionOriginError = predictedOrigin - renderOrigin;
predictionAnglesError = predictedAngles - viewAngles;
predictionAnglesError.Normalize180();
predictionErrorTime = gameLocal.time;
// skip doing error decay if the error is too high to be due to misprediction
// this would take care of stray cases where we fail to mark that no decaying should happen
if ( predictionOriginError.Length() > 64.0f ) {
if ( net_showPredictionError.GetInteger() == entityNumber ) {
renderSystem->DebugGraph( 1.0f, 0.0f, 1.0f, colorRed );
}
idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
predictedOrigin = renderOrigin;
predictedAngles = viewAngles;
predictedFrame = gameLocal.framenum;
return;
}
if ( net_showPredictionError.GetInteger() == entityNumber ) {
renderSystem->DebugGraph( predictionOriginError.Length(), 0.0f, 100.0f, colorGreen );
renderSystem->DebugGraph( predictionAnglesError.Length(), 0.0f, 180.0f, colorBlue );
}
}
int t = gameLocal.time - predictionErrorTime;
float f = ( net_predictionErrorDecay.GetFloat() - t ) / net_predictionErrorDecay.GetFloat();
if ( f > 0.0f && f < 1.0f ) {
predictedOrigin = renderOrigin + f * predictionOriginError;
predictedAngles = viewAngles + f * predictionAnglesError;
predictedAngles.Normalize180();
} else {
predictedOrigin = renderOrigin;
predictedAngles = viewAngles;
}
predictedFrame = gameLocal.framenum;
}
viewAngles = predictedAngles;
// adjust them now so they are right for the bound objects ( head and weapon )
AdjustBodyAngles();
predictedUpdated = true;
}
/*
===============
idPlayer::WantSmoothing
===============
*/
bool idPlayer::WantSmoothing( void ) const {
if ( !gameLocal.isClient ) {
return false;
}
if ( net_predictionErrorDecay.GetFloat() <= 0.0f ) {
return false;
}
return true;
}
/*
================
idPlayer::GetPhysicsToVisualTransform
================
*/
bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if ( af.IsActive() ) {
af.GetPhysicsToVisualTransform( origin, axis );
return true;
}
if ( vehicleController.IsDriving() ) {
vehicleController.GetDriverPosition( origin, axis );
origin.Zero();
return true;
}
PredictionErrorDecay();
// smoothen the rendered origin and angles of other clients
if ( gameLocal.framenum >= predictedFrame && WantSmoothing() ) {
axis = idAngles( 0.0f, predictedAngles.yaw, 0.0f ).ToMat3();
origin = ( predictedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
} else {
axis = viewAxis;
origin = modelOffset;
}
return true;
}
/*
================
idPlayer::GetPhysicsToSoundTransform
================
*/
bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
idCamera *camera;
if ( privateCameraView ) {
camera = privateCameraView;
} else {
camera = gameLocal.GetCamera();
}
if ( camera ) {
renderView_t view;
memset( &view, 0, sizeof( view ) );
camera->GetViewParms( &view );
origin = view.vieworg;
axis = view.viewaxis;
return true;
} else {
return idActor::GetPhysicsToSoundTransform( origin, axis );
}
}
/*
================
idPlayer::WriteToSnapshot
================
*/
void idPlayer::WriteToSnapshot( idBitMsgDelta &msg ) const {
assert( !IsFakeClient() );
physicsObj.WriteToSnapshot( msg );
WriteBindToSnapshot( msg );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
msg.WriteShort( health );
msg.WriteByte( inventory.armor );
msg.WriteBits( lastDamageDef, gameLocal.entityDefBits );
msg.WriteDir( lastDamageDir, 9 );
msg.WriteShort( lastDamageLocation );
msg.WriteBits( idealWeapon, -idMath::BitsForInteger( MAX_WEAPONS ) );
msg.WriteBits( inventory.weapons, MAX_WEAPONS );
msg.WriteBits( weaponViewModel.GetSpawnId(), 32 );
msg.WriteBits( weaponWorldModel.GetSpawnId(), 32 );
msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
msg.WriteBits( weaponGone, 1 );
msg.WriteBits( isLagged, 1 );
msg.WriteBits( isChatting, 1 );
msg.WriteLong( connectTime );
msg.WriteByte( lastKiller ? lastKiller->entityNumber : 255 );
if ( weapon ) {
msg.WriteBits( 1, 1 );
weapon->WriteToSnapshot( msg );
} else {
msg.WriteBits( 0, 1 );
}
msg.WriteBits( inBuyZone, 1 );
msg.WriteLong( (int)buyMenuCash );
}
/*
================
idPlayer::ReadFromSnapshot
================
*/
void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
assert( !IsFakeClient() );
int i, oldHealth, newIdealWeapon, weaponSpawnId, weaponWorldSpawnId;
bool stateHitch;
int lastKillerEntity;
if ( snapshotSequence - lastSnapshotSequence > 1 ) {
stateHitch = true;
} else {
stateHitch = false;
}
lastSnapshotSequence = snapshotSequence;
oldHealth = health;
physicsObj.ReadFromSnapshot( msg );
ReadBindFromSnapshot( msg );
deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
health = msg.ReadShort();
inventory.armor = msg.ReadByte();
lastDamageDef = msg.ReadBits( gameLocal.entityDefBits );
lastDamageDir = msg.ReadDir( 9 );
lastDamageLocation = msg.ReadShort();
newIdealWeapon = msg.ReadBits( -idMath::BitsForInteger( MAX_WEAPONS ) );
inventory.weapons = msg.ReadBits( MAX_WEAPONS );
weaponSpawnId = msg.ReadBits( 32 );
weaponWorldSpawnId = msg.ReadBits( 32 );
int latchedSpectator = spectator;
spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
if ( spectating && latchedSpectator != spectator ) {
// don't do any smoothing with this snapshot
predictedFrame = gameLocal.framenum;
if ( this == gameLocal.GetLocalPlayer() ) {
// this is where the client updates their spectated player
if ( gameLocal.gameType == GAME_TOURNEY ) {
rvTourneyArena& arena = ((rvTourneyGameState*)gameLocal.mpGame.GetGameState())->GetArena( GetArena() );
if( arena.GetPlayers()[ 0 ] == NULL || arena.GetPlayers()[ 1 ] == NULL || (spectator != arena.GetPlayers()[ 0 ]->entityNumber && spectator != arena.GetPlayers()[ 1 ]->entityNumber) ) {
gameLocal.mpGame.tourneyGUI.ArenaSelect( GetArena(), TGH_BRACKET );
} else if( spectator == arena.GetPlayers()[ 0 ]->entityNumber ) {
gameLocal.mpGame.tourneyGUI.ArenaSelect( GetArena(), TGH_PLAYER_ONE );
} else if( spectator == arena.GetPlayers()[ 1 ]->entityNumber ) {
gameLocal.mpGame.tourneyGUI.ArenaSelect( GetArena(), TGH_PLAYER_TWO );
}
gameLocal.mpGame.tourneyGUI.UpdateScores();
}
if ( gameLocal.entities[ spectator ] ) {
idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ spectator ] );
p->UpdateHudWeapon( p->currentWeapon );
if ( p->weapon ) {
p->weapon->SpectatorCycle();
}
}
}
}
weaponGone = msg.ReadBits( 1 ) != 0;
isLagged = msg.ReadBits( 1 ) != 0;
isChatting = msg.ReadBits( 1 ) != 0;
connectTime = msg.ReadLong();
lastKillerEntity = msg.ReadByte();
if( lastKillerEntity >= 0 && lastKillerEntity < MAX_CLIENTS) {
lastKiller = static_cast<idPlayer *>(gameLocal.entities[ lastKillerEntity ]);
} else {
lastKiller = NULL;
}
bool weaponChange = ( idealWeapon != newIdealWeapon );
if ( gameLocal.clientAckSequence < clientIdealWeaponPredictFrame ) {
// we have predicted a weapon change, and sent a reliable message to the server about it
// but it has not been processed yet, so ignore what may be contradictory information here
weaponChange = false;
} else {
clientIdealWeaponPredictFrame = -1;
}
if ( weaponChange ) {
if ( stateHitch ) {
weaponCatchup = true;
}
idealWeapon = newIdealWeapon;
StopFiring();
UpdateHudWeapon();
usercmd.buttons &= (~BUTTON_ATTACK);
clientIdealWeaponPredictFrame = -1;
}
// Attach the world and view entities
if ( weaponWorldModel.GetSpawnId() != weaponWorldSpawnId || weaponViewModel.GetSpawnId() != weaponSpawnId ) {
SetWeapon( -1 );
if ( weaponWorldModel.SetSpawnId( weaponWorldSpawnId ) && weaponViewModel.SetSpawnId( weaponSpawnId ) ) {
weaponCatchup = true;
}
}
// rjohnson: instance persistance information
if ( weaponWorldModel.IsValid() ) {
weaponWorldModel->fl.persistAcrossInstances = true;
weaponWorldModel->SetInstance( GetInstance() );
}
if ( weaponViewModel.IsValid() ) {
weaponViewModel->fl.persistAcrossInstances = true;
weaponViewModel->SetInstance( GetInstance() );
}
// Clear the weapon for clients entering PVS
if ( fl.networkStale && currentWeapon != idealWeapon && entityNumber != gameLocal.localClientNum && weaponWorldModel.IsValid() && weaponViewModel.IsValid() ) {
// We've probably already mispredicted a few frames without the client.
// Clearing the weapon here (during snapshot reading) prevents
// the wrong weapon from getting ClientUnstale()
// Weapon_Combat will set the proper weapon.
SetWeapon( -1 );
weaponCatchup = true;
}
// If we have a weapon then update it from the snapshot, otherwise
// we just skip whatever it would have read if it were there
if ( msg.ReadBits( 1 ) ) {
if ( weapon ) {
weapon->ReadFromSnapshot( msg );
} else {
rvWeapon::SkipFromSnapshot( msg );
}
}
inBuyZone = msg.ReadBits( 1 ) != 0;
int cash = msg.ReadLong();
if ( cash != (int)buyMenuCash ) {
buyMenuCash = (float)cash;
gameLocal.mpGame.RedrawLocalBuyMenu();
}
// no msg reading below this
// if not a local client assume the client has all ammo types
// don't do this for server demos because they have the player states included
if ( !gameLocal.isRepeater && (entityNumber != gameLocal.GetDemoFollowClient()) && !IsLocalClient() && !IsSpectatedClient() ) {
for( i = 0; i < MAX_AMMO; i++ ) {
inventory.ammo[ i ] = -1;
}
}
if ( oldHealth > 0 && health <= 0 ) {
if ( stateHitch ) {
// so we just hide and don't show a death skin
UpdateDeathSkin( true );
}
// die
pfl.dead = true;
ClearPowerUps();
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Dead", 4 );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Dead", 4 );
animator.ClearAllJoints();
StartRagdoll();
physicsObj.SetMovementType( PM_DEAD );
if ( !stateHitch ) {
StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
}
const idDeclEntityDef* def = static_cast<const idDeclEntityDef*>(declManager->DeclByIndex ( DECL_ENTITYDEF, lastDamageDef ));
if ( def ) {
// TODO: get attackers push scale?
InitDeathPush ( lastDamageDir, lastDamageLocation, &def->dict, 1.0f );
ClientDamageEffects ( def->dict, lastDamageDir, ( oldHealth - health ) * 4 );
}
// gib them here
if ( health < -20 || ( lastKiller && lastKiller->PowerUpActive( POWERUP_QUADDAMAGE )) ) {
ClientGib( lastDamageDir );
}
if ( weapon ) {
weapon->OwnerDied();
// Get rid of the weapon now
delete weapon;
weapon = NULL;
currentWeapon = -1;
}
} else if ( oldHealth <= 0 && health > 0 ) {
// respawn
//common->DPrintf( "idPlayer::ReadFromSnapshot() - Player respawn detected for %d '%s' - re-enabling clip\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "" );
// this is the first time we've seen the player since we heard he died - he may have picked up
// some powerups since he actually spawned in, so restore those
int latchPowerup = inventory.powerups;
Init();
inventory.powerups = latchPowerup;
StopRagdoll();
SetPhysics( &physicsObj );
physicsObj.EnableClip();
SetCombatContents( true );
} else if ( oldHealth - health > 2 && health > 0 ) {
if ( stateHitch ) {
lastDmgTime = gameLocal.time;
} else {
// damage feedback
const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
if ( def ) {
ClientDamageEffects ( def->dict, lastDamageDir, oldHealth - health );
pfl.pain = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
lastDmgTime = gameLocal.time;
} else {
common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
}
}
}
if ( oldHealth > 0 && health > 0 && IsHidden() && !spectating ) {
// ensure the client is shown after the initial spawn frame
Show();
}
if ( msg.HasChanged() ) {
UpdateVisuals();
}
}
/*
================
idPlayer::WritePlayerStateToSnapshot
================
*/
void idPlayer::WritePlayerStateToSnapshot( int lastSnapshotFrame, idBitMsgDelta &msg ) const {
int i;
// write out a flag if a hit sound was triggered since last snap
msg.WriteBits( lastHitFrame > lastSnapshotFrame ? 1 : 0, 1 );
msg.WriteBits( lastHitArmor, 1 );
msg.WriteDeltaByte( 0, bobCycle );
msg.WriteDeltaLong( 0, stepUpTime );
msg.WriteDeltaFloat( 0.0f, stepUpDelta );
msg.WriteShort( inventory.weapons );
msg.WriteByte( inventory.armor );
msg.WriteShort( inventory.powerups );
for( i = 0; i < MAX_AMMO; i++ ) {
// send a value of -1 as the max positive value as we have ASYNC_PLAYER_INV_AMMO_BITS>0
if ( inventory.ammo[i] == -1 ) {
msg.WriteBits( ( 1 << ASYNC_PLAYER_INV_AMMO_BITS ) - 1, ASYNC_PLAYER_INV_AMMO_BITS );
} else {
msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
}
}
for ( i = 0; i < POWERUP_MAX; i ++ ) {
msg.WriteLong( inventory.powerupEndTime[ i ] );
}
}
/*
================
idPlayer::ReadPlayerStateFromSnapshot
================
*/
void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
int i, ammo;
bool trigger = ( msg.ReadBits( 1 ) != 0 );
bool armor = ( msg.ReadBits( 1 ) != 0 );
if ( trigger ) {
TriggerHitSound( armor );
}
bobCycle = msg.ReadDeltaByte( 0 );
stepUpTime = msg.ReadDeltaLong( 0 );
stepUpDelta = msg.ReadDeltaFloat( 0.0f );
inventory.weapons = msg.ReadShort();
inventory.armor = msg.ReadByte();
inventory.powerups = msg.ReadShort();
for( i = 0; i < MAX_AMMO; i++ ) {
ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
if ( gameLocal.time >= inventory.ammoPredictTime ) {
if ( ammo == ( 1 << ASYNC_PLAYER_INV_AMMO_BITS ) - 1 ) {
inventory.ammo[ i ] = -1;
} else {
inventory.ammo[ i ] = ammo;
}
}
}
int powerup_max = POWERUP_MAX;
for ( i = 0; i < powerup_max; i ++ ) {
inventory.powerupEndTime[ i ] = msg.ReadLong();
}
while ( i < POWERUP_MAX ) {
inventory.powerupEndTime[ i ] = 0;
i++;
}
if ( gameLocal.IsMultiplayer() ) {
if ( (inventory.weapons&~oldInventoryWeapons) ) {
//added a weapon from inventory, bring up bar
UpdateHudWeapon();
}
oldInventoryWeapons = inventory.weapons;
}
}
/*
================
idPlayer::ServerReceiveEvent
================
*/
bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
return true;
}
// client->server events
switch ( event ) {
case EVENT_IMPULSE: {
int impulse = msg.ReadBits( IMPULSE_NUMBER_OF_BITS );
serverReceiveEvent = true; // marking so we know if ACKs are needed
PerformImpulse( impulse );
serverReceiveEvent = false;
return true;
}
case EVENT_EMOTE: {
// forward the emote on to all clients except the one that sent it to us
ServerSendInstanceEvent( EVENT_EMOTE, &msg, false, entityNumber );
// Set the emote locally
SetEmote( (playerEmote_t)msg.ReadByte() );
return true;
}
default: {
return false;
}
}
}
/*
===============
idPlayer::CheckAckReply
===============
*/
void idPlayer::CheckAckReply( void ) const {
assert( !gameLocal.isClient );
if ( serverReceiveEvent ) {
idBitMsg outMsg;
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_EVENT_ACK );
outMsg.WriteLong( gameLocal.framenum );
networkSystem->ServerSendReliableMessage( entityNumber, outMsg );
}
}
/*
================
idPlayer::ClientReceiveEvent
================
*/
bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
int powerup;
bool start;
switch ( event ) {
case EVENT_EXIT_TELEPORTER:
Event_ExitTeleporter();
return true;
case EVENT_ABORT_TELEPORTER:
SetPrivateCameraView( NULL );
return true;
case EVENT_POWERUP: {
powerup = msg.ReadShort();
start = ( msg.ReadBits( 1 ) != 0 );
if ( start ) {
bool team = ( msg.ReadBits( 1 ) != 0 );
GivePowerUp( powerup, 0, team );
} else {
ClearPowerup( powerup );
}
return true;
}
case EVENT_SPECTATE: {
bool spectate = ( msg.ReadBits( 1 ) != 0 );
// force to spectator if we got this event about a client in a different
// instance
Spectate( spectate );
// spectate might re-link clip for stale players, so re-call ClientStale if we're stale
if ( fl.networkStale ) {
ClientStale();
}
return true;
}
case EVENT_ADD_DAMAGE_EFFECT: {
if ( spectating ) {
// if we're spectating, ignore
// happens if the event and the spectate change are written on the server during the same frame (fraglimit)
return true;
}
return idActor::ClientReceiveEvent( event, time, msg );
}
case EVENT_EMOTE: {
// Set the emote locally
SetEmote( (playerEmote_t)msg.ReadByte() );
return true;
}
case EVENT_JUMP: {
// jumps for the local client are predicted
if ( IsLocalClient() ) {
return true;
}
StartSound( "snd_jump", (s_channelType)FC_SOUND, 0, false, NULL );
return true;
}
default: {
return idActor::ClientReceiveEvent( event, time, msg );
}
}
}
/*
================
idPlayer::Hide
================
*/
void idPlayer::Hide( void ) {
idActor::Hide();
if ( weapon ) {
weapon->HideWorldModel( );
}
}
/*
================
idPlayer::Show
================
*/
void idPlayer::Show( void ) {
idActor::Show();
if ( weapon ) {
weapon->ShowWorldModel( );
}
}
/*
===============
idPlayer::ShowTip
===============
*/
void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
if ( tipUp ) {
return;
}
hud->SetStateString( "tip", tip );
hud->SetStateString( "tiptitle", title );
hud->HandleNamedEvent( "tipWindowUp" );
if ( autoHide ) {
PostEventSec( &EV_Player_HideTip, 5.0f );
}
tipUp = true;
}
/*
===============
idPlayer::HideTip
===============
*/
void idPlayer::HideTip( void ) {
hud->HandleNamedEvent( "tipWindowDown" );
tipUp = false;
}
/*
===============
idPlayer::Event_HideTip
===============
*/
void idPlayer::Event_HideTip( void ) {
HideTip();
}
/*
===============
idPlayer::ShowObjective
===============
*/
void idPlayer::ShowObjective( const char *obj ) {
objectiveSystem->HandleNamedEvent( obj );
objectiveUp = true;
}
/*
===============
idPlayer::HideObjective
===============
*/
void idPlayer::HideObjective( void ) {
objectiveSystem->HandleNamedEvent( "closeObjective" );
objectiveUp = false;
}
/*
===============
idPlayer::SetSpectateOrigin
===============
*/
void idPlayer::SetSpectateOrigin( void ) {
idVec3 neworig;
neworig = GetPhysics()->GetOrigin();
neworig[ 2 ] += EyeHeight();
neworig[ 2 ] += 25;
SetOrigin( neworig );
}
/*
===============
idPlayer::RemoveWeapon
===============
*/
void idPlayer::RemoveWeapon( const char *weap ) {
if ( weap && *weap ) {
inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
}
}
/*
===============
idPlayer::CanShowWeaponViewmodel
===============
*/
bool idPlayer::CanShowWeaponViewmodel( void ) const {
return showWeaponViewModel;
}
/*
===============
idPlayer::SetLevelTrigger
===============
*/
void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
if ( levelName && *levelName && triggerName && *triggerName ) {
idLevelTriggerInfo lti;
lti.levelName = levelName;
lti.triggerName = triggerName;
inventory.levelTriggers.Append( lti );
}
}
/*
===============
idPlayer::Event_LevelTrigger
===============
*/
void idPlayer::Event_LevelTrigger( void ) {
idStr mapName = gameLocal.GetMapName();
mapName.StripPath();
mapName.StripFileExtension();
for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
if ( ent ) {
ent->PostEventMS( &EV_Activate, 1, this );
}
}
}
}
/*
================
idPlayer::ToggleFlashlight
================
*/
void idPlayer::ToggleFlashlight ( void ) {
// Dead people can use flashlights
// RAVEN BEGIN
// mekberg: check to see if the weapon is enabled.
if ( health <= 0 || !weaponEnabled ) {
return;
}
// RAVEN END
int flashlightWeapon = currentWeapon;
if ( !spawnArgs.GetBool( va( "weapon%d_flashlight", flashlightWeapon ) ) ) {
// TODO: find the first flashlight weapon that has ammo starting at the bottom
for( flashlightWeapon = MAX_WEAPONS - 1; flashlightWeapon >= 0; flashlightWeapon-- ) {
if ( inventory.weapons & ( 1 << flashlightWeapon ) ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", flashlightWeapon ) );
int ammo = inventory.ammo[inventory.AmmoIndexForWeaponClass ( weap ) ];
if ( !ammo ) {
continue;
}
if ( spawnArgs.GetBool ( va ( "weapon%d_flashlight", flashlightWeapon ) ) ) {
break;
}
}
}
// Couldnt find flashlight
if ( flashlightWeapon < 0 ) {
return;
}
}
// If the current weapon isnt the flashlight then always force the flashlight on
if ( flashlightWeapon != idealWeapon ) {
flashlightOn = true;
idealWeapon = flashlightWeapon;
// Inform the weapon to toggle the flashlight, this will eventually cause the players
// Flashlight method to be called
} else if ( weapon ) {
weapon->Flashlight ( );
}
}
/*
================
idPlayer::Flashlight
================
*/
void idPlayer::Flashlight ( bool on ) {
flashlightOn = on;
}
/*
================
idPlayer::DamageFeedback
================
*/
void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
assert( !gameLocal.isClient );
//rvTramCars weren't built on the idActor inheritance hierarchy but need to be treated like one when shot.
//TODO: Maybe add a key to entity flags that will allow them to be shot as actors even if they aren't actors?
if ( !victim || ( !victim->IsType( idActor::GetClassType() ) && !victim->IsType( rvTramCar::GetClassType() ) ) || victim->health <= 0 ) {
return;
}
bool armorHit = false;
if ( gameLocal.isMultiplayer && victim->IsType( idPlayer::GetClassType() ) ) {
if ( this == victim ) {
// no feedback for self hits
return;
}
idPlayer *p = static_cast< idPlayer * >( victim );
if ( gameLocal.IsTeamGame() && p->team == team ) {
// no feedback for team hits
return;
}
if ( p->inventory.armor > 0 ) {
armorHit = true;
}
}
TriggerHitSound( armorHit );
}
/*
==============
idPlayer::GetWeaponDef
==============
*/
const idDeclEntityDef* idPlayer::GetWeaponDef ( int weaponIndex ) {
if ( cachedWeaponDefs[weaponIndex] ) {
return cachedWeaponDefs[weaponIndex];
}
idStr weapon;
weapon = spawnArgs.GetString ( va("def_weapon%d", weaponIndex ) );
if ( !weapon.Length() ) {
return NULL;
}
cachedWeaponDefs[weaponIndex] = gameLocal.FindEntityDef ( weapon, false );
if ( !cachedWeaponDefs[weaponIndex] ) {
gameLocal.Error( "Could not find weapon definition '%s'", weapon.c_str() );
}
return cachedWeaponDefs[weaponIndex];
}
/*
==============
idPlayer::GetPowerupDef
Returns the powerup dictionary for the given powerup index. The dictionary is cached to ensure a
speedy retrieval after the first call.
==============
*/
const idDeclEntityDef* idPlayer::GetPowerupDef ( int powerupIndex ) {
const idDict* types;
int i;
int num;
if ( cachedPowerupDefs[powerupIndex] ) {
return cachedPowerupDefs[powerupIndex];
}
types = gameLocal.FindEntityDefDict( "powerup_types", false );
if ( !types ) {
gameLocal.Error( "Could not find entity definition for 'powerup_types'" );
}
num = types->GetNumKeyVals();
for( i = 0; i < num; i++ ) {
const idKeyValue* kv;
kv = types->GetKeyVal( i );
if ( atoi(kv->GetValue()) == powerupIndex ) {
cachedPowerupDefs[powerupIndex] = gameLocal.FindEntityDef ( kv->GetKey(), false );
if ( !cachedPowerupDefs[powerupIndex] ) {
gameLocal.Error( "Could not find powerup definition '%s'", kv->GetKey().c_str() );
}
return cachedPowerupDefs[powerupIndex];
}
}
gameLocal.Error( "Could not find powerup definition '%d'", powerupIndex );
return NULL;
}
/*
==============
idPlayer::discoverSecretArea
Announces a secret area and increases the secret area tally
==============
*/
void idPlayer::DiscoverSecretArea(const char* _description) {
//increment the secret area tally
inventory.secretAreasDiscovered++;
}
/*
==============
idPlayer::StartBossBattle
Starts a boss battle with the given entity. During a boss battle the health of the boss
will be displayed on the HUD
==============
*/
void idPlayer::StartBossBattle ( idEntity* enemy ) {
bossEnemy = enemy;
idUserInterface *hud_ = GetHud();
if ( hud_ ) {
hud_->SetStateInt ( "boss_maxhealth", enemy->health );
hud_->HandleNamedEvent ( "showBossBar" );
}
}
/*
=====================
idPlayer::SetInitialHud
=====================
*/
void idPlayer::SetInitialHud ( void ) {
if ( !mphud || !gameLocal.isMultiplayer || gameLocal.GetLocalPlayer() != this ) {
return;
}
mphud->SetStateInt( "gametype", gameLocal.gameType );
if( hud ) {
hud->SetStateInt( "gametype", gameLocal.gameType );
}
mphud->HandleNamedEvent( "InitHud" );
mphud->HandleNamedEvent( "TeamChange" );
if( gameLocal.IsFlagGameType() ) {
mphud->SetStateFloat( "ap", gameLocal.mpGame.assaultPoints.Num() );
for( int i = 0; i < TEAM_MAX; i++ ) {
mphud->SetStateInt( "team", i );
if( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( i ) == FS_DROPPED ) {
mphud->HandleNamedEvent( "flagDrop" );
} else if( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( i ) == FS_TAKEN ) {
mphud->HandleNamedEvent( "flagTaken" );
} else if( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( i ) == FS_TAKEN_MARINE ) {
mphud->SetStateInt( "team", TEAM_MARINE );
mphud->HandleNamedEvent( "flagTaken" );
} else if( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( i ) == FS_TAKEN_STROGG ) {
mphud->SetStateInt( "team", TEAM_STROGG );
mphud->HandleNamedEvent( "flagTaken" );
} else if( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( i ) == FS_AT_BASE ) {
mphud->SetStateInt( "team", i );
mphud->HandleNamedEvent( "flagReturn" );
}
}
for( int i = 0; i < gameLocal.mpGame.assaultPoints.Num(); i++ ) {
mphud->SetStateFloat( "apindex", i );
//mphud->SetStateInt( "apteam", ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetAPOwner( i ) );
mphud->StateChanged( gameLocal.time );
mphud->HandleNamedEvent( "APCaptured" );
}
}
mphud->StateChanged ( gameLocal.time );
}
void idPlayer::RemoveClientModel ( const char *entityDefName ) {
rvClientEntity* cent;
rvClientEntity* next;
for( cent = clientEntities.Next(); cent != NULL; cent = next ) {
next = cent->bindNode.Next();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( cent->IsType ( rvClientModel::GetClassType() ) ) {
// RAVEN END
if ( !idStr::Icmp ( ( static_cast<rvClientModel*> ( cent ) )->GetClassname(), entityDefName ) ) {
cent->Unbind ( );
delete cent;
}
}
}
}
rvClientEntityPtr<rvClientModel> idPlayer::AddClientModel ( const char* entityDefName, const char* shaderName ) {
rvClientEntityPtr<rvClientModel> ptr;
ptr = NULL;
if ( entityDefName == NULL ) {
return ptr;
}
const idDict* entityDef = gameLocal.FindEntityDefDict ( entityDefName, false );
if ( entityDef == NULL ) {
return ptr;
}
rvClientModel *newModel = NULL;
gameLocal.SpawnClientEntityDef( *entityDef, (rvClientEntity**)(&newModel), false, "rvClientModel" );
if( newModel == NULL ) {
return ptr;
}
idMat3 rotation;
rotation = entityDef->GetAngles( "angles" ).ToMat3();
newModel->SetAxis( rotation );
newModel->SetOrigin( entityDef->GetVector( "origin" ) * rotation );
newModel->Bind ( this, animator.GetJointHandle( entityDef->GetString ( "joint" ) ) );
newModel->SetCustomShader ( shaderName );
newModel->GetRenderEntity()->suppressSurfaceInViewID = entityNumber + 1;
newModel->GetRenderEntity()->noSelfShadow = true;
newModel->GetRenderEntity()->noShadow = true;
ptr = newModel;
return ptr;
}
void idPlayer::RemoveClientModels ( void ) {
rvClientEntity* cent;
rvClientEntity* next;
for( cent = clientEntities.Next(); cent != NULL; cent = next ) {
next = cent->bindNode.Next();
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
if ( cent->IsType ( rvClientModel::GetClassType() ) ) {
// RAVEN END
cent->Unbind ( );
delete cent;
}
}
}
/*
=====================
idPlayer::ClientGib()
ddynerman: Spawns client side gibs around this player
=====================
*/
void idPlayer::ClientGib( const idVec3& dir ) {
if( !spawnArgs.GetBool( "gib" ) ) {
return;
}
int i;
idVec3 entityCenter, velocity;
idList<rvClientMoveable *> list;
// hide the player
SetSkin( gibSkin );
//and the head
if( gameLocal.isMultiplayer ) {
if( clientHead ) {
clientHead->UnlinkCombat();
delete clientHead;
}
} else {
if ( head.GetEntity() ) {
head.GetEntity()->Hide();
}
}
// blow out the gibs in the given direction away from the center of the entity
// spawn gib client models
rvClientMoveable::SpawnClientMoveables( this, "clientgib", &list );
entityCenter = GetPhysics()->GetAbsBounds().GetCenter();
for ( i = 0; i < list.Num(); i++ ) {
list[i]->GetPhysics()->SetContents( CONTENTS_CORPSE );
// we don't want collision on gibs
//list[i]->GetPhysics()->SetClipMask( CONTENTS_SOLID );
velocity = list[i]->GetPhysics()->GetAbsBounds().GetCenter() - entityCenter;
velocity.NormalizeFast();
velocity += ( i & 1 ) ? dir : -dir;
list[i]->GetPhysics()->ApplyImpulse( 0, list[i]->GetPhysics()->GetOrigin(), velocity * ( 25000.0f + ( gameLocal.random.RandomFloat() * 50000.0f)) );
// list[i]->GetPhysics()->SetLinearVelocity( velocity * ( 25.0f + ( gameLocal.random.RandomFloat() * 300.0f)));
list[i]->GetPhysics()->SetAngularVelocity( velocity * ( -250.0f + ( gameLocal.random.RandomFloat() * 500.0f)));
list[i]->GetRenderEntity()->noShadow = true;
list[i]->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
list[i]->PostEventMS( &CL_FadeOut, SEC2MS( 4.0f ), SEC2MS( 2.0f ) );
}
//play gib fx
gameLocal.PlayEffect( spawnArgs, "fx_gib", GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
// gibs are PVS agnostic. If we gib a player outside of our PVS, set the oldHealth
// to below 0 so when this player re-appears in our snap we respawn him
if( gameLocal.isClient && gameLocal.GetLocalPlayer() && health > 0 ) {
health = -100;
}
}
/*
=====================
idPlayer::CanDamage
=====================
*/
bool idPlayer::CanDamage( const idVec3 &origin, idVec3 &damagePoint, idEntity *ignoreEnt ) {
if( gameLocal.isMultiplayer && health <= 0 ) {
return false;
}
return idActor::CanDamage( origin, damagePoint, ignoreEnt );
}
/*
=====================
idPlayer::ClientDamageEffects
=====================
*/
void idPlayer::ClientDamageEffects ( const idDict& damageDef, const idVec3& dir, int damage ) {
idVec3 from;
idVec3 localDir;
float fadeDB;
// Only necessary on clients
if ( gameLocal.isMultiplayer && !gameLocal.isClient && !gameLocal.isListenServer ) {
return;
}
from = dir;
from.Normalize();
viewAxis.ProjectVector( from, localDir );
if ( damage ) {
// RAVEN BEGIN
// jnewquist: Controller rumble
idPlayer *p = gameLocal.GetLocalPlayer();
if ( p && ( p == this || ( p->spectating && p->spectator == entityNumber ) ) ) {
playerView.DamageImpulse( localDir, &damageDef, damage );
}
// RAVEN END
}
// Visual effects
if ( health > 0 && damage ) {
// Let the hud know about the hit
if ( hud ) {
hud->SetStateFloat ( "hitdir", localDir.ToAngles()[YAW] + 180.0f );
hud->HandleNamedEvent ( "playerHit" );
}
}
// Sound effects
if ( damageDef.GetFloat ( "hl_volumeDB", "-40", fadeDB ) ) {
float fadeTime;
fadeTime = 0.0f;
if ( !pfl.hearingLoss ) {
const char* fade;
fadeTime = damageDef.GetFloat ( "hl_fadeOutTime", ".25" );
soundSystem->FadeSoundClasses( SOUNDWORLD_GAME, 0, fadeDB, fadeTime );
pfl.hearingLoss = true;
// sound overlayed?
if ( damageDef.GetString ( "snd_hl", "", &fade ) && *fade ) {
StartSoundShader ( declManager->FindSound ( fade ), SND_CHANNEL_DEMONIC, 0, false, NULL );
}
}
fadeTime += damageDef.GetFloat ( "hl_time", "1" );
CancelEvents ( &EV_Player_FinishHearingLoss );
PostEventSec ( &EV_Player_FinishHearingLoss, fadeTime, damageDef.GetFloat ( "hl_fadeInTime", ".25" ) );
}
}
/*
=====================
idPlayer::GetDebugInfo
=====================
*/
void idPlayer::GetDebugInfo ( debugInfoProc_t proc, void* userData ) {
idActor::GetDebugInfo ( proc, userData );
proc ( "idPlayer", "inventory.armor", va("%d", inventory.armor ), userData );
proc ( "idPlayer", "inventory.weapons", va("%d", inventory.weapons ), userData );
proc ( "idPlayer", "inventory.powerups", va("%d", inventory.powerups ), userData );
}
// RAVEN END
/*
=====================
idPlayer::ApplyImpulse
=====================
*/
void idPlayer::ApplyImpulse( idEntity *ent, int id, const idVec3 &point, const idVec3 &impulse, bool splash ) {
if( !ent) {
gameLocal.Warning( "idPlayer::ApplyImpulse called with null entity as instigator.");
return;
}
lastImpulsePlayer = NULL;
lastImpulseTime = gameLocal.time + 1000;
if( ent->IsType( idPlayer::Type ) && ent != this ) {
lastImpulsePlayer = static_cast<idPlayer*>(ent);
// RAVEN BEGIN
// jnewquist: Use accessor for static class type
} else if( ent->IsType( idProjectile::GetClassType() ) ) {
// RAVEN END
idEntity* owner = static_cast<idProjectile*>(ent)->GetOwner();
if( owner && owner->IsType( idPlayer::Type ) && owner != this ) {
lastImpulsePlayer = static_cast<idPlayer*>(owner);
}
}
idAFEntity_Base::ApplyImpulse( ent, id, point, impulse, splash );
}
/*
=====================
idPlayer::SetupHead
=====================
*/
void idPlayer::SetupHead( const char* headModel, idVec3 headOffset ) {
if( gameLocal.isMultiplayer ) {
// player's don't use idActor's real head entities - uses clientEntities instead
if( clientHead.GetEntity() ) {
delete clientHead.GetEntity();
clientHead = NULL;
}
if( spectating || (gameLocal.GetLocalPlayer() && instance != gameLocal.GetLocalPlayer()->GetInstance()) ) {
return;
}
const idDict* headDict = gameLocal.FindEntityDefDict( headModel, false );
if ( !headDict ) {
return;
}
rvClientAFAttachment* headEnt = clientHead.GetEntity();
gameLocal.SpawnClientEntityDef( *headDict, (rvClientEntity**)&headEnt, false );
if( headEnt ) {
idStr jointName = spawnArgs.GetString( "joint_head" );
jointHandle_t joint = animator.GetJointHandle( jointName );
if ( joint == INVALID_JOINT ) {
return;
}
headEnt->SetBody ( this, headDict->GetString ( "model" ), joint );
headEnt->SetOrigin( vec3_origin );
headEnt->SetAxis( mat3_identity );
headEnt->Bind( this, joint, true );
headEnt->InitCopyJoints();
// Spawn might have parsed a skin from the spawnargs, save it for future use here
headSkin = headEnt->GetRenderEntity()->customSkin;
clientHead = headEnt;
}
} else {
idActor::SetupHead( headModel, headOffset );
if ( head ) {
head->fl.persistAcrossInstances = true;
}
}
}
/*
=====================
idPlayer::GUIMainNotice
=====================
*/
void idPlayer::GUIMainNotice( const char* message, bool persist ) {
if( !gameLocal.isMultiplayer || !mphud ) {
return;
}
mphud->SetStateString( "main_notice_text", message );
mphud->SetStateBool( "main_notice_persist", persist );
mphud->StateChanged( gameLocal.time );
mphud->HandleNamedEvent( "main_notice" );
}
/*
=====================
idPlayer::GUIFragNotice
=====================
*/
void idPlayer::GUIFragNotice( const char* message, bool persist ) {
if( !gameLocal.isMultiplayer || !mphud ) {
return;
}
mphud->SetStateString( "frag_notice_text", message );
mphud->SetStateBool( "frag_notice_persist", persist );
mphud->StateChanged( gameLocal.time );
mphud->HandleNamedEvent( "frag_notice" );
}
/*
=====================
idPlayer::SetHudOverlay
=====================
*/
void idPlayer::SetHudOverlay( idUserInterface* overlay, int duration ) {
overlayHud = overlay;
overlayHudTime = gameLocal.time + duration;
}
// RAVEN BEGIN
// mekberg: wrap saveMessages
/*
=====================
idPlayer::SaveMessage
=====================
*/
void idPlayer::SaveMessage( void ) {
#ifndef _XENON
if ( GetHud( ) ) {
GetHud()->HandleNamedEvent( "saveMessage" );
}
if ( objectiveSystem ) {
objectiveSystem->HandleNamedEvent( "saveMessage" );
}
#endif
}
// mekberg: set pm_ cvars
/*
=====================
idPlayer::SetPMCVars
=====================
*/
void idPlayer::SetPMCVars( void ) {
const idKeyValue *kv;
if ( !gameLocal.isMultiplayer || gameLocal.isServer ) {
kv = spawnArgs.MatchPrefix( "pm_", NULL );
while( kv ) {
cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
kv = spawnArgs.MatchPrefix( "pm_", kv );
}
}
}
// RAVEN END
/*
=====================
idPlayer::GetSpawnClassname
=====================
*/
const char* idPlayer::GetSpawnClassname ( void ) {
idEntity* world;
const char* entityFilter;
// Test player def
if ( *g_testPlayer.GetString() ) {
return g_testPlayer.GetString ( );
}
// Multiplayer
if ( gameLocal.isMultiplayer ) {
return "player_marine_mp";
}
// See if the world spawn specifies a player
world = gameLocal.entities[ENTITYNUM_WORLD];
assert( world );
gameLocal.serverInfo.GetString( "si_entityFilter", "", &entityFilter );
if ( entityFilter && *entityFilter ) {
return world->spawnArgs.GetString( va("player_%s", entityFilter ), world->spawnArgs.GetString( "player", "player_marine" ) );
}
return world->spawnArgs.GetString( "player", "player_marine" );
}
/*
===============
idPlayer::SetInstance
===============
*/
void idPlayer::SetInstance( int newInstance ) {
common->DPrintf( "idPlayer::SetInstance() - Setting instance for '%s' to %d\n", name.c_str(), newInstance );
idEntity::SetInstance( newInstance );
if( head.GetEntity() ) {
head.GetEntity()->SetInstance( newInstance );
}
if( weapon ) {
if( weapon->GetViewModel() ) {
weapon->GetViewModel()->SetInstance( newInstance );
}
if( weapon->GetWorldModel() ) {
weapon->GetWorldModel()->SetInstance( newInstance );
}
}
if( weaponWorldModel ) {
weaponWorldModel->SetInstance( newInstance );
}
if( weaponViewModel ) {
weaponViewModel->SetInstance( newInstance );
}
// reschedule time announcements if needed
if( this == gameLocal.GetLocalPlayer() ) {
gameLocal.mpGame.ScheduleTimeAnnouncements();
if( gameLocal.isServer ) {
// remove/add heads on server
for( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = (idPlayer*)gameLocal.entities[ i ];
if( player ) {
if( player->instance != newInstance ) {
if( player->clientHead.GetEntity() ) {
delete player->clientHead;
player->clientHead = NULL;
}
} else {
player->UpdateModelSetup( true );
}
}
}
}
}
}
/*
===============
idPlayer::JoinInstance
===============
*/
void idPlayer::JoinInstance( int newInstance ) {
assert( gameLocal.isServer );
if( instance == newInstance ) {
return;
}
if( newInstance < 0 || newInstance >= MAX_ARENAS ) {
gameLocal.Warning( "idPlayer::JoinInstance() - Invalid instance %d specified\n", newInstance );
}
if( gameLocal.GetNumInstances() <= newInstance || gameLocal.GetInstance( newInstance ) == NULL ) {
// don't populate instance until player gets linked into the right one
gameLocal.AddInstance( newInstance );
if( this == gameLocal.GetLocalPlayer() ) {
// ensure InstanceLeave() gets called on newly spawned entities before
// InstanceJoin() gets called.
gameLocal.mpGame.ServerSetInstance( instance );
}
}
SetArena( newInstance );
SetInstance( newInstance );
gameLocal.GetInstance( newInstance )->JoinInstance( this );
}
/*
===============
idPlayer::SetEmote
===============
*/
void idPlayer::SetEmote( playerEmote_t newEmote ) {
emote = newEmote;
// if we're the ones generating the emote, pass it along
if( IsLocalClient() ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
assert( entityNumber == gameLocal.localClientNum );
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.BeginWriting();
msg.WriteByte( emote );
if( gameLocal.isServer ) {
ServerSendInstanceEvent( EVENT_EMOTE, &msg, false, -1 );
} else {
ClientSendEvent( EVENT_EMOTE, &msg );
}
}
}
/*
===============
idPlayer::GetGroundElevator
===============
*/
idEntity* idPlayer::GetGroundElevator( idEntity* testElevator ) const {
idEntity* groundEnt = GetGroundEntity();
if ( !groundEnt ) {
return NULL;
}
while ( groundEnt->GetBindMaster() ) {
groundEnt = groundEnt->GetBindMaster();
}
if ( !groundEnt->IsType( idElevator::GetClassType() ) ) {
return NULL;
}
//NOTE: for player, don't care if all the way on, or not
return groundEnt;
}
/*
===================
idPlayer::IsCrouching
===================
*/
bool idPlayer::IsCrouching( void ) const {
return physicsObj.IsCrouching();
}
/*
===============
idPlayer::SetArena
===============
*/
void idPlayer::SetArena( int newArena ) {
if( arena == newArena ) {
return;
}
arena = newArena;
if( gameLocal.GetLocalPlayer() == this && gameLocal.gameType == GAME_TOURNEY ) {
if( arena >= 0 && arena <= MAX_ARENAS ) {
if( arena < MAX_ARENAS ) {
// RAVEN BEGIN
// rhummer: localized these strings.
GUIMainNotice( va( common->GetLocalizedString( "#str_107270" ), newArena + 1 ) );
} else {
GUIMainNotice( common->GetLocalizedString( "#str_107271" ) );
}
// RAVEN END
if( gameLocal.GetLocalPlayer() ) {
gameLocal.mpGame.tourneyGUI.ArenaSelect( newArena, TGH_BRACKET );
}
gameLocal.mpGame.RemoveAnnouncerSoundRange( AS_TOURNEY_JOIN_ARENA_ONE, AS_TOURNEY_JOIN_ARENA_EIGHT );
gameLocal.mpGame.ScheduleAnnouncerSound( (announcerSound_t)(AS_TOURNEY_JOIN_ARENA_ONE + arena), gameLocal.time );
}
}
}
/*
===============
idPlayer::Event_DamageEffect
===============
*/
void idPlayer::Event_DamageEffect( const char *damageDefName, idEntity* _damageFromEnt )
{
const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( damageDef )
{
idVec3 dir = (_damageFromEnt!=NULL)?(GetEyePosition()-_damageFromEnt->GetEyePosition()):viewAxis[2];
dir.Normalize();
int damage = 1;
ClientDamageEffects( damageDef->dict, dir, damage );
if ( !g_testDeath.GetBool() ) {
lastDmgTime = gameLocal.time;
}
lastDamageDir = dir;
lastDamageDir.Normalize();
lastDamageDef = damageDef->Index();
lastDamageLocation = 0;
}
}
/*
===============
idPlayer::UpdateDeathShader
===============
*/
void idPlayer::UpdateDeathShader ( bool state_hitch ) {
if ( !doingDeathSkin && gameLocal.time > deathSkinTime && deathSkinTime ) {
deathSkinTime = 0;
deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
doingDeathSkin = true;
if ( state_hitch ) {
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
if( gameLocal.isMultiplayer ) {
if( clientHead ) {
clientHead.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
clientHead.GetEntity()->GetRenderEntity()->noShadow = true;
}
} else {
if( head ) {
head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
head.GetEntity()->GetRenderEntity()->noShadow = true;
}
}
} else {
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
if( gameLocal.isMultiplayer ) {
if( clientHead ) {
clientHead.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
clientHead.GetEntity()->GetRenderEntity()->noShadow = true;
}
} else {
if( head ) {
head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
head.GetEntity()->GetRenderEntity()->noShadow = true;
}
}
}
renderEntity.noShadow = true;
UpdateVisuals();
}
}
#if 0
/*
===============
idPlayer::Event_InitWeapon
===============
*/
void idPlayer::InitWeapon( void ) {
currentWeapon = -1;
SetWeapon( idealWeapon );
}
#endif
/*
===============
idPlayer::GetHitscanTint
===============
*/
const idVec4& idPlayer::GetHitscanTint( void ) {
assert( !IsFakeClient() );
if( gameLocal.IsTeamGame() ) {
if( gameLocal.serverInfo.GetInt( "si_allowHitscanTint" ) >= 2 ) {
if( team == TEAM_MARINE ) {
return marineHitscanTint;
} else if( team == TEAM_STROGG ) {
return stroggHitscanTint;
} else {
gameLocal.Error( "idPlayer::GetHitscanTint() - Unknown team '%d' on player %d '%s'\n", team, entityNumber, GetUserInfo()->GetString( "ui_name" ) );
}
} else {
return defaultHitscanTint;
}
}
if( gameLocal.serverInfo.GetInt( "si_allowHitscanTint" ) >= 1 ) {
return hitscanTint;
}
return defaultHitscanTint;
}
/*
===============
idPlayer::IsReady
===============
*/
bool idPlayer::IsReady( void ) {
return !gameLocal.serverInfo.GetBool( "si_useReady" ) || ready || forcedReady;
}
/*
===============
idPlayer::ForceScoreboard
===============
*/
void idPlayer::ForceScoreboard( bool force, int time ) {
forceScoreBoard = force;
forceScoreBoardTime = time;
}
/*
===============
idPlayer::GetTextTourneyStatus
===============
*/
const char* idPlayer::GetTextTourneyStatus( void ) {
if( tourneyStatus == PTS_ADVANCED ) {
return common->GetLocalizedString( "#str_107740" );
} else if( tourneyStatus == PTS_ELIMINATED ) {
return common->GetLocalizedString( "#str_107729" );
} else if( tourneyStatus == PTS_PLAYING ) {
return common->GetLocalizedString( "#str_107728" );
} else if( tourneyStatus == PTS_UNKNOWN ) {
return common->GetLocalizedString( "#str_107739" );
}
return "UNKNOWN TOURNEY STATUS";
}
/*
===============
idPlayer::ClientInstanceJoin
Players know about all other players, even in other instances
We need to hide/show them on the client as we switch to/from instances
===============
*/
void idPlayer::ClientInstanceJoin( void ) {
assert( gameLocal.isClient );
common->DPrintf( "idPlayer::ClientInstanceJoin() - client %d ('%s') is being shown\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "?" );
// restore client
Spectate( spectating, true );
}
/*
===============
idPlayer::ClientInstanceLeave
Players know about all other players, even in other instances
We need to hide/show them on the client as we switch to/from instances
===============
*/
void idPlayer::ClientInstanceLeave( void ) {
assert( gameLocal.isClient );
common->DPrintf( "idPlayer::ClientInstanceLeave() - client %d ('%s') is being hidden\n", entityNumber, GetUserInfo() ? GetUserInfo()->GetString( "ui_name" ) : "?" );
// force client to spectate
Spectate( spectating, true );
}
/*
===============
idPlayer::ClientStale
===============
*/
bool idPlayer::ClientStale( void ) {
idEntity::ClientStale();
// remove all powerup effects
for( int i = 0; i < POWERUP_MAX; i++ ) {
if( inventory.powerups & ( 1 << i ) ) {
StopPowerUpEffect( i );
}
}
if( clientHead ) {
delete clientHead;
clientHead = NULL;
}
Hide();
// never delete client
return false;
}
/*
===============
idPlayer::ClientUnstale
===============
*/
void idPlayer::ClientUnstale( void ) {
idEntity::ClientUnstale();
// force render ent to position
renderEntity.axis = physicsObj.GetAxis();
renderEntity.origin = physicsObj.GetOrigin();
// don't do any smoothing with this snapshot
predictedFrame = gameLocal.framenum;
// the powerup effects ( rvClientEntity ) will do some bindings, which in turn will call GetPosition
// which uses the predictedOrigin .. which won't be updated till we Think() so just don't leave the predictedOrigin to the old position
predictedOrigin = renderEntity.origin;
// restart powerup effects on clients that are coming back into our snapshot
int i;
for ( i = 0; i < POWERUP_MAX; i++ ) {
if ( inventory.powerups & (1 << i) ) {
StartPowerUpEffect( i );
}
}
UpdateModelSetup( true );
if ( weapon ) {
weapon->ClientUnstale();
}
}
/*
===============
idPlayer::AllowedVoiceDest
===============
*/
bool idPlayer::AllowedVoiceDest( int from ) {
int i, free;
free = -1;
for( i = 0; i < MAX_CONCURRENT_VOICES; i++ ) {
if( voiceDest[i] == from ) {
voiceDestTimes[i] = gameLocal.time;
return true;
}
if( voiceDestTimes[i] + 200 < gameLocal.time ) {
free = i;
}
}
if( free > -1 ) {
voiceDest[free] = from;
voiceDestTimes[i] = gameLocal.time;
return true;
}
return false;
}
// RITUAL BEGIN
void idPlayer::ClampCash( float minCash, float maxCash )
{
if( buyMenuCash < minCash )
buyMenuCash = minCash;
if( buyMenuCash > maxCash )
buyMenuCash = maxCash;
}
void idPlayer::GiveCash( float cashDeltaAmount )
{
//int minCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMinCash", 0 );
//int maxCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMaxCash", 0 );
float minCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMinCredits");
float maxCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMaxCredits");
float oldCash = buyMenuCash;
buyMenuCash += cashDeltaAmount;
ClampCash( minCash, maxCash );
if( (int)buyMenuCash != (int)oldCash )
{
gameLocal.mpGame.RedrawLocalBuyMenu();
}
if( (int)buyMenuCash > (int)oldCash )
{
// Play the "get cash" sound
// gameLocal.GetLocalPlayer()->StartSound( "snd_buying_givecash", SND_CHANNEL_ANY, 0, false, NULL );
}
else if( (int)buyMenuCash < (int)oldCash )
{
// Play the "lose cash" sound
// gameLocal.GetLocalPlayer()->StartSound( "snd_buying_givecash", SND_CHANNEL_ANY, 0, false, NULL );
}
}
void idPlayer::SetCash( float newCashAmount )
{
//int minCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMinCash", 0 );
//int maxCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMaxCash", 0 );
float minCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMinCredits");
float maxCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMaxCredits");
buyMenuCash = newCashAmount;
ClampCash( minCash, maxCash );
}
void idPlayer::ResetCash()
{
//int minCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMinCash", 0 );
//int maxCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerMaxCash", 0 );
//buyMenuCash = gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerStartingCash", 0 );
float minCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMinCredits");
float maxCash = (float) gameLocal.serverInfo.GetInt("si_buyModeMaxCredits");
buyMenuCash = (float) gameLocal.serverInfo.GetInt("si_buyModeStartingCredits");
ClampCash( minCash, maxCash );
}
/**
* Checks to see if the player can accept this item in their inventory
*
* weaponName Name of the weapon.
*/
int idPlayer::CanSelectWeapon(const char* weaponName)
{
int weaponNum = -1;
if(weaponName == NULL)
return weaponNum;
for( int i = 0; i < MAX_WEAPONS; i++ ) {
if ( inventory.weapons & ( 1 << i ) ) {
const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( !idStr::Cmp( weap, weaponName ) ) {
weaponNum = i;
break;
}
}
}
return weaponNum;
}
// RITUAL END
bool idPlayer::IsSpectatedClient( void ) const {
idPlayer *localPlayer = gameLocal.GetLocalPlayer();
if ( !localPlayer ) {
return false;
}
if ( localPlayer->spectating && localPlayer->spectator == entityNumber ) {
return true;
}
return false;
}