Files
DoomRTX/neo/prey/game/Player.cpp
T
2026-05-05 15:01:01 -07:00

7488 lines
194 KiB
C++

// Copyright (C) 2004 Id Software, Inc.
//
#include "precompiled.h"
#pragma hdrstop
#include "Game_local.h"
#include "../prey/game_portal.h" // HUMANHEAD
/*
===============================================================================
Player control of the Doom Marine.
This object handles all player movement and world interaction.
===============================================================================
*/
// 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;
// how many units to raise spectator above default view height so it's in the head of someone
const int SPECTATE_RAISE = -39;//25 //HUMANHEAD rww - this is no longer acceptable because of the crazy placement of our spawns (especially in grav zones)
//unfortunately those spawns don't have proper axis values, so we can't offset based on that either.
const int HEALTHPULSE_TIME = 333;
// minimum speed to bob and play run/walk animations at
const float MIN_BOB_SPEED = 5.0f;
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_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" ); // HUMANHEAD pdm: <hide> from scripts since it's got a trace parm
CLASS_DECLARATION( idActor, idPlayer )
EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
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_Gibbed, idPlayer::Event_Gibbed )
END_CLASS
const int MAX_RESPAWN_TIME = 10000;
const int RAGDOLL_DEATH_TIME = 500; //HUMANHEAD rww - was 3000
const int MAX_PDAS = 64;
const int MAX_PDA_ITEMS = 128;
const int STEPUP_TIME = 200;
const int MAX_INVENTORY_ITEMS = 20;
idVec3 idPlayer::colorBarTable[ 5 ] = {
idVec3( 0.25f, 0.25f, 0.25f ),
idVec3( 1.00f, 0.00f, 0.00f ),
idVec3( 0.00f, 0.80f, 0.10f ),
idVec3( 0.20f, 0.50f, 0.80f ),
idVec3( 1.00f, 0.80f, 0.10f )
};
/*
==============
idInventory::Clear
==============
*/
void idInventory::Clear( void ) {
maxHealth = 0;
weapons = 0;
memset( ammo, 0, sizeof( ammo ) );
// 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 );
levelTriggers.Clear();
pickupItemNames.Clear();
ammoPredictTime = 0;
ammoPulse = false;
weaponPulse = false;
}
/*
==============
idInventory::GetPersistantData
==============
*/
void idInventory::GetPersistantData( idDict &dict ) {
/* HUMANHEAD pdm: OVERRIDDEN
int i;
int num;
idDict *item;
idStr key;
const idKeyValue *kv;
const char *name;
// armor
dict.SetInt( "armor", armor );
// don't bother with powerups, maxhealth, maxarmor, or the clip
// ammo
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
name = idWeapon::GetAmmoNameForNum( ( ammo_t )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 );
// pdas viewed
for ( i = 0; i < 4; i++ ) {
dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
}
dict.SetInt( "selPDA", selPDA );
dict.SetInt( "selVideo", selVideo );
dict.SetInt( "selEmail", selEMail );
dict.SetInt( "selAudio", selAudio );
dict.SetInt( "pdaOpened", pdaOpened );
dict.SetInt( "turkeyScore", turkeyScore );
// pdas
for ( i = 0; i < pdas.Num(); i++ ) {
sprintf( key, "pda_%i", i );
dict.Set( key, pdas[ i ] );
}
dict.SetInt( "pdas", pdas.Num() );
// video cds
for ( i = 0; i < videos.Num(); i++ ) {
sprintf( key, "video_%i", i );
dict.Set( key, videos[ i ].c_str() );
}
dict.SetInt( "videos", videos.Num() );
// emails
for ( i = 0; i < emails.Num(); i++ ) {
sprintf( key, "email_%i", i );
dict.Set( key, emails[ i ].c_str() );
}
dict.SetInt( "emails", emails.Num() );
// weapons
dict.SetInt( "weapon_bits", weapons );
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 ) {
/* HUMANHEAD pdm: OVERRIDDEN
int i;
int num;
idDict *item;
idStr key;
idStr itemname;
const idKeyValue *kv;
const char *name;
Clear();
// health/armor
maxHealth = dict.GetInt( "maxhealth", "100" );
armor = dict.GetInt( "armor", "50" );
maxarmor = dict.GetInt( "maxarmor", "100" );
deplete_armor = dict.GetInt( "deplete_armor", "0" );
deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
// the clip and powerups aren't restored
// ammo
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
name = idWeapon::GetAmmoNameForNum( ( ammo_t )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 );
}
}
// pdas viewed
for ( i = 0; i < 4; i++ ) {
pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
}
selPDA = dict.GetInt( "selPDA" );
selEMail = dict.GetInt( "selEmail" );
selVideo = dict.GetInt( "selVideo" );
selAudio = dict.GetInt( "selAudio" );
pdaOpened = dict.GetBool( "pdaOpened" );
turkeyScore = dict.GetBool( "turkeyScore" );
// pdas
num = dict.GetInt( "pdas" );
pdas.SetNum( num );
for ( i = 0; i < num; i++ ) {
sprintf( itemname, "pda_%i", i );
pdas[i] = dict.GetString( itemname, "default" );
}
// videos
num = dict.GetInt( "videos" );
videos.SetNum( num );
for ( i = 0; i < num; i++ ) {
sprintf( itemname, "video_%i", i );
videos[i] = dict.GetString( itemname, "default" );
}
// emails
num = dict.GetInt( "emails" );
emails.SetNum( num );
for ( i = 0; i < num; i++ ) {
sprintf( itemname, "email_%i", i );
emails[i] = dict.GetString( itemname, "default" );
}
// weapons are stored as a number for persistant data, but as strings in the entityDef
weapons = dict.GetInt( "weapon_bits", "0" );
#ifdef ID_DEMO_BUILD
Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
#else
if ( g_skill.GetInteger() >= 3 ) {
Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false );
} else {
Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
}
#endif
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( ammoPredictTime );
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
savefile->WriteInt( ammo[ i ] );
}
for( i = 0; i < MAX_WEAPONS; i++ ) {
savefile->WriteInt( clip[ i ] );
}
savefile->WriteInt( items.Num() );
for( i = 0; i < items.Num(); i++ ) {
savefile->WriteDict( items[ i ] );
}
savefile->WriteInt( pickupItemNames.Num() );
for( i = 0; i < pickupItemNames.Num(); i++ ) {
savefile->WriteString( pickupItemNames[i].icon );
savefile->WriteString( pickupItemNames[i].name );
// HUMANHEAD pdm
savefile->WriteInt( pickupItemNames[i].time );
savefile->WriteInt( pickupItemNames[i].slotZeroTime );
savefile->WriteFloat( pickupItemNames[i].matcolorAlpha );
savefile->WriteBool( pickupItemNames[i].bDoubleWide );
// HUMANHEAD END
}
savefile->WriteInt( levelTriggers.Num() );
for ( i = 0; i < levelTriggers.Num(); i++ ) {
savefile->WriteString( levelTriggers[i].levelName );
savefile->WriteString( levelTriggers[i].triggerName );
}
savefile->WriteBool( ammoPulse );
savefile->WriteBool( weaponPulse );
}
/*
==============
idInventory::Restore
==============
*/
void idInventory::Restore( idRestoreGame *savefile ) {
int i, num;
savefile->ReadInt( maxHealth );
savefile->ReadInt( weapons );
savefile->ReadInt( ammoPredictTime );
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
savefile->ReadInt( ammo[ i ] );
}
for( i = 0; i < MAX_WEAPONS; i++ ) {
savefile->ReadInt( clip[ i ] );
}
savefile->ReadInt( num );
for( i = 0; i < num; i++ ) {
idDict *itemdict = new idDict;
savefile->ReadDict( itemdict );
items.Append( itemdict );
}
savefile->ReadInt( num );
for( i = 0; i < num; i++ ) {
idItemInfo info;
savefile->ReadString( info.icon );
savefile->ReadString( info.name );
// HUMANHEAD pdm
savefile->ReadInt( info.time );
savefile->ReadInt( info.slotZeroTime );
savefile->ReadFloat( info.matcolorAlpha );
savefile->ReadBool( info.bDoubleWide );
// HUMANHEAD END
pickupItemNames.Append( info );
}
savefile->ReadInt( num );
for ( i = 0; i < num; i++ ) {
idLevelTriggerInfo lti;
savefile->ReadString( lti.levelName );
savefile->ReadString( lti.triggerName );
levelTriggers.Append( lti );
}
savefile->ReadBool( ammoPulse );
savefile->ReadBool( weaponPulse );
}
/*
==============
idInventory::AmmoIndexForAmmoClass
==============
*/
ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
return idWeapon::GetAmmoNumForName( ammo_classname );
}
/*
==============
idInventory::AmmoIndexForAmmoClass
==============
*/
int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
}
/*
==============
idInventory::AmmoPickupNameForIndex
==============
*/
const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
return idWeapon::GetAmmoPickupNameForNum( ammonum );
}
/*
==============
idInventory::WeaponIndexForAmmoClass
mapping could be prepared in the constructor
==============
*/
int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
int i;
const char *weapon_classname;
for( i = 0; i < MAX_WEAPONS; i++ ) {
weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
if ( !weapon_classname ) {
continue;
}
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !decl ) {
continue;
}
if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
return i;
}
}
return -1;
}
/*
==============
idInventory::AmmoIndexForWeaponClass
==============
*/
ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
#if HUMANHEAD
const idDeclEntityDef *weaponObjDecl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !weaponObjDecl ) {
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
}
const char *fireInfoName = weaponObjDecl->dict.GetString("def_fireInfo");
const idDeclEntityDef *decl = gameLocal.FindEntityDef( fireInfoName, false );
if ( !decl ) {
gameLocal.Error( "Unknown fireinfo in decl '%s'", fireInfoName );
}
#else
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !decl ) {
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
}
#endif
if ( ammoRequired ) {
*ammoRequired = decl->dict.GetInt( "ammoRequired" );
}
ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
return ammo_i;
}
/*
==============
idInventory::AltAmmoIndexForWeaponClass
HUMANHEAD bjk
==============
*/
ammo_t idInventory::AltAmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
const idDeclEntityDef *weaponObjDecl = gameLocal.FindEntityDef( weapon_classname, false );
if ( !weaponObjDecl ) {
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
}
const char *fireInfoName = weaponObjDecl->dict.GetString("def_altFireInfo");
const idDeclEntityDef *decl = gameLocal.FindEntityDef( fireInfoName, false );
if ( !decl ) {
gameLocal.Error( "Unknown fireinfo in decl '%s'", fireInfoName );
}
if ( ammoRequired ) {
*ammoRequired = decl->dict.GetInt( "ammoRequired" );
}
ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
return ammo_i;
}
/*
==============
idInventory::AddPickupName
==============
*/
void idInventory::AddPickupName( const char *name, const char *icon ) {
/* HUMANHEAD pdm: OVERRIDDEN
int num;
num = pickupItemNames.Num();
if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].name.Icmp( name ) != 0 ) ) {
idItemInfo &info = pickupItemNames.Alloc();
if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
info.name = common->GetLanguageDict()->GetString( name );
} else {
info.name = name;
}
info.icon = icon;
}
*/
}
/*
==============
idInventory::Give
==============
*/
bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud ) {
/* HUMANHEAD pdm: OVERRIDDEN
int i;
const char *pos;
const char *end;
int len;
idStr weaponString;
int max;
const idDeclEntityDef *weaponDecl;
bool tookWeapon;
int amount;
idItemInfo info;
const char *name;
if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
i = AmmoIndexForAmmoClass( statname );
max = MaxAmmoForAmmoClass( owner, statname );
if ( ammo[ i ] >= max ) {
return false;
}
amount = atoi( value );
if ( amount ) {
ammo[ i ] += amount;
if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
ammo[ i ] = max;
}
ammoPulse = true;
name = AmmoPickupNameForIndex( i );
if ( idStr::Length( name ) ) {
AddPickupName( name, "" );
}
}
} else if ( !idStr::Icmp( statname, "armor" ) ) {
if ( armor >= maxarmor ) {
return false; // can't hold any more, so leave the item
}
amount = atoi( value );
if ( amount ) {
armor += amount;
if ( armor > maxarmor ) {
armor = maxarmor;
}
nextArmorDepleteTime = 0;
armorPulse = true;
}
} else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
i = WeaponIndexForAmmoClass( spawnArgs, statname + 7 );
if ( i != -1 ) {
// set, don't add. not going over the clip size limit.
clip[ i ] = atoi( value );
}
} else if ( !idStr::Icmp( statname, "berserk" ) ) {
GivePowerUp( owner, BERSERK, SEC2MS( atof( value ) ) );
} else if ( !idStr::Icmp( statname, "mega" ) ) {
GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
} else if ( !idStr::Icmp( statname, "weapon" ) ) {
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 name
for( i = 0; i < MAX_WEAPONS; i++ ) {
if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
break;
}
}
if ( i >= MAX_WEAPONS ) {
gameLocal.Error( "Unknown weapon '%s'", weaponName.c_str() );
}
// cache the media for this weapon
weaponDecl = gameLocal.FindEntityDef( weaponName, false );
// don't pickup "no ammo" weapon types twice
// not for D3 SP .. 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 conistent to pick it up
if ( gameLocal.isMultiplayer && weaponDecl && ( weapons & ( 1 << i ) ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
continue;
}
if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
if ( owner->GetUserInfo()->GetBool( "ui_autoSwitch" ) && idealWeapon ) {
assert( !gameLocal.isClient );
*idealWeapon = i;
}
if ( owner->hud && updateHud && lastGiveTime + 1000 < gameLocal.time ) {
owner->hud->SetStateInt( "newWeapon", i );
owner->hud->HandleNamedEvent( "newWeapon" );
lastGiveTime = gameLocal.time;
}
weaponPulse = true;
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 ) );
ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
if ( ammo_i ) {
clip[ weapon_index ] = -1;
ammo[ ammo_i ] = 0;
}
}
/*
===============
idInventory::HasAmmo
===============
*/
int idInventory::HasAmmo( ammo_t type, int amount ) {
if ( ( type == 0 ) || !amount ) {
// always allow weapons that don't use ammo to fire
return -1;
}
// check if we have infinite ammo
if ( ammo[ type ] < 0 ) {
return -1;
}
// return how many shots we can fire
return ammo[ type ] / amount;
}
/*
===============
idInventory::HasAmmo
===============
*/
int idInventory::HasAmmo( const char *weapon_classname ) {
int ammoRequired;
ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
return HasAmmo( ammo_i, ammoRequired );
}
/*
===============
idInventory::UseAmmo
===============
*/
bool idInventory::UseAmmo( ammo_t type, int amount ) {
if ( !HasAmmo( type, amount ) ) {
return false;
}
// take an ammo away if not infinite
if ( ammo[ type ] >= 0 ) {
ammo[ type ] -= 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 ) );
noclip = false;
godmode = false;
spawnAnglesSet = false;
spawnAngles = ang_zero;
viewAngles = ang_zero;
cmdAngles = ang_zero;
oldButtons = 0;
buttonMask = 0;
oldFlags = 0;
lastHitTime = 0;
lastSndHitTime = 0;
lastSavingThrowTime = 0;
weapon = NULL;
hud = NULL;
lastDmgTime = 0;
deathClearContentsTime = 0;
stamina = 0.0f;
healthPool = 0.0f;
nextHealthPulse = 0;
healthPulse = false;
spiritPulse = false; // HUMANHEAD pdm
nextHealthTake = 0;
healthTake = false;
scoreBoardOpen = false;
forceScoreBoard = false;
forceRespawn = false;
spectating = false;
spectator = 0;
colorBar = vec3_zero;
colorBarIndex = 0;
forcedReady = false;
wantSpectate = false;
lastHitToggle = false;
minRespawnTime = 0;
maxRespawnTime = 0;
firstPersonViewOrigin = vec3_zero;
firstPersonViewAxis = mat3_identity;
MPLastSpawnSpot = NULL; //HUMANHEAD rww - does NOT need to be saved/restored, mp-only
hipJoint = INVALID_JOINT;
chestJoint = INVALID_JOINT;
headJoint = 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;
currentWeapon = -1;
idealWeapon = -1;
previousWeapon = -1;
weaponSwitchTime = 0;
weaponEnabled = true;
weapon_fists = -1;
showWeaponViewModel = true;
skin = NULL;
baseSkinName = "";
numProjectilesFired = 0;
numProjectileHits = 0;
airless = false;
airTics = 0;
lastAirDamage = 0;
gibDeath = false;
gibsLaunched = false;
gibsDir = vec3_zero;
//HUMANHEAD: aob
zoomFov.Init( gameLocal.GetTime(), 0, g_fov.GetFloat(), g_fov.GetFloat() );
//HUMANHEAD END
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;
focusGUIent = NULL;
focusUI = NULL;
cursor = NULL;
oldMouseX = 0;
oldMouseY = 0;
#if GAMEPAD_SUPPORT // VENOM BEGIN
iCurrentRumble = 0;
#endif // VENOM END
lastDamageDef = 0;
lastDamageDir = vec3_zero;
lastDamageLocation = 0;
smoothedFrame = 0;
smoothedOriginUpdated = false;
smoothedOrigin = vec3_zero;
smoothedAngles = ang_zero;
fl.networkSync = true;
latchedTeam = -1;
doingDeathSkin = false;
weaponGone = false;
useInitialSpawns = false;
tourneyRank = 0;
lastSpectateTeleport = 0;
tourneyLine = 0;
hiddenWeapon = false;
tipUp = false;
teleportEntity = NULL;
teleportKiller = -1;
respawning = false;
ready = false;
leader = false;
lastSpectateChange = 0;
lastTeleFX = -9999;
weaponCatchup = false;
lastSnapshotSequence = 0;
MPAim = -1;
#if !HUMANHEAD // HUMANHEAD pdm
lastMPAim = -1;
lastMPAimTime = 0;
MPAimFadeTime = 0;
MPAimHighlight = false;
#endif // HUMANHEAD END
spawnedTime = 0;
lastManOver = false;
lastManPlayAgain = false;
lastManPresent = false;
isTelefragged = false;
isLagged = false;
isChatting = false;
selfSmooth = false;
bBufferNextSnapAngles = false; //HUMANHEAD rww - doesn't need to be saved/restored, for networking only
}
/*
==============
idPlayer::LinkScriptVariables
set up conditions for animation
==============
*/
void idPlayer::LinkScriptVariables( void ) {
AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
AI_BACKWARD.LinkTo( scriptObject, "AI_BACKWARD" );
AI_STRAFE_LEFT.LinkTo( scriptObject, "AI_STRAFE_LEFT" );
AI_STRAFE_RIGHT.LinkTo( scriptObject, "AI_STRAFE_RIGHT" );
AI_ATTACK_HELD.LinkTo( scriptObject, "AI_ATTACK_HELD" );
AI_WEAPON_FIRED.LinkTo( scriptObject, "AI_WEAPON_FIRED" );
AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
AI_CROUCH.LinkTo( scriptObject, "AI_CROUCH" );
AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
AI_ONLADDER.LinkTo( scriptObject, "AI_ONLADDER" );
AI_HARDLANDING.LinkTo( scriptObject, "AI_HARDLANDING" );
AI_SOFTLANDING.LinkTo( scriptObject, "AI_SOFTLANDING" );
AI_RUN.LinkTo( scriptObject, "AI_RUN" );
AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
AI_RELOAD.LinkTo( scriptObject, "AI_RELOAD" );
AI_TELEPORT.LinkTo( scriptObject, "AI_TELEPORT" );
AI_TURN_LEFT.LinkTo( scriptObject, "AI_TURN_LEFT" );
AI_TURN_RIGHT.LinkTo( scriptObject, "AI_TURN_RIGHT" );
AI_REALLYFALL.LinkTo( scriptObject, "AI_REALLYFALL" ); //HUMANHEAD rww
}
/*
==============
idPlayer::SetupWeaponEntity
==============
*/
void idPlayer::SetupWeaponEntity( void ) {
/* HUMANHEAD pdm: OVERRIDDEN
int w;
const char *weap;
if ( weapon.GetEntity() ) {
// get rid of old weapon
weapon.GetEntity()->Clear();
currentWeapon = -1;
} else if ( !gameLocal.isClient ) {
weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
weapon.GetEntity()->SetOwner( this );
currentWeapon = -1;
}
for( w = 0; w < MAX_WEAPONS; w++ ) {
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( weap && *weap ) {
idWeapon::CacheWeapon( weap );
}
}
*/
}
/*
==============
idPlayer::Init
==============
*/
void idPlayer::Init( void ) {
const char *value;
const idKeyValue *kv;
noclip = false;
godmode = false;
oldButtons = 0;
oldFlags = 0;
currentWeapon = -1;
idealWeapon = -1;
previousWeapon = -1;
weaponSwitchTime = 0;
weaponEnabled = true;
weapon_fists = SlotForWeapon( "weapon_fists" );
showWeaponViewModel = GetUserInfo()->GetBool( "ui_showGun" );
lastDmgTime = 0;
bobCycle = 0;
bobFrac = 0.0f;
landChange = 0;
landTime = 0;
//HUMANHEAD: aob
zoomFov.Init( gameLocal.GetTime(), 0, g_fov.GetFloat(), g_fov.GetFloat() );
//HUMANHEAD END
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;
focusGUIent = NULL;
focusUI = NULL;
bDialogDamageMode = false; // pdm - diallow health from going below 1
bDialogWeaponMode = false; // pdm - lock weapon during dialog
// remove any damage effects
playerView.ClearEffects();
// damage values
fl.takedamage = true;
ClearPain();
// restore persistent data
RestorePersistantInfo();
bobCycle = 0;
stamina = 0.0f;
healthPool = 0.0f;
nextHealthPulse = 0;
healthPulse = false;
spiritPulse = false; // HUMANHEAD pdm
nextHealthTake = 0;
healthTake = false;
SetupWeaponEntity();
currentWeapon = -1;
previousWeapon = -1;
idealLegsYaw = 0.0f;
legsYaw = 0.0f;
legsForward = true;
oldViewYaw = 0.0f;
// set the pm_ cvars
if ( !gameLocal.isMultiplayer || gameLocal.isServer ) {
kv = spawnArgs.MatchPrefix( "pm_", NULL );
while( kv ) {
cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
kv = spawnArgs.MatchPrefix( "pm_", kv );
}
}
// disable stamina on hell levels
if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
pm_stamina.SetFloat( 0.0f );
}
// stamina always initialized to maximum
stamina = pm_stamina.GetFloat();
// air always initialized to maximum too
airTics = pm_airTics.GetFloat();
airless = false;
gibDeath = false;
gibsLaunched = false;
gibsDir.Zero();
// set the gravity
physicsObj.SetGravity( gameLocal.GetGravity() );
// start out standing
SetEyeHeight( pm_normalviewheight.GetFloat() );
stepUpTime = 0;
stepUpDelta = 0.0f;
viewBobAngles.Zero();
viewBob.Zero();
//HUMANHEAD rww - changed from SetModel
SetPlayerModel( false );
//HUMANHEAD END
if ( cursor ) {
cursor->SetStateString( "combatcursor", "1" );
cursor->SetStateString( "activecombatcursor", "1" ); // HUMANHEAD pdm
}
if ( ( gameLocal.isMultiplayer || 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;
}
value = spawnArgs.GetString( "bone_hips", "" );
hipJoint = animator.GetJointHandle( value );
if ( hipJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
}
value = spawnArgs.GetString( "bone_chest", "" );
chestJoint = animator.GetJointHandle( value );
if ( chestJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
}
value = spawnArgs.GetString( "bone_head", "" );
headJoint = animator.GetJointHandle( value );
if ( headJoint == INVALID_JOINT ) {
gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
}
// initialize the script variables
AI_FORWARD = false;
AI_BACKWARD = false;
AI_STRAFE_LEFT = false;
AI_STRAFE_RIGHT = false;
AI_ATTACK_HELD = false;
AI_WEAPON_FIRED = false;
AI_JUMP = false;
AI_DEAD = false;
AI_CROUCH = false;
AI_ONGROUND = true;
AI_REALLYFALL = false; //HUMANHEAD rww
AI_ONLADDER = false;
AI_HARDLANDING = false;
AI_SOFTLANDING = false;
AI_RUN = false;
AI_PAIN = false;
AI_RELOAD = false;
AI_TELEPORT = false;
AI_TURN_LEFT = false;
AI_TURN_RIGHT = false;
// reset the script object
ConstructScriptObject();
// execute the script so the script object's constructor takes effect immediately
scriptThread->Execute();
forceScoreBoard = false;
forcedReady = false;
privateCameraView = NULL;
lastSpectateChange = 0;
lastTeleFX = -9999;
hiddenWeapon = false;
tipUp = false;
teleportEntity = NULL;
teleportKiller = -1;
leader = false;
SetPrivateCameraView( NULL );
lastSnapshotSequence = 0;
MPAim = -1;
cvarSystem->SetCVarBool( "ui_chat", false );
fl.neverDormant = true; // HUMANHEAD JRM
}
/*
==============
idPlayer::Spawn
Prepare any resources used by the player.
==============
*/
void idPlayer::Spawn( void ) {
idStr temp;
idBounds bounds;
if ( entityNumber >= MAX_CLIENTS ) {
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 );
physicsObj.SetClipMask( MASK_PLAYERSOLID );
SetPhysics( &physicsObj );
InitAASLocation();
skin = renderEntity.customSkin;
// only the local player needs guis
if ( !gameLocal.isMultiplayer || entityNumber == gameLocal.localClientNum ) {
// load HUD
// HUMANHEAD pdm: changed to IsCompetitive() to allow for coop
if ( gameLocal.IsCompetitive() ) {
hud = uiManager->FindGui( "guis/mphud.gui", true, false, true );
} else if ( spawnArgs.GetString( "hud", "", temp ) ) {
hud = uiManager->FindGui( temp, true, false, true );
}
if ( hud ) {
hud->Activate( true, gameLocal.time );
}
// load cursor
if ( spawnArgs.GetString( "cursor", "", temp ) ) {
cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer );
}
if ( cursor ) {
cursor->Activate( true, gameLocal.time );
}
}
SetLastHitTime( 0 );
// set up conditions for animation
LinkScriptVariables();
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
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
UserInfoChanged( false );
}
// create combat collision hull for exact collision detection
SetCombatModel();
// init the damage effects
playerView.SetPlayerEntity( static_cast<hhPlayer*>(this) ); //HUMANHEAD bjk: for god sakes we need to stop this hh version bullshit
// 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
//HUMANHEAD rww - preserve the same look in sp where we have been self-shadowing
if (gameLocal.isMultiplayer)
//HUMANHEAD END
{
renderEntity.noShadow = MP_PLAYERNOSHADOW_DEFAULT;
renderEntity.noSelfShadow = true;
}
idAFAttachment *headEnt = head.GetEntity();
if ( headEnt ) {
headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
//HUMANHEAD rww - preserve the same look in sp where we have been self-shadowing
if (gameLocal.isMultiplayer)
//HUMANHEAD END
{
headEnt->GetRenderEntity()->noSelfShadow = true;
}
}
if ( gameLocal.isMultiplayer ) {
Init();
Hide(); // properly hidden if starting as a spectator
if ( !gameLocal.isClient ) {
// set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
SetupWeaponEntity();
SpawnFromSpawnSpot();
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
// HUMANHEAD pdm: changed to IsCompetitve() to allow for coop
if ( !gameLocal.IsCompetitive() && 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.GetEntity() ) {
weapon.GetEntity()->LowerWeapon();
}
idealWeapon = 0;
} else {
hiddenWeapon = false;
}
if ( hud ) {
UpdateHudWeapon(false); // HUMANHEAD pdm: Don't display weapon switch
hud->StateChanged( gameLocal.time );
}
tipUp = false;
if ( inventory.levelTriggers.Num() ) {
PostEventMS( &EV_Player_LevelTrigger, 0 );
}
}
/*
==============
idPlayer::~idPlayer()
Release any resources used by the player.
==============
*/
idPlayer::~idPlayer() {
delete weapon.GetEntity();
weapon = NULL;
}
/*
===========
idPlayer::Save
===========
*/
void idPlayer::Save( idSaveGame *savefile ) const {
int i;
savefile->WriteUsercmd( usercmd );
playerView.Save( savefile );
savefile->WriteBool( noclip );
savefile->WriteBool( godmode );
// don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
savefile->WriteAngles( spawnAngles );
savefile->WriteBool( !InVehicle() ); // HUMANHEAD mdl: We need to know this on restore
savefile->WriteAngles( viewAngles );
savefile->WriteAngles( cmdAngles );
savefile->WriteInt( buttonMask );
savefile->WriteInt( oldButtons );
savefile->WriteInt( oldFlags );
savefile->WriteInt( lastHitTime );
savefile->WriteInt( lastSndHitTime );
savefile->WriteInt( lastSavingThrowTime );
// idBoolFields don't need to be saved, just re-linked in Restore
inventory.Save( savefile );
weapon.Save( savefile );
savefile->WriteUserInterface( hud, false );
savefile->WriteInt( weapon_fists );
savefile->WriteInt( lastDmgTime );
savefile->WriteInt( deathClearContentsTime );
savefile->WriteBool( doingDeathSkin );
savefile->WriteFloat( stamina );
savefile->WriteFloat( healthPool );
savefile->WriteInt( nextHealthPulse );
savefile->WriteBool( healthPulse );
savefile->WriteBool( spiritPulse );
savefile->WriteInt( nextHealthTake );
savefile->WriteBool( healthTake );
savefile->WriteBool( hiddenWeapon );
savefile->WriteInt( spectator );
savefile->WriteVec3( colorBar );
savefile->WriteInt( colorBarIndex );
savefile->WriteBool( scoreBoardOpen );
savefile->WriteBool( forceScoreBoard );
savefile->WriteBool( forceRespawn );
savefile->WriteBool( spectating );
savefile->WriteInt( lastSpectateTeleport );
savefile->WriteBool( lastHitToggle );
savefile->WriteBool( forcedReady );
savefile->WriteBool( wantSpectate );
savefile->WriteBool( weaponGone );
savefile->WriteBool( useInitialSpawns );
savefile->WriteInt( latchedTeam );
savefile->WriteInt( tourneyRank );
savefile->WriteInt( tourneyLine );
teleportEntity.Save( savefile );
savefile->WriteInt( teleportKiller );
savefile->WriteInt( minRespawnTime );
savefile->WriteInt( maxRespawnTime );
savefile->WriteVec3( firstPersonViewOrigin );
savefile->WriteMat3( firstPersonViewAxis );
// don't bother saving dragEntity since it's a dev tool
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->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->WriteInt( currentWeapon );
savefile->WriteInt( idealWeapon );
savefile->WriteInt( previousWeapon );
savefile->WriteInt( weaponSwitchTime );
savefile->WriteBool( weaponEnabled );
savefile->WriteBool( showWeaponViewModel );
savefile->WriteSkin( skin );
savefile->WriteString( baseSkinName );
savefile->WriteInt( numProjectilesFired );
savefile->WriteInt( numProjectileHits );
savefile->WriteBool( airless );
savefile->WriteInt( airTics );
savefile->WriteInt( lastAirDamage );
savefile->WriteBool( gibDeath );
savefile->WriteBool( gibsLaunched );
savefile->WriteVec3( gibsDir );
savefile->WriteFloat( zoomFov.GetStartTime() );
savefile->WriteFloat( zoomFov.GetDuration() );
savefile->WriteFloat( zoomFov.GetStartValue() );
savefile->WriteFloat( zoomFov.GetEndValue() );
savefile->WriteFloat( centerView.GetStartTime() );
savefile->WriteFloat( centerView.GetDuration() );
savefile->WriteFloat( centerView.GetStartValue() );
savefile->WriteFloat( centerView.GetEndValue() );
savefile->WriteBool( fxFov );
savefile->WriteFloat( influenceFov );
savefile->WriteInt( influenceActive );
savefile->WriteFloat( influenceRadius );
savefile->WriteObject( influenceEntity );
savefile->WriteMaterial( influenceMaterial );
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->WriteObject( focusGUIent );
// can't save focusUI
savefile->WriteInt( focusTime );
savefile->WriteUserInterface( cursor, false );
savefile->WriteInt( oldMouseX );
savefile->WriteInt( oldMouseY );
savefile->WriteBool( tipUp );
savefile->WriteInt( lastDamageDef );
savefile->WriteVec3( lastDamageDir );
savefile->WriteInt( lastDamageLocation );
savefile->WriteInt( smoothedFrame );
savefile->WriteBool( smoothedOriginUpdated );
savefile->WriteVec3( smoothedOrigin );
savefile->WriteAngles( smoothedAngles );
savefile->WriteBool( ready );
savefile->WriteBool( respawning );
savefile->WriteBool( leader );
savefile->WriteInt( lastSpectateChange );
savefile->WriteInt( lastTeleFX );
// HUMANHEAD pdm
savefile->WriteBool( bDialogDamageMode );
savefile->WriteBool( bDialogWeaponMode );
savefile->WriteFloat( pm_stamina.GetFloat() );
if ( hud ) {
hud->SetStateString( "message", common->GetLanguageDict()->GetString( "#str_02916" ) );
hud->HandleNamedEvent( "Message" );
}
}
/*
===========
idPlayer::Restore
===========
*/
void idPlayer::Restore( idRestoreGame *savefile ) {
int i;
int num;
float set;
savefile->ReadUsercmd( usercmd );
playerView.Restore( savefile );
savefile->ReadBool( noclip );
savefile->ReadBool( godmode );
savefile->ReadAngles( spawnAngles );
// HUMANHEAD mdl
bool restoreAngles;
savefile->ReadBool( restoreAngles );
// HUMANHEAD END
savefile->ReadAngles( viewAngles );
savefile->ReadAngles( cmdAngles );
memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
if( restoreAngles ) { // HUMANHEAD mdl: View angles are zeroed out if we're in a vehicle
SetViewAngles( viewAngles );
}
spawnAnglesSet = true;
savefile->ReadInt( buttonMask );
savefile->ReadInt( oldButtons );
savefile->ReadInt( oldFlags );
usercmd.flags = 0;
oldFlags = 0;
savefile->ReadInt( lastHitTime );
savefile->ReadInt( lastSndHitTime );
savefile->ReadInt( lastSavingThrowTime );
// Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
LinkScriptVariables();
inventory.Restore( savefile );
weapon.Restore( savefile );
savefile->ReadUserInterface( hud );
savefile->ReadInt( weapon_fists );
savefile->ReadInt( lastDmgTime );
savefile->ReadInt( deathClearContentsTime );
savefile->ReadBool( doingDeathSkin );
savefile->ReadFloat( stamina );
savefile->ReadFloat( healthPool );
savefile->ReadInt( nextHealthPulse );
savefile->ReadBool( healthPulse );
savefile->ReadBool( spiritPulse );
savefile->ReadInt( nextHealthTake );
savefile->ReadBool( healthTake );
savefile->ReadBool( hiddenWeapon );
savefile->ReadInt( spectator );
savefile->ReadVec3( colorBar );
savefile->ReadInt( colorBarIndex );
savefile->ReadBool( scoreBoardOpen );
savefile->ReadBool( forceScoreBoard );
savefile->ReadBool( forceRespawn );
savefile->ReadBool( spectating );
savefile->ReadInt( lastSpectateTeleport );
savefile->ReadBool( lastHitToggle );
savefile->ReadBool( forcedReady );
savefile->ReadBool( wantSpectate );
savefile->ReadBool( weaponGone );
savefile->ReadBool( useInitialSpawns );
savefile->ReadInt( latchedTeam );
savefile->ReadInt( tourneyRank );
savefile->ReadInt( tourneyLine );
teleportEntity.Restore( savefile );
savefile->ReadInt( teleportKiller );
savefile->ReadInt( minRespawnTime );
savefile->ReadInt( maxRespawnTime );
savefile->ReadVec3( firstPersonViewOrigin );
savefile->ReadMat3( firstPersonViewAxis );
// don't bother saving dragEntity since it's a dev tool
dragEntity.Clear();
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->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->ReadInt( currentWeapon );
savefile->ReadInt( idealWeapon );
savefile->ReadInt( previousWeapon );
savefile->ReadInt( weaponSwitchTime );
savefile->ReadBool( weaponEnabled );
savefile->ReadBool( showWeaponViewModel );
savefile->ReadSkin( skin );
savefile->ReadString( baseSkinName );
savefile->ReadInt( numProjectilesFired );
savefile->ReadInt( numProjectileHits );
savefile->ReadBool( airless );
savefile->ReadInt( airTics );
savefile->ReadInt( lastAirDamage );
savefile->ReadBool( gibDeath );
savefile->ReadBool( gibsLaunched );
savefile->ReadVec3( gibsDir );
savefile->ReadFloat( set );
zoomFov.SetStartTime( set );
savefile->ReadFloat( set );
zoomFov.SetDuration( set );
savefile->ReadFloat( set );
zoomFov.SetStartValue( set );
savefile->ReadFloat( set );
zoomFov.SetEndValue( set );
savefile->ReadFloat( set );
centerView.SetStartTime( set );
savefile->ReadFloat( set );
centerView.SetDuration( set );
savefile->ReadFloat( set );
centerView.SetStartValue( set );
savefile->ReadFloat( set );
centerView.SetEndValue( set );
savefile->ReadBool( fxFov );
savefile->ReadFloat( influenceFov );
savefile->ReadInt( influenceActive );
savefile->ReadFloat( influenceRadius );
savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
savefile->ReadMaterial( influenceMaterial );
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->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
// can't save focusUI
focusUI = NULL;
savefile->ReadInt( focusTime );
savefile->ReadUserInterface( cursor );
savefile->ReadInt( oldMouseX );
savefile->ReadInt( oldMouseY );
savefile->ReadBool( tipUp );
savefile->ReadInt( lastDamageDef );
savefile->ReadVec3( lastDamageDir );
savefile->ReadInt( lastDamageLocation );
savefile->ReadInt( smoothedFrame );
savefile->ReadBool( smoothedOriginUpdated );
savefile->ReadVec3( smoothedOrigin );
savefile->ReadAngles( smoothedAngles );
savefile->ReadBool( ready );
savefile->ReadBool( respawning );
savefile->ReadBool( leader );
savefile->ReadInt( lastSpectateChange );
savefile->ReadInt( lastTeleFX );
// HUMANHEAD pdm
savefile->ReadBool( bDialogDamageMode );
savefile->ReadBool( bDialogWeaponMode );
// 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 );
}
savefile->ReadFloat( set );
pm_stamina.SetFloat( set );
// create combat collision hull for exact collision detection
SetCombatModel();
}
/*
===============
idPlayer::PrepareForRestart
================
*/
void idPlayer::PrepareForRestart( void ) {
//HUMANHEAD rww - some local spirit stuff needs to be updated on the client
if (gameLocal.isMultiplayer) {
CancelEvents(&EV_SetOverlayMaterial);
playerView.SetViewOverlayMaterial( NULL );
if (gameLocal.isClient) {
assert(IsType(hhPlayer::Type));
hhPlayer *hhSelf = static_cast<hhPlayer *>(this);
if (hhSelf->bSpiritWalk) {
if (gameLocal.localClientNum == entityNumber) {
if (hud) {
hud->HandleNamedEvent("SwitchFromEthereal");
}
gameLocal.SpiritWalkSoundMode( false );
}
SetSkinByName( NULL );
}
}
}
//HUMANHEAD END
Spectate( true );
forceRespawn = true;
// 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 ) {
Init();
} else {
// choose a random spot and prepare the point of view in case player is left spectating
assert( spectating );
SpawnFromSpawnSpot();
}
//HUMANEHAD rww - don't bother ever using initial spawns.
useInitialSpawns = false; //true;
UpdateSkinSetup( true );
}
/*
===============
idPlayer::ServerSpectate
================
*/
void idPlayer::ServerSpectate( bool spectate ) {
assert( !gameLocal.isClient );
if ( spectating != spectate ) {
Spectate( spectate );
if ( spectate ) {
SetSpectateOrigin();
} else {
if ( gameLocal.gameType == GAME_DM ) {
// make sure the scores are reset so you can't exploit by spectating and entering the game back
// other game types don't matter, as you either can't join back, or it's team scores
gameLocal.mpGame.ClearFrags( entityNumber );
}
}
}
if ( !spectate ) {
SpawnFromSpawnSpot();
//HUMANHEAD rww - play a respawn effect after respawning (don't do on client and let it broadcast)
if (!gameLocal.isClient) {
hhFxInfo fxInfo;
idVec3 boneOffset;
idMat3 boneAxis;
GetJointWorldTransform( "waist", boneOffset, boneAxis );
fxInfo.RemoveWhenDone( true );
fxInfo.SetNormal( boneAxis[1] );
BroadcastFxInfo( spawnArgs.GetString( "fx_spiritReturn" ), boneOffset, GetAxis(), &fxInfo );
}
//HUMANHEAD END
}
}
/*
===========
idPlayer::SelectInitialSpawnPoint
Try to find a spawn point marked 'initial', otherwise
use normal spawn selection.
============
*/
//HUMANHEAD rww - added axis
void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles, idMat3 *axis ) {
idEntity *spot;
idStr skin;
spot = gameLocal.SelectInitialSpawnPoint( this );
if (axis) {
*axis = spot->GetAxis();
}
// set the player skin from the spawn location
if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
spawnArgs.Set( "spawn_skin", skin );
}
// activate the spawn locations targets
spot->PostEventMS( &EV_ActivateTargets, 0, this );
//HUMANHEAD: aob - in case we spawn upside down
#ifdef HUMANHEAD
origin = spot->GetPhysics()->GetOrigin() + spot->GetPhysics()->GetAxis()[2] * (4.0f + CM_BOX_EPSILON);
#else
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
#endif
//HUMANHEAD END
angles = spot->GetPhysics()->GetAxis().ToAngles();
}
/*
===========
idPlayer::SpawnFromSpawnSpot
Chooses a spawn location and spawns the player
============
*/
void idPlayer::SpawnFromSpawnSpot( void ) {
idVec3 spawn_origin;
idAngles spawn_angles;
SelectInitialSpawnPoint( spawn_origin, spawn_angles );
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 ) {
/* HUMANHEAD pdm: Overridden
idVec3 spec_origin;
assert( !gameLocal.isClient );
respawning = true;
Init();
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 );
} 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;
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 ) {
idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
lastTeleFX = gameLocal.time;
}
}
AI_TELEPORT = true;
} else {
AI_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
gameLocal.KillBox( this );
}
// 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;
BecomeActive( TH_THINK );
// run a client frame to drop exactly to the floor,
// initialize animations and other things
Think();
respawning = false;
lastManOver = false;
lastManPlayAgain = false;
isTelefragged = false;
*/
}
/*
===============
idPlayer::SavePersistantInfo
Saves any inventory and player stats when changing levels.
===============
*/
void idPlayer::SavePersistantInfo( void ) {
idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
playerInfo.Clear();
inventory.GetPersistantData( playerInfo );
playerInfo.SetInt( "health", health );
playerInfo.SetInt( "current_weapon", currentWeapon );
playerInfo.SetInt( "max_ammo_energy", spawnArgs.GetInt( "max_ammo_energy" ) ); //HUMANHEAD bjk: possibly unneeded but def works with it in
}
/*
===============
idPlayer::RestorePersistantInfo
Restores any inventory and player stats when changing levels.
===============
*/
void idPlayer::RestorePersistantInfo( void ) {
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", "1" );
}
// HUMANHEAD pdm: Evaluate requirements anytime the inventory items may have changed
inventory.EvaluateRequirements(this);
// HUMANHEAD END
}
/*
================
idPlayer::GetUserInfo
================
*/
idDict *idPlayer::GetUserInfo( void ) {
return &gameLocal.userInfo[ entityNumber ];
}
//HUMANHEAD rww
/*
==============
idPlayer::SetPlayerModel
==============
*/
void idPlayer::SetPlayerModel( bool preserveChannels ) {
int i;
const int strLen = 128;
char *strMem = NULL;
const char *modelname;
idStr customModel, currentModel;
int modelNum;
if (GetUserInfo()->GetInt("ui_modelNum", "0", modelNum)) {
if (!spawnArgs.GetString(va("model_mp%i", modelNum), "", customModel)) {
if (!spawnArgs.GetString("model", "", customModel)) { //use default model if invalid one specified
gameLocal.Error("MP player def has no model specified.\n");
}
}
modelname = customModel.c_str();
}
else {
modelname = spawnArgs.GetString( "model" );
}
if (!modelname || !modelname[0]) {
gameLocal.Error("idPlayer::SetPlayerModel: player model could not be determined.");
}
spawnArgs.GetString("playerModel", "", currentModel);
if (!strcmp(currentModel, modelname)) { //if the player is already using this model, don't bother.
return;
}
spawnArgs.Set("playerModel", modelname);
FreeModelDef();
if (preserveChannels) {
strMem = (char *)_alloca(ANIM_NumAnimChannels*strLen);
for( i = ANIMCHANNEL_TORSO; i < ANIMCHANNEL_EYELIDS; i++ ) {
char *buf = strMem+(i*strLen);
strcpy(buf, GetAnimState(i));
}
}
renderEntity.hModel = animator.SetModel( modelname );
if ( !renderEntity.hModel ) {
idEntity::SetModel( modelname );
return;
}
if (strMem) { //restore anim states
for( i = ANIMCHANNEL_TORSO; i < ANIMCHANNEL_EYELIDS; i++ ) {
char *buf = strMem+(i*strLen);
if (buf[0]) {
SetAnimState(i, buf, 4);
}
}
}
if (IsType(hhPlayer::Type)) {
hhPlayer *hhSelf = static_cast<hhPlayer *>(this);
if (!hhSelf->IsSpiritOrDeathwalking()) { //do not override the skin if they changed skins while spirit walking
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
}
}
// set the callback to update the joints
renderEntity.callback = idEntity::ModelCallback;
animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
animator.GetBounds( gameLocal.time, renderEntity.bounds );
UpdateVisuals();
}
/*
==============
idPlayer::SetSkin
overriding to deal with custom grandfather mp model skin (and possibly others)
==============
*/
void idPlayer::SetSkin( const idDeclSkin *skin ) {
if (!skin) { //if null skin specified, see if our modelDef has an overriden default
const idDeclModelDef *modelDef = animator.ModelDef();
if (modelDef) {
skin = modelDef->GetDefaultSkin();
}
}
idActor::SetSkin(skin);
}
/*
==============
idPlayer::GetModelPortraitName
==============
*/
const char *idPlayer::GetModelPortraitName(bool bThumb) {
int modelNum = GetUserInfo()->GetInt("ui_modelNum");
const char *modelPortraitName;
if (bThumb) {
modelPortraitName = spawnArgs.GetString(va("mtr_modelPortraitThumb%i", modelNum));
}
else {
modelPortraitName = spawnArgs.GetString(va("mtr_modelPortrait%i", modelNum));
}
if (!modelPortraitName || !modelPortraitName[0]) { //if invalid model number, use the default
modelPortraitName = spawnArgs.GetString("mtr_modelPortrait0");
HH_ASSERT(modelPortraitName && modelPortraitName[0]);
}
return modelPortraitName;
}
//HUMANHEAD END
/*
==============
idPlayer::UpdateSkinSetup
==============
*/
void idPlayer::UpdateSkinSetup( bool restart ) {
#ifdef HUMANHEAD //HUMANHEAD rww
team = ( idStr::Icmp( GetUserInfo()->GetString( "ui_team" ), "Blue" ) == 0 );
colorBarIndex = (team == 0) ? 1 : 3; // team0==red, team1==blue (table: red=1, blue=3, yellow=4)
colorBar = colorBarTable[ colorBarIndex ];
SetPlayerModel(true);
if ( gameLocal.gameType == GAME_TDM ) {
if ( !gameLocal.isClient && team != latchedTeam ) {
gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
}
latchedTeam = team;
}
#else //HUMANHEAD END
/*
if ( restart ) {
team = ( idStr::Icmp( GetUserInfo()->GetString( "ui_team" ), "Blue" ) == 0 );
}
if ( gameLocal.gameType == GAME_TDM ) {
if ( team ) {
baseSkinName = "skins/characters/player/marine_mp_blue";
} else {
baseSkinName = "skins/characters/player/marine_mp_red";
}
if ( !gameLocal.isClient && team != latchedTeam ) {
gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
}
latchedTeam = team;
} else {
baseSkinName = GetUserInfo()->GetString( "ui_skin" );
}
if ( !baseSkinName.Length() ) {
baseSkinName = "skins/characters/player/marine_mp";
}
skin = declManager->FindSkin( baseSkinName, false );
assert( skin );
// match the skin to a color band for scoreboard
if ( baseSkinName.Find( "red" ) != -1 ) {
colorBarIndex = 1;
} else if ( baseSkinName.Find( "green" ) != -1 ) {
colorBarIndex = 2;
} else if ( baseSkinName.Find( "blue" ) != -1 ) {
colorBarIndex = 3;
} else if ( baseSkinName.Find( "yellow" ) != -1 ) {
colorBarIndex = 4;
} else {
colorBarIndex = 0;
}
colorBar = colorBarTable[ colorBarIndex ];
if ( PowerUpActive( BERSERK ) ) {
powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
}
*/
#endif
}
/*
==============
idPlayer::BalanceTDM
==============
*/
bool idPlayer::BalanceTDM( void ) {
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 ) ) {
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 ) {
common->DPrintf( "team balance: forcing player %d to %s team\n", entityNumber, balanceTeam ? "blue" : "red" );
team = balanceTeam;
GetUserInfo()->Set( "ui_team", team ? "Blue" : "Red" );
return true;
}
return false;
}
/*
==============
idPlayer::UserInfoChanged
==============
*/
bool idPlayer::UserInfoChanged( bool canModify ) {
idDict *userInfo;
bool modifiedInfo;
bool spec;
bool newready;
userInfo = GetUserInfo();
showWeaponViewModel = userInfo->GetBool( "ui_showGun" );
if ( !gameLocal.isMultiplayer ) {
return false;
}
modifiedInfo = false;
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 ( canModify && gameLocal.mpGame.GetGameState() == idMultiplayerGame::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 ( canModify && spec ) {
userInfo->Set( "ui_spectate", "Play" );
modifiedInfo |= true;
} else if ( spectating ) {
// allow player to leaving spectator mode if they were in it when si_spectators got turned off
forceRespawn = true;
}
wantSpectate = false;
}
newready = ( idStr::Icmp( userInfo->GetString( "ui_ready" ), "Ready" ) == 0 );
if ( ready != newready && gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP && !wantSpectate ) {
gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_07180" ), userInfo->GetString( "ui_name" ), newready ? common->GetLanguageDict()->GetString( "#str_04300" ) : common->GetLanguageDict()->GetString( "#str_04301" ) );
}
ready = newready;
team = ( idStr::Icmp( userInfo->GetString( "ui_team" ), "Blue" ) == 0 );
// server maintains TDM balance
if ( canModify && gameLocal.gameType == GAME_TDM && !gameLocal.mpGame.IsInGame( entityNumber ) && g_balanceTDM.GetBool() ) {
modifiedInfo |= BalanceTDM( );
}
UpdateSkinSetup( false );
isChatting = userInfo->GetBool( "ui_chat", "0" );
if ( canModify && isChatting && AI_DEAD ) {
// if dead, always force chat icon off.
isChatting = false;
userInfo->SetBool( "ui_chat", false );
modifiedInfo |= true;
}
return modifiedInfo;
}
/*
===============
idPlayer::UpdateHudAmmo
===============
*/
void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
/* HUMANHEAD pdm: Overridden
int inclip;
int ammoamount;
assert( weapon.GetEntity() );
assert( _hud );
inclip = weapon.GetEntity()->AmmoInClip();
ammoamount = weapon.GetEntity()->AmmoAvailable();
if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() ) {
// show infinite ammo
_hud->SetStateString( "player_ammo", "" );
_hud->SetStateString( "player_totalammo", "" );
} else {
// show remaining ammo
_hud->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip ) );
_hud->SetStateString( "player_ammo", weapon.GetEntity()->ClipSize() ? va( "%i", inclip ) : "--" ); // how much in the current clip
_hud->SetStateString( "player_clips", weapon.GetEntity()->ClipSize() ? va( "%i", ammoamount / weapon.GetEntity()->ClipSize() ) : "--" );
_hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
}
_hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
_hud->SetStateBool( "player_clip_empty", ( weapon.GetEntity()->ClipSize() ? inclip == 0 : false ) );
_hud->SetStateBool( "player_clip_low", ( weapon.GetEntity()->ClipSize() ? inclip <= weapon.GetEntity()->LowAmmo() : false ) );
_hud->HandleNamedEvent( "updateAmmo" );
*/
}
/*
===============
idPlayer::UpdateHudStats
===============
*/
void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
/* HUMANHEAD pdm: Overridden
int staminapercentage;
float max_stamina;
assert( _hud );
max_stamina = pm_stamina.GetFloat();
if ( !max_stamina ) {
// stamina disabled, so show full stamina bar
staminapercentage = 100.0f;
} else {
staminapercentage = idMath::FtoiFast( 100.0f * stamina / max_stamina );
}
_hud->SetStateInt( "player_health", health );
_hud->SetStateInt( "player_stamina", staminapercentage );
_hud->SetStateInt( "player_armor", inventory.armor );
_hud->SetStateInt( "player_hr", heartRate );
_hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 );
_hud->HandleNamedEvent( "updateArmorHealthAir" );
if ( healthPulse ) {
_hud->HandleNamedEvent( "healthPulse" );
StartSound( "snd_healthpulse", SND_CHANNEL_ITEM, 0, false, NULL );
healthPulse = false;
}
if ( healthTake ) {
_hud->HandleNamedEvent( "healthPulse" );
StartSound( "snd_healthtake", SND_CHANNEL_ITEM, 0, false, NULL );
healthTake = false;
}
if ( inventory.ammoPulse ) {
_hud->HandleNamedEvent( "ammoPulse" );
inventory.ammoPulse = false;
}
if ( inventory.weaponPulse ) {
// We need to update the weapon hud manually, but not
// the armor/ammo/health because they are updated every
// frame no matter what
UpdateHudWeapon();
_hud->HandleNamedEvent( "weaponPulse" );
inventory.weaponPulse = false;
}
if ( inventory.armorPulse ) {
_hud->HandleNamedEvent( "armorPulse" );
inventory.armorPulse = false;
}
UpdateHudAmmo( _hud );
*/
}
/*
===============
idPlayer::UpdateHudWeapon
===============
*/
void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
/* HUMANHEAD pdm: Overridden
idUserInterface *hud = idPlayer::hud;
// if updating the hud of a followed client
if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
if ( p->spectating && p->spectator == entityNumber ) {
assert( p->hud );
hud = p->hud;
}
}
if ( !hud ) {
return;
}
for ( int i = 0; i < MAX_WEAPONS; i++ ) {
const char *weapnum = va( "def_weapon%d", i );
const char *hudWeap = va( "weapon%d", i );
int weapstate = 0;
if ( inventory.weapons & ( 1 << i ) ) {
const char *weap = spawnArgs.GetString( weapnum );
if ( weap && *weap ) {
weapstate++;
}
if ( idealWeapon == i ) {
weapstate++;
}
}
hud->SetStateInt( hudWeap, weapstate );
}
if ( flashWeapon ) {
hud->HandleNamedEvent( "weaponChange" );
}
*/
}
/*
===============
idPlayer::DrawHUD
===============
*/
void idPlayer::DrawHUD( idUserInterface *_hud ) {
/* HUMANHEAD pdm: Overridden
if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !_hud || !g_showHud.GetBool() ) {
return;
}
UpdateHudStats( _hud );
_hud->SetStateString( "weapicon", weapon.GetEntity()->Icon() );
// 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" ) );
weapon.GetEntity()->UpdateGUI();
_hud->Redraw( gameLocal.realClientTime );
// weapon targeting crosshair
if ( !GuiActive() ) {
if ( cursor && weapon.GetEntity()->ShowCrosshair() ) {
cursor->Redraw( gameLocal.realClientTime );
}
}
*/
}
/*
===============
idPlayer::EnterCinematic
===============
*/
void idPlayer::EnterCinematic( void ) {
Hide();
StopSound( SND_CHANNEL_PDA, false );
physicsObj.SetLinearVelocity( vec3_origin );
SetState( "EnterCinematic" );
UpdateScript();
if ( weaponEnabled && weapon.GetEntity() ) {
weapon.GetEntity()->EnterCinematic();
}
AI_FORWARD = false;
AI_BACKWARD = false;
AI_STRAFE_LEFT = false;
AI_STRAFE_RIGHT = false;
AI_RUN = false;
AI_ATTACK_HELD = false;
AI_WEAPON_FIRED = false;
AI_JUMP = false;
AI_CROUCH = false;
AI_ONGROUND = true;
AI_REALLYFALL = false; //HUMANHEAD rww
AI_ONLADDER = false;
AI_DEAD = ( health <= 0 );
AI_RUN = false;
AI_PAIN = false;
AI_HARDLANDING = false;
AI_SOFTLANDING = false;
AI_RELOAD = false;
AI_TELEPORT = false;
AI_TURN_LEFT = false;
AI_TURN_RIGHT = false;
}
/*
===============
idPlayer::ExitCinematic
===============
*/
void idPlayer::ExitCinematic( void ) {
Show();
if ( weaponEnabled && weapon.GetEntity() ) {
weapon.GetEntity()->ExitCinematic();
}
SetState( "ExitCinematic" );
UpdateScript();
}
/*
=====================
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();
assert(!influenceActive); //HUMANHEAD rww
if ( gameLocal.time - lastDmgTime < 500 ) {
forwardspeed = velocity * viewAxis[ 0 ];
sidespeed = velocity * viewAxis[ 1 ];
AI_FORWARD = AI_ONGROUND && ( forwardspeed > 20.01f );
AI_BACKWARD = AI_ONGROUND && ( forwardspeed < -20.01f );
AI_STRAFE_LEFT = AI_ONGROUND && ( sidespeed > 20.01f );
AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
} else if ( xyspeed > MIN_BOB_SPEED ) {
AI_FORWARD = AI_ONGROUND && ( usercmd.forwardmove > 0 );
AI_BACKWARD = AI_ONGROUND && ( usercmd.forwardmove < 0 );
AI_STRAFE_LEFT = AI_ONGROUND && ( usercmd.rightmove < 0 );
AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
} else {
AI_FORWARD = false;
AI_BACKWARD = false;
AI_STRAFE_LEFT = false;
AI_STRAFE_RIGHT = false;
}
AI_RUN = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
AI_DEAD = ( health <= 0 );
}
/*
==================
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
AI_WEAPON_FIRED = true;
// update view feedback
playerView.WeaponFireFeedback( weaponDef );
}
/*
===============
idPlayer::StopFiring
===============
*/
void idPlayer::StopFiring( void ) {
AI_ATTACK_HELD = false;
AI_WEAPON_FIRED = false;
AI_RELOAD = false;
if ( weapon.GetEntity() ) {
weapon.GetEntity()->EndAttack();
}
}
/*
===============
idPlayer::FireWeapon
===============
*/
void idPlayer::FireWeapon( void ) {
/* HUMANHEAD pdm: Overridden
idMat3 axis;
idVec3 muzzle;
if ( privateCameraView ) {
return;
}
if ( g_editEntityMode.GetInteger() ) {
GetViewPos( muzzle, axis );
if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
return;
}
}
if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
if ( weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
AI_ATTACK_HELD = true;
weapon.GetEntity()->BeginAttack();
if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
if ( hud ) {
hud->HandleNamedEvent( "soulCubeNotReady" );
}
SelectWeapon( previousWeapon, false );
}
} else {
NextBestWeapon();
}
}
if ( hud ) {
if ( tipUp ) {
HideTip();
}
// may want to track with with a bool as well
// keep from looking up named events so often
if ( objectiveUp ) {
HideObjective();
}
}
*/
}
/*
===============
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 ) ) {
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
if ( weap != "" ) {
idWeapon::CacheWeapon( weap );
} else {
inventory.weapons &= ~( 1 << w );
}
}
}
}
/*
===============
idPlayer::Give
===============
*/
bool idPlayer::Give( const char *statname, const char *value ) {
/* HUMANHEAD pdm: Overridden
int amount;
if ( AI_DEAD ) {
return false;
}
if ( !idStr::Icmp( statname, "health" ) ) {
if ( health >= inventory.maxHealth ) {
return false;
}
amount = atoi( value );
if ( amount ) {
health += amount;
if ( health > inventory.maxHealth ) {
health = inventory.maxHealth;
}
if ( hud ) {
hud->HandleNamedEvent( "healthPulse" );
}
}
} else if ( !idStr::Icmp( statname, "stamina" ) ) {
if ( stamina >= 100 ) {
return false;
}
stamina += atof( value );
if ( stamina > 100 ) {
stamina = 100;
}
} else if ( !idStr::Icmp( statname, "heartRate" ) ) {
heartRate += atoi( value );
if ( heartRate > MAX_HEARTRATE ) {
heartRate = MAX_HEARTRATE;
}
} 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 {
return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
}
return true;
*/ return false;
}
/*
===============
idPlayer::GiveHealthPool
adds health to the player health pool
===============
*/
void idPlayer::GiveHealthPool( float amt ) {
// HUMANHEAD pdm: not used
}
/*
===============
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;
#if !HUMANHEAD
int numPickup;
#endif
if ( gameLocal.isMultiplayer && spectating ) {
return false;
}
//HUMANHEAD rww
if (gameLocal.isServer && gameLocal.localClientNum != entityNumber) {
//on a server for other players, these otherwise wouldn't be zero'd back out
healthPulse = false;
spiritPulse = false;
inventory.ammoPulse = false;
inventory.weaponPulse = false;
}
//HUMANHEAD END
item->GetAttributes( attr );
gave = false;
#if !HUMANHEAD
numPickup = inventory.pickupItemNames.Num();
#endif
for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
arg = attr.GetKeyVal( i );
if ( Give( arg->GetKey(), arg->GetValue() ) ) {
gave = true;
}
}
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
UpdateHudWeapon( false );
hud->HandleNamedEvent( "weaponPulse" );
}
// HUMANHEAD CJR: Force certain items to be picked up when touched (specifically, lifeforce)
if ( !gave && item->spawnArgs.GetBool( "forcePickup", "0" ) ) {
gave = true;
} // END HUMANHEAD
// display the pickup feedback on the hud
#if HUMANHEAD
if ( gave ) {
inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ), item->spawnArgs.MatchPrefix("inv_weapon", NULL)!=NULL );
//HUMANHEAD rww - player prediction purposely does not touch triggers and that is how we pick up our items, so we send events for pickups.
if (gameLocal.isServer && gameLocal.localClientNum != entityNumber) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
int menuSendEvents = (1<<MENU_NET_EVENT_INVPICKUP);
if ( healthPulse ) {
menuSendEvents |= (1<<MENU_NET_EVENT_HEALTHPULSE);
}
if ( spiritPulse ) {
menuSendEvents |= (1<<MENU_NET_EVENT_SPIRITPULSE);
}
if ( inventory.ammoPulse ) {
menuSendEvents |= (1<<MENU_NET_EVENT_AMMOPULSE);
}
if ( inventory.weaponPulse ) {
menuSendEvents |= (1<<MENU_NET_EVENT_WEAPONPULSE);
}
msg.Init(msgBuf, sizeof(msgBuf));
msg.WriteBits(menuSendEvents, MENU_NET_EVENT_NUM);
if (menuSendEvents & (1<<MENU_NET_EVENT_INVPICKUP)) {
msg.WriteBits(gameLocal.GetSpawnId(item), 32);
}
ServerSendEvent(EVENT_MENUEVENT, &msg, false, -1, entityNumber);
}
//HUMANHEAD END
}
#else
if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ) );
}
#endif
return gave;
}
/*
===============
idPlayer::GiveInventoryItem
===============
*/
bool idPlayer::GiveInventoryItem( idDict *item ) {
if ( gameLocal.isMultiplayer && spectating ) {
return false;
}
inventory.items.Append( new idDict( *item ) );
#if HUMANHEAD // HUMANHEAD pdm: Make inventory items flow through the same logic as other pickups
inventory.AddPickupName(item->GetString( "inv_name" ), item->GetString( "inv_icon" ), false);
if ( hud ) {
hud->HandleNamedEvent( "invPickup" );
}
// Evaluate requirements anytime the inventory items may have changed
inventory.EvaluateRequirements(this);
#else // HUMANHEAD END
idItemInfo info;
const char* itemName = item->GetString( "inv_name" );
if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
info.name = common->GetLanguageDict()->GetString( itemName );
} else {
info.name = itemName;
}
info.icon = item->GetString( "inv_icon" );
inventory.pickupItemNames.Append( info );
if ( hud ) {
hud->SetStateString( "itemicon", info.icon );
hud->HandleNamedEvent( "invPickup" );
}
#endif
return true;
}
/*
===============
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() );
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 ) {
return;
}
if ( spectating || gameLocal.inCinematic || influenceActive ) {
return;
}
if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
weapon.GetEntity()->Reload();
}
}
/*
===============
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 ) {
/* HUMANHEAD pdm: overridden
const char *weap;
int w;
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
return;
}
if ( gameLocal.isClient ) {
return;
}
// check if we have any weapons
if ( !inventory.weapons ) {
return;
}
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 != currentWeapon ) && ( w != idealWeapon ) ) {
idealWeapon = w;
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
UpdateHudWeapon();
}
*/
}
/*
===============
idPlayer::PrevWeapon
===============
*/
void idPlayer::PrevWeapon( void ) {
/* HUMANHEAD pdm: overridden
const char *weap;
int w;
if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
return;
}
if ( gameLocal.isClient ) {
return;
}
// check if we have any weapons
if ( !inventory.weapons ) {
return;
}
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 != currentWeapon ) && ( w != idealWeapon ) ) {
idealWeapon = w;
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
UpdateHudWeapon();
}
*/
}
/*
===============
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 ) {
return;
}
// HUMANHEAD pdm: removed pda logic
if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
num = weapon_fists;
hiddenWeapon ^= 1;
if ( hiddenWeapon && weapon.GetEntity() ) {
weapon.GetEntity()->LowerWeapon();
} else {
weapon.GetEntity()->RaiseWeapon();
}
}
weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
if ( !weap[ 0 ] ) {
gameLocal.Printf( "Invalid weapon\n" );
return;
}
if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
//HUMANHEAD jsh added alt ammo check
if ( !inventory.HasAmmo( weap ) && !inventory.HasAltAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
return;
}
if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
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::DropWeapon
=================
*/
void idPlayer::DropWeapon( bool died ) {
idVec3 forward, up;
int inclip, ammoavailable;
assert( !gameLocal.isClient );
if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
return;
}
if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
return;
}
// ammoavailable is how many shots we can fire
// inclip is which amount is in clip right now
ammoavailable = weapon.GetEntity()->AmmoAvailable();
inclip = weapon.GetEntity()->AmmoInClip();
// don't drop a grenade if we have none left
if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
return;
}
// expect an ammo setup that makes sense before doing any dropping
// ammoavailable is -1 for infinite ammo, and weapons like chainsaw
// a bad ammo config usually indicates a bad weapon state, so we should not drop
// used to be an assertion check, but it still happens in edge cases
if ( ( ammoavailable != -1 ) && ( ammoavailable - inclip < 0 ) ) {
common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
return;
}
idEntity *item = NULL;
if ( died ) {
// ain't gonna throw you no weapon if I'm dead
item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
} else {
viewAngles.ToVectors( &forward, NULL, &up );
item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
}
if ( !item ) {
return;
}
// set the appropriate ammo in the dropped object
const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
if ( keyval ) {
item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
idStr inclipKey = keyval->GetKey();
inclipKey.Insert( "inclip_", 4 );
item->spawnArgs.SetInt( inclipKey, inclip );
}
if ( !died ) {
// remove from our local inventory completely
inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
weapon.GetEntity()->ResetAmmoClip();
NextWeapon();
weapon.GetEntity()->WeaponStolen();
weaponGone = true;
}
}
/*
=================
idPlayer::StealWeapon
steal the target player's current weapon
=================
*/
void idPlayer::StealWeapon( idPlayer *player ) {
assert( !gameLocal.isClient );
// make sure there's something to steal
#ifdef HUMANHEAD //aob: Can't cast weapon to idWeapon because hhWeapon doesn't inherit from it.
if ( !player->weapon.IsValid() || !player->weapon->CanDrop() || weaponGone ) {
return;
}
#else
idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
return;
}
#endif
// steal - we need to effectively force the other player to abandon his weapon
int newweap = player->currentWeapon;
if ( newweap == -1 ) {
return;
}
// might be just dropped - check inventory
if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
return;
}
const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
assert( weapon_classname );
int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
int inclip = player->weapon.GetEntity()->AmmoInClip();
if ( ( ammoavailable != -1 ) && ( ammoavailable - inclip < 0 ) ) {
// see DropWeapon
common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
// we still steal the weapon, so let's use the default ammo levels
inclip = -1;
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
assert( decl );
const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
assert( keypair );
ammoavailable = atoi( keypair->GetValue() );
}
player->weapon.GetEntity()->WeaponStolen();
player->inventory.Drop( player->spawnArgs, NULL, newweap );
player->SelectWeapon( weapon_fists, false );
// in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
// this will ensure the firing actually stops
player->weaponGone = true;
// give weapon, setup the ammo count
Give( "weapon", weapon_classname );
ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
idealWeapon = newweap;
inventory.ammo[ ammo_i ] += ammoavailable;
inventory.clip[ newweap ] = inclip;
}
/*
===============
idPlayer::ActiveGui
===============
*/
idUserInterface *idPlayer::ActiveGui( void ) {
return focusUI;
}
/*
===============
idPlayer::Weapon_Combat
===============
*/
void idPlayer::Weapon_Combat( void ) {
/* HUMANHEAD pdm: OVERRIDDEN
*/
}
/*
===============
idPlayer::LowerWeapon
===============
*/
void idPlayer::LowerWeapon( void ) {
if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
weapon.GetEntity()->LowerWeapon();
}
}
/*
===============
idPlayer::RaiseWeapon
===============
*/
void idPlayer::RaiseWeapon( void ) {
if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
weapon.GetEntity()->RaiseWeapon();
}
}
/*
===============
idPlayer::WeaponLoweringCallback
===============
*/
void idPlayer::WeaponLoweringCallback( void ) {
SetState( "LowerWeapon" );
UpdateScript();
}
/*
===============
idPlayer::WeaponRisingCallback
===============
*/
void idPlayer::WeaponRisingCallback( void ) {
SetState( "RaiseWeapon" );
UpdateScript();
}
/*
===============
idPlayer::Weapon_GUI
===============
*/
void idPlayer::Weapon_GUI( void ) {
/* HUMANHEAD pdm: OVERRIDDEN
if ( !objectiveSystemOpen ) {
if ( idealWeapon != currentWeapon ) {
Weapon_Combat();
}
StopFiring();
weapon.GetEntity()->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 && focusGUIent && ui == focusUI ) {
focusGUIent->UpdateVisuals();
}
}
if ( gameLocal.isClient ) {
// we predict enough, but don't want to execute commands
return;
}
if ( focusGUIent ) {
HandleGuiCommands( focusGUIent, command );
} else {
HandleGuiCommands( this, command );
}
}
*/
}
/*
===============
idPlayer::UpdateWeapon
===============
*/
void idPlayer::UpdateWeapon( void ) {
/* HUMANHEAD pdm: OVERRIDDEN
if ( health <= 0 ) {
return;
}
assert( !spectating );
if ( gameLocal.isClient ) {
// clients need to wait till the weapon and it's world model entity
// are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
if ( !weapon.GetEntity()->IsWorldModelReady() ) {
return;
}
}
// always make sure the weapon is correctly setup before accessing it
if ( !weapon.GetEntity()->IsLinked() ) {
if ( idealWeapon != -1 ) {
animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ idealWeapon ] );
assert( weapon.GetEntity()->IsLinked() );
} else {
return;
}
}
if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
HideTip();
}
if ( g_dragEntity.GetBool() ) {
StopFiring();
weapon.GetEntity()->LowerWeapon();
dragEntity.Update( this );
} else if ( ActiveGui() ) {
// gui handling overrides weapon use
Weapon_GUI();
} else if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
Weapon_NPC();
} else {
Weapon_Combat();
}
if ( hiddenWeapon ) {
weapon.GetEntity()->LowerWeapon();
}
// update weapon state, particles, dlights, etc
weapon.GetEntity()->PresentWeapon( showWeaponViewModel );
*/
}
/*
===============
idPlayer::SpectateFreeFly
===============
*/
void idPlayer::SpectateFreeFly( bool force ) {
idPlayer *player;
idVec3 newOrig;
idVec3 spawn_origin;
idAngles spawn_angles;
//HUMANHEAD rww
if (gameLocal.isClient) {
return;
}
//HUMANHEAD END
player = gameLocal.GetClientByNum( spectator );
if ( force || gameLocal.time > lastSpectateChange ) {
spectator = entityNumber;
if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
newOrig = player->GetPhysics()->GetOrigin();
//HUMANHEAD rww - reworked for gravity
/*
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;
*/
idMat3 plAx = player->GetPhysics()->GetAxis();
if ( player->physicsObj.IsCrouching() ) {
newOrig += plAx[2]*pm_crouchviewheight.GetFloat();
} else {
newOrig += plAx[2]*pm_normalviewheight.GetFloat();
}
newOrig += plAx[2]*SPECTATE_RAISE;
idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
idVec3 start = player->GetPhysics()->GetOrigin();
start += plAx[2]*pm_spectatebbox.GetFloat() * 0.5f;
//HUMANHEAD END
trace_t t;
// assuming spectate bbox is inside stand or crouch box
gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
newOrig.Lerp( start, newOrig, t.fraction );
SetOrigin( newOrig );
idAngles angle = player->viewAngles;
angle[ 2 ] = 0;
SetViewAngles( angle );
} else {
SelectInitialSpawnPoint( spawn_origin, spawn_angles );
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 ) {
int latchedSpectator = spectator;
spectator = gameLocal.GetNextClientNum( spectator );
player = gameLocal.GetClientByNum( spectator );
assert( player ); // never call here when the current spectator is wrong
// ignore other spectators
while ( latchedSpectator != spectator && player->spectating ) {
spectator = gameLocal.GetNextClientNum( spectator );
player = gameLocal.GetClientByNum( spectator );
}
lastSpectateChange = gameLocal.time + 500;
}
}
/*
===============
idPlayer::UpdateSpectating
===============
*/
void idPlayer::UpdateSpectating( void ) {
assert( spectating );
assert( !gameLocal.isClient );
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 ) {
SpectateFreeFly( false );
} else if ( usercmd.buttons & BUTTON_ATTACK ) {
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;
}
src->UnreadToken( &token );
return false;
}
/*
==============
idPlayer::Collide
==============
*/
bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
idEntity *other;
if ( gameLocal.isClient ) {
return false;
}
other = gameLocal.entities[ collision.c.entityNum ];
if ( other ) {
// HUMANHEAD nla - Taken from an earlier version of the code. Added back in to allow the moving of ragdolls.
impactInfo_t info;
other->GetImpactInfo( this, collision.c.id, collision.c.point, &info );
if ( info.invMass ) {
//HUMANHEAD rww - if other is a moveable, cap the z velocity, so the player can't launch things unnaturally
idVec3 localVel = velocity;
// mdl: Ugly hack to fix movables in harvesterb gravity puzzle
float negVel = (other->GetPhysics()->GetMass() > 50000.0f ? -24.0f : -48.0f);
if (other->IsType(idMoveable::Type)) {
if (localVel.z > 128.0f) {
localVel.z = 128.0f;
} else if (localVel.z < negVel) { // mdl: For inverse gravity
localVel.z = negVel;
}
}
//HUMANHEAD END
other->GetImpactInfo( this, collision.c.id, collision.c.point, &info );
other->ApplyImpulse( this, collision.c.id, collision.c.point, localVel / info.invMass );
}
// HUMANHEAD END
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 ) {
/* HUMANHEAD pdm: overridden
if( hud ) {
idLocationEntity* locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
if( locationEntity ) {
hud->SetStateString( "location", locationEntity->GetLocation() );
} else {
hud->SetStateString( "location", common->GetLanguageDict()->GetString( "#str_02911" ) );
}
}
*/
}
/*
================
idPlayer::ClearFocus
Clears the focus cursor
================
*/
void idPlayer::ClearFocus( void ) {
focusGUIent = NULL;
focusUI = NULL;
}
//HUMANHEAD PCF rww 09/15/06 - female mp sounds
bool idPlayer::IsFemale(void) {
if (gameLocal.isMultiplayer) {
int modelNum = GetUserInfo()->GetInt("ui_modelNum");
if (modelNum == 14 || //blonde
modelNum == 16 || //elhuit
modelNum == 18 || //jen
modelNum == 19) { //mother
return true;
}
}
return false;
}
//HUMANHEAD 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 ) {
/* HUMANHEAD pdm: OVERRIDDEN
idClipModel *clipModelList[ MAX_GENTITIES ];
idClipModel *clip;
int listedClipModels;
idEntity *oldFocus;
idEntity *ent;
idUserInterface *oldUI;
idAI *oldChar;
int oldTalkCursor;
idAFEntity_Vehicle *oldVehicle;
int i, j;
idVec3 start, end;
bool allowFocus;
const char *command;
trace_t trace;
guiPoint_t pt;
const idKeyValue *kv;
sysEvent_t ev;
idUserInterface *ui;
if ( gameLocal.inCinematic ) {
return;
}
// only update the focus character when attack button isn't pressed so players
// can still chainsaw NPC's
if ( gameLocal.isMultiplayer || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
allowFocus = false;
} else {
allowFocus = true;
}
oldFocus = focusGUIent;
oldUI = focusUI;
oldChar = focusCharacter;
oldTalkCursor = talkCursor;
oldVehicle = focusVehicle;
if ( focusTime <= gameLocal.time ) {
ClearFocus();
}
// don't let spectators interact with GUIs
if ( spectating ) {
return;
}
start = GetEyePosition();
end = start + viewAngles.ToForward() * 80.0f;
// player identification -> names to the hud
if ( gameLocal.isMultiplayer && entityNumber == gameLocal.localClientNum ) {
idVec3 end = start + viewAngles.ToForward() * 768.0f;
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
int iclient = -1;
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
iclient = trace.c.entityNum;
}
if ( MPAim != iclient ) {
lastMPAim = MPAim;
MPAim = iclient;
lastMPAimTime = gameLocal.realClientTime;
}
}
idBounds bounds( start );
bounds.AddPoint( end );
listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
// no pretense at sorting here, just assume that there will only be one active
// gui within range along the trace
for ( i = 0; i < listedClipModels; i++ ) {
clip = clipModelList[ i ];
ent = clip->GetEntity();
if ( ent->IsHidden() ) {
continue;
}
if ( allowFocus ) {
if ( ent->IsType( idAFAttachment::Type ) ) {
idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
if ( body && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
ClearFocus();
focusCharacter = static_cast<idAI *>( body );
talkCursor = 1;
focusTime = gameLocal.time + FOCUS_TIME;
break;
}
}
continue;
}
if ( ent->IsType( idAI::Type ) ) {
if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
ClearFocus();
focusCharacter = static_cast<idAI *>( ent );
talkCursor = 1;
focusTime = gameLocal.time + FOCUS_TIME;
break;
}
}
continue;
}
if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
ClearFocus();
focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
focusTime = gameLocal.time + FOCUS_TIME;
break;
}
continue;
}
}
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 ) {
// we have a hit
renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
if ( !focusGUIrenderEntity ) {
continue;
}
if ( pt.guiId == 1 ) {
ui = focusGUIrenderEntity->gui[ 0 ];
} else if ( pt.guiId == 2 ) {
ui = focusGUIrenderEntity->gui[ 1 ];
} else {
ui = focusGUIrenderEntity->gui[ 2 ];
}
if ( ui == NULL ) {
continue;
}
ClearFocus();
focusGUIent = ent;
focusUI = ui;
if ( oldFocus != ent ) {
// new activation
// going to see if we have anything in inventory a gui might be interested in
// need to enumerate inventory items
focusUI->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" );
const char *iicon = item->GetString( "inv_icon" );
const char *itext = item->GetString( "inv_text" );
focusUI->SetStateString( va( "inv_name_%i", j), iname );
focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
focusUI->SetStateString( va( "inv_text_%i", j), itext );
kv = item->MatchPrefix("inv_id", NULL);
if ( kv ) {
focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
}
focusUI->SetStateInt( iname, 1 );
}
for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
const char *p = inventory.pdaSecurity[ j ];
if ( p && *p ) {
focusUI->SetStateInt( p, 1 );
}
}
int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
focusUI->SetStateString( "player_health", va("%i", health ) );
focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
while ( kv ) {
focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
}
}
// clamp the mouse to the corner
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
command = focusUI->HandleEvent( &ev, gameLocal.time );
HandleGuiCommands( focusGUIent, command );
// move to an absolute position
ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
command = focusUI->HandleEvent( &ev, gameLocal.time );
HandleGuiCommands( focusGUIent, command );
focusTime = gameLocal.time + FOCUS_GUI_TIME;
break;
}
}
if ( focusGUIent && focusUI ) {
if ( !oldFocus || oldFocus != focusGUIent ) {
command = focusUI->Activate( true, gameLocal.time );
HandleGuiCommands( focusGUIent, command );
StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
// HideTip();
// HideObjective();
}
} else if ( oldFocus && oldUI ) {
command = oldUI->Activate( false, gameLocal.time );
HandleGuiCommands( oldFocus, command );
StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
}
if ( cursor && ( oldTalkCursor != talkCursor ) ) {
cursor->SetStateInt( "talkcursor", talkCursor );
}
if ( oldChar != focusCharacter && hud ) {
if ( focusCharacter ) {
hud->SetStateString( "npc", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
hud->HandleNamedEvent( "showNPC" );
// HideTip();
// HideObjective();
} else {
hud->SetStateString( "npc", "" );
hud->HandleNamedEvent( "hideNPC" );
}
}
*/
}
/*
=================
idPlayer::CrashLand
Check for hard landings that generate sound events
=================
*/
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
/* HUMANHEAD pdm: OVERRIDDEN
idVec3 origin, velocity;
idVec3 gravityVector, gravityNormal;
float delta;
float hardDelta, fatalDelta;
float dist;
float vel, acc;
float t;
float a, b, c, den;
waterLevel_t waterLevel;
bool noDamage;
AI_SOFTLANDING = false;
AI_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;
StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
break;
}
}
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;
}
// allow falling a bit further for multiplayer
if ( gameLocal.isMultiplayer ) {
fatalDelta = 75.0f;
hardDelta = 50.0f;
} else {
fatalDelta = 65.0f;
hardDelta = 45.0f;
}
if ( delta > fatalDelta ) {
AI_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 > hardDelta ) {
AI_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 > 30 ) {
AI_HARDLANDING = 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 > 7 ) {
AI_SOFTLANDING = true;
landChange = -8;
landTime = gameLocal.time;
} else if ( delta > 3 ) {
// just walk on
}
*/
}
/*
===============
idPlayer::BobCycle
===============
*/
void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
/* HUMANHEAD pdm: OVERRIDDEN
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;
gravityDir = physicsObj.GetGravityNormal();
vel = velocity - ( velocity * gravityDir ) * gravityDir;
xyspeed = vel.LengthFast();
// do not evaluate the bob for other clients
// when doing a spectate follow, don't do any weapon bobbing
if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
viewBobAngles.Zero();
viewBob.Zero();
return;
}
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.msec ) & 255;
bobFoot = ( bobCycle & 128 ) >> 7;
bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
}
// calculate angles for view bobbing
viewBobAngles.Zero();
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;
}
viewBob[2] += bob;
// 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 ) {
/* HUMANHEAD pdm: Overridden
int i;
idAngles delta;
if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
// 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 ( DoThirdPersonDeath() ) { //HUMANHEAD rww - changed to use utility function
viewAngles.roll = 0.0f;
viewAngles.pitch = 30.0f;
} else {
viewAngles.roll = 40.0f;
viewAngles.pitch = -15.0f;
}
return;
}
// circularly clamp the angles with deltas
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::ToggleScoreboard
==============
*/
void idPlayer::ToggleScoreboard( void ) {
scoreBoardOpen ^= 1;
}
/*
==============
idPlayer::Spectate
==============
*/
void idPlayer::Spectate( bool spectate ) {
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
//assert( ( teleportEntity.GetEntity() != NULL ) || ( IsHidden() == spectating ) );
//HUMANHEAD rww - this assert is no longer valid because we hide the player instantly on death for spawning the ragdoll entity.
if ( spectating == spectate ) {
return;
}
//HUMANHEAD rww
if (InVehicle()) { //get out of vehicle if we're in one
if (GetVehicleInterface()) {
hhVehicle *vehicle = GetVehicleInterface()->GetVehicle();
if (vehicle) {
vehicle->EjectPilot();
}
}
}
if (spectate) { //going into spectator
if (IsType(hhPlayer::Type)) {
hhPlayer *hhSelf = static_cast<hhPlayer *>(this);
if (hhSelf->IsSpiritWalking()) { //stop spirit walking
hhSelf->StopSpiritWalk(true);
}
if (hhSelf->hand.IsValid() && hhSelf->hand.GetEntity()) { //remove hand
hhSelf->hand->RemoveHand();
}
if (hhSelf->handNext.IsValid() && hhSelf->handNext.GetEntity()) { //remove hand
hhSelf->handNext->RemoveHand();
}
if (hhSelf->IsLighterOn()) { //turn lighter off
hhSelf->LighterOff();
}
}
if (weapon.IsValid() && weapon.GetEntity()) { //remove weapon
weapon->PostEventMS(&EV_Remove, 0);
weapon = NULL;
}
} else { //shouldn't have a weapon or anything while spectating
if (weapon.IsValid()) {
gameLocal.Error("Valid weapon on player %i when coming out of spectator.", entityNumber);
}
}
//HUMANHEAD END
spectating = spectate;
if ( gameLocal.isServer ) {
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.WriteBits( spectating, 1 );
ServerSendEvent( EVENT_SPECTATE, &msg, false, -1 );
}
if ( spectating ) {
// join the spectators
spectator = this->entityNumber;
Init();
StopRagdoll();
SetPhysics( &physicsObj );
physicsObj.DisableClip();
Hide();
Event_DisableWeapon();
//HUMANHEAD rww - respawn going into spectator, since we could otherwise clip out of the world in certain gravity/wallwalk orientations
if (!gameLocal.isClient) {
idEntity *spot = gameLocal.SelectInitialSpawnPoint( this );
if (spot) {
idVec3 spawn_origin = spot->GetPhysics()->GetOrigin();
spawn_origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
idAngles spawn_angles = spot->GetPhysics()->GetAxis().ToAngles();
spawn_angles.pitch = 0.0f;
spawn_angles.roll = 0.0f;
spawn_origin[2] += pm_normalviewheight.GetFloat();
spawn_origin[2] += SPECTATE_RAISE;
SetAxis(mat3_identity);
physicsObj.SetAxis(mat3_identity);
physicsObj.SetGravity(gameLocal.GetGravity());
SetOrigin( spawn_origin );
SetViewAngles( spawn_angles );
}
}
//HUMANHEAD END
#if !HUMANHEAD
if ( hud ) {
hud->HandleNamedEvent( "aim_clear" );
MPAimFadeTime = 0;
}
#endif
} else {
// put everything back together again
currentWeapon = -1; // to make sure the def will be loaded if necessary
Show();
Event_EnableWeapon();
}
SetClipModel();
}
/*
==============
idPlayer::SetClipModel
==============
*/
void idPlayer::SetClipModel( void ) {
idBounds bounds;
if ( spectating ) {
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;
if ( pm_usecylinder.GetBool() ) {
newClip = new idClipModel( idTraceModel( bounds, 8 ) );
newClip->Translate( physicsObj.PlayerGetOrigin() );
physicsObj.SetClipModel( newClip, 1.0f );
} else {
newClip = new idClipModel( idTraceModel( bounds ) );
newClip->Translate( physicsObj.PlayerGetOrigin() );
physicsObj.SetClipModel( newClip, 1.0f );
}
}
/*
==============
idPlayer::UseVehicle
==============
*/
void idPlayer::UseVehicle( void ) {
trace_t trace;
idVec3 start, end;
idEntity *ent;
if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
Show();
static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
} else {
start = GetEyePosition();
end = start + viewAngles.ToForward() * 80.0f;
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
if ( trace.fraction < 1.0f ) {
ent = gameLocal.entities[ trace.c.entityNum ];
if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
Hide();
static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
}
}
}
}
/*
==============
idPlayer::PerformImpulse
==============
*/
void idPlayer::PerformImpulse( int impulse ) {
if ( gameLocal.isClient ) {
idBitMsg msg;
byte msgBuf[MAX_EVENT_PARAM_SIZE];
assert( entityNumber == gameLocal.localClientNum );
msg.Init( msgBuf, sizeof( msgBuf ) );
msg.BeginWriting();
msg.WriteBits( impulse, 6 );
ClientSendEvent( EVENT_IMPULSE, &msg );
}
//HUMANHEAD rww - changed from IMPULSE_0 to IMPULSE_1.
//IMPULSE_0 for doom3 was fists, but for us it is "no weapon" (not selectable)
if ( impulse >= IMPULSE_1 && impulse <= IMPULSE_12 ) {
SelectWeapon( impulse, false );
return;
}
switch( impulse ) {
case IMPULSE_13: {
Reload();
break;
}
#ifndef HUMANHEAD //HUMANHEAD jsh - handle this in hhPlayer
case IMPULSE_14: {
NextWeapon();
break;
}
case IMPULSE_15: {
PrevWeapon();
break;
}
#endif
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_20: {
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
gameLocal.mpGame.ToggleTeam();
}
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: {
UseVehicle();
break;
}
}
}
bool idPlayer::HandleESC( void ) {
if ( gameLocal.inCinematic ) {
return SkipCinematic();
}
return false;
}
bool idPlayer::SkipCinematic( void ) {
StartSound( "snd_skipcinematic", SND_CHANNEL_ANY, 0, false, NULL );
return gameLocal.SkipCinematic();
}
/*
==============
idPlayer::EvaluateControls
==============
*/
void idPlayer::EvaluateControls( void ) {
// check for respawning
if ( IsDead() ) { // HUMANHEAD cjr: Replaced health <= 0 with IsDead() call for deathwalk override
if ( ( gameLocal.time > minRespawnTime ) &&
( oldButtons & BUTTON_ATTACK ) && // HUMANHEAD pdm: wait until fire is pressed AND released
!( usercmd.buttons & BUTTON_ATTACK ) ) {
forceRespawn = true;
} else if ( gameLocal.time > maxRespawnTime ) {
forceRespawn = true;
}
}
// 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 ) ) {
PerformImpulse( usercmd.impulse );
}
scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
oldFlags = usercmd.flags;
AdjustSpeed();
// update the viewangles
UpdateViewAngles();
}
/*
==============
idPlayer::AdjustSpeed
==============
*/
void idPlayer::AdjustSpeed( void ) {
float speed;
float rate;
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 ) ) {
if ( !gameLocal.isMultiplayer && !physicsObj.IsCrouching() ) { // HUMANHEAD pdm: powerups not used
stamina -= MS2SEC( gameLocal.msec );
}
if ( stamina < 0 ) {
stamina = 0;
}
if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
bobFrac = 1.0f;
} else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
bobFrac = 0.0f;
} else {
bobFrac = stamina / pm_staminathreshold.GetFloat();
}
speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
} else {
rate = pm_staminarate.GetFloat();
// increase 25% faster when not moving
if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( usercmd.upmove == 0 ) ) ) {
rate *= 1.25f;
}
stamina += rate * MS2SEC( gameLocal.msec );
if ( stamina > pm_stamina.GetFloat() ) {
stamina = pm_stamina.GetFloat();
}
speed = pm_walkspeed.GetFloat();
bobFrac = 0.0f;
}
physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
}
/*
==============
idPlayer::AdjustBodyAngles
==============
*/
void idPlayer::AdjustBodyAngles( void ) {
idMat3 lookAxis;
idMat3 legsAxis;
bool blend;
float diff;
float frac;
float upBlend;
float forwardBlend;
float downBlend;
idAngles transAngles; //HUMANHEAD rww
if ( health < 0 ) {
return;
}
//HUMANHEAD rww
if (IsType(hhPlayer::Type)) {
hhPlayer *pl = static_cast<hhPlayer *>(this);
transAngles = pl->GetUntransformedViewAngles();
}
else {
transAngles = viewAngles;
}
//HUMANHEAD END
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( transAngles.yaw - oldViewYaw ); //HUMANHEAD rww
if ( diff < 0.1f ) {
legsYaw = idealLegsYaw;
blend = false;
}
}
if ( !physicsObj.IsCrouching() ) {
legsForward = true;
}
oldViewYaw = transAngles.yaw; //HUMANHEAD rww
AI_TURN_LEFT = false;
AI_TURN_RIGHT = false;
if ( idealLegsYaw > 45.0f ) { // HUMANHEAD pdm: reversed
idealLegsYaw = 0;
AI_TURN_RIGHT = true;
blend = true;
} else if ( idealLegsYaw < -45.0f ) {
idealLegsYaw = 0;
AI_TURN_LEFT = 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 );
// calculate the blending between down, straight, and up
frac = transAngles.pitch / 90.0f; //HUMANHEAD rww
if ( frac > 0.0f ) {
downBlend = frac;
forwardBlend = 1.0f - frac;
upBlend = 0.0f;
} else {
downBlend = 0.0f;
forwardBlend = 1.0f + frac;
upBlend = -frac;
}
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 ) {
/* HUMANHEAD pdm: OVERRIDDEN
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 ) {
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 || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
physicsObj.SetContents( CONTENTS_BODY );
physicsObj.SetMovementType( PM_FREEZE );
} else {
physicsObj.SetContents( CONTENTS_BODY );
physicsObj.SetMovementType( PM_NORMAL );
}
if ( spectating ) {
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 );
RunPhysics();
// update our last valid AAS location for the AI
SetAASLocation();
if ( spectating ) {
newEyeOffset = 0.0f;
} else if ( health <= 0 ) {
newEyeOffset = pm_deadviewheight.GetFloat();
} else if ( physicsObj.IsCrouching() ) {
newEyeOffset = pm_crouchviewheight.GetFloat();
} else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
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 ) ) {
AI_CROUCH = false;
AI_ONGROUND = ( influenceActive == INFLUENCE_LEVEL2 );
AI_ONLADDER = false;
AI_JUMP = false;
} else {
AI_CROUCH = physicsObj.IsCrouching();
AI_ONGROUND = physicsObj.HasGroundContacts();
AI_ONLADDER = physicsObj.OnLadder();
AI_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::Type ) ) {
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_walkspeed.GetFloat();
} else {
// give em a push in the direction they're going
vel *= 1.1f;
}
physicsObj.SetLinearVelocity( vel );
}
}
if ( AI_JUMP ) {
// bounce the view weapon
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;
}
if ( AI_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 );
}
}
BobCycle( pushVelocity );
CrashLand( oldOrigin, oldVelocity );
*/
}
/*
==============
idPlayer::UpdateHud
==============
*/
void idPlayer::UpdateHud( idUserInterface *_hud ) {
/* HUMANHEAD pdm: Overridden
idPlayer *aimed;
if ( !hud ) {
return;
}
if ( entityNumber != gameLocal.localClientNum ) {
return;
}
int c = inventory.pickupItemNames.Num();
if ( c > 0 ) {
if ( gameLocal.time > inventory.nextItemPickup ) {
if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
inventory.nextItemNum = 1;
}
int i;
for ( i = 0; i < 5, i < c; i++ ) {
hud->SetStateString( va( "itemtext%i", inventory.nextItemNum ), inventory.pickupItemNames[0].name );
hud->SetStateString( va( "itemicon%i", inventory.nextItemNum ), inventory.pickupItemNames[0].icon );
hud->HandleNamedEvent( va( "itemPickup%i", inventory.nextItemNum++ ) );
inventory.pickupItemNames.RemoveIndex( 0 );
if (inventory.nextItemNum == 1 ) {
inventory.onePickupTime = gameLocal.time;
} else if ( inventory.nextItemNum > 5 ) {
inventory.nextItemNum = 1;
inventory.nextItemPickup = inventory.onePickupTime + 2000;
} else {
inventory.nextItemPickup = gameLocal.time + 400;
}
}
}
}
if ( gameLocal.realClientTime == lastMPAimTime ) {
if ( MPAim != -1 && gameLocal.gameType == GAME_TDM
&& gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
&& static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
hud->HandleNamedEvent( "aim_flash" );
MPAimHighlight = true;
MPAimFadeTime = 0; // no fade till loosing focus
} else if ( MPAimHighlight ) {
hud->HandleNamedEvent( "aim_fade" );
MPAimFadeTime = gameLocal.realClientTime;
MPAimHighlight = false;
}
}
if ( MPAimFadeTime ) {
assert( !MPAimHighlight );
if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
MPAimFadeTime = 0;
}
}
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 && gameLocal.localClientNum == entityNumber ) {
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 ) {
deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
doingDeathSkin = true;
renderEntity.noShadow = true;
if ( state_hitch ) {
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
} else {
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
}
UpdateVisuals();
}
// wait a bit before switching off the content
if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
SetCombatContents( false );
deathClearContentsTime = 0;
}
} else {
renderEntity.noShadow = (gameLocal.isMultiplayer ? MP_PLAYERNOSHADOW_DEFAULT : false);
renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
UpdateVisuals();
doingDeathSkin = false;
}
}
/*
==============
idPlayer::StartFxOnBone
==============
*/
void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
idVec3 offset;
idMat3 axis;
jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
if ( jointHandle == INVALID_JOINT ) {
gameLocal.Printf( "Cannot find bone %s\n", bone );
return;
}
if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
axis = axis * GetPhysics()->GetAxis();
}
idEntityFx::StartFx( fx, &offset, &axis, this, true );
}
/*
==============
idPlayer::Think
Called every tic for each player
==============
*/
void idPlayer::Think( void ) {
/* HUMANHEAD pdm: Overridden
renderEntity_t *headRenderEnt;
UpdatePlayerIcons();
// latch button actions
oldButtons = usercmd.buttons;
// grab out usercmd
usercmd_t oldCmd = usercmd;
usercmd = gameLocal.usercmds[ entityNumber ];
buttonMask &= usercmd.buttons;
usercmd.buttons &= ~buttonMask;
if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
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 ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
if ( objectiveSystemOpen && AI_PAIN ) {
TogglePDA();
}
usercmd.forwardmove = 0;
usercmd.rightmove = 0;
usercmd.upmove = 0;
}
// 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 );
}
// zooming
if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
} else {
zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
}
}
// 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.GetEntity() ) {
weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
}
EvaluateControls();
if ( !af.IsActive() ) {
AdjustBodyAngles();
CopyJointsFromBodyToHead();
}
Move();
if ( !g_stopTime.GetBool() ) {
if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
TouchTriggers();
}
// not done on clients for various reasons. don't do it on server and save the sound channel for other things
if ( !gameLocal.isMultiplayer ) {
SetCurrentHeartRate();
float scale = g_damageScale.GetFloat();
if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
if ( scale < 1.0f ) {
scale += 0.05f;
}
if ( scale > 1.0f ) {
scale = 1.0f;
}
g_damageScale.SetFloat( scale );
}
}
// update GUIs, Items, and character interactions
UpdateFocus();
UpdateLocation();
// update player script
UpdateScript();
// service animations
if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
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
AI_PAIN = false;
}
// 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();
inventory.UpdateArmor();
if ( spectating ) {
UpdateSpectating();
} else if ( health > 0 ) {
UpdateWeapon();
}
UpdateAir();
UpdateHud();
UpdatePowerUps();
UpdateDeathSkin( false );
if ( gameLocal.isMultiplayer ) {
DrawPlayerIcons();
}
if ( head.GetEntity() ) {
headRenderEnt = head.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
if ( headRenderEnt ) {
if ( influenceSkin ) {
headRenderEnt->customSkin = influenceSkin;
} else {
headRenderEnt->customSkin = NULL;
}
}
if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
renderEntity.suppressShadowInViewID = 0;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = 0;
}
} else {
renderEntity.suppressShadowInViewID = entityNumber+1;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = entityNumber+1;
}
}
// never cast shadows from our first-person muzzle flashes
renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
}
if ( !g_stopTime.GetBool() ) {
UpdateAnimation();
Present();
UpdateDamageEffects();
LinkCombat();
playerView.CalculateShake();
}
if ( !( thinkFlags & TH_THINK ) ) {
gameLocal.Printf( "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() ) {
gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
num++;
}
gameLocal.Printf( "%d: enemies\n", num );
}
*/
}
/*
=================
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;
}
}
/*
==================
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 (noclip) { // HUMANHEAD pdm: Because of deathwalk, this shouldn't be allowed
return;
}
if ( spectating ) {
SpectateFreeFly( false );
} else if ( health > 0 ) {
godmode = 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 ) {
/* HUMANHEAD pdm: Overridden
*/
}
/*
=====================
idPlayer::GetAIAimTargets
Returns positions for the AI to aim at.
=====================
*/
void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
idVec3 offset;
idMat3 axis;
idVec3 origin;
origin = lastSightPos - physicsObj.GetOrigin();
GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
headPos = offset + origin;
GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
chestPos = offset + origin;
}
/*
================
idPlayer::DamageFeedback
callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
================
*/
void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
assert( !gameLocal.isClient );
if ( damage && ( victim != this ) && victim->IsType( idActor::Type ) ) {
SetLastHitTime( gameLocal.time );
}
}
/*
=================
idPlayer::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;
damageDef->GetInt( "damage", "20", damage );
if( !damageDef->GetInt( "noscaling" ) ) { //HUMANHEAD bjk
damage = GetDamageForLocation( damage, location );
} //HUMANHEAD END
idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
/* HUMANHEAD CJR: Commented out for now, so the skill levels don't influcent our basic damage testing
we can base our DDA system upon this code, though
if ( !gameLocal.isMultiplayer ) {
if ( inflictor != gameLocal.world ) {
switch ( g_skill.GetInteger() ) {
case 0:
damage *= 0.80f;
if ( damage < 1 ) {
damage = 1;
}
break;
case 2:
damage *= 1.70f;
break;
case 3:
damage *= 3.5f;
break;
default:
break;
}
}
}
*/
int originalDamage = damage; // HUMANHEAD CJR: Ensuring that damageScale doesn't scale to zero
damage *= damageScale;
if ( damage == 0 && originalDamage != 0 ) { // HUMANHEAD CJR: Don't let damage scale to zero (unless the original damage was zero)
damage = 1;
} // HUMANHEAD END
// always give half damage if hurting self
if ( attacker == this ) {
//HUMANHEAD rww - no.
/*
if ( gameLocal.isMultiplayer ) {
// only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
} else {
damage *= damageDef->GetFloat( "selfDamageScale", "1" );
}
*/
damage *= damageDef->GetFloat( "selfDamageScale", "1" );
//HUMANHEAD END
}
// check for completely getting out of the damage
if ( !damageDef->GetBool( "noGod" ) ) {
// check for godmode
if ( godmode ) {
damage = 0;
}
}
// inform the attacker that they hit someone
attacker->DamageFeedback( this, inflictor, damage );
#if HUMANHEAD // HUMANHEAD CJR: No armor in Prey
armorSave = 0;
#else /*
// 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;
}
*/
#endif // HUMANHEAD END
// check for team damage
if ( gameLocal.gameType == GAME_TDM
&& !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, const int location ) {
idVec3 kick;
int damage;
int armorSave;
int knockback;
idVec3 damage_from;
idVec3 localDamageVector;
float attackerPushScale;
// damage is only processed on server
if ( gameLocal.isClient ) {
return;
}
if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
return;
}
if ( !inflictor ) {
inflictor = gameLocal.world;
}
if ( !attacker ) {
attacker = gameLocal.world;
}
const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
if ( !damageDef ) {
gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
return;
}
if ( damageDef->dict.GetBool( "ignore_player" ) ) {
return;
}
CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
// determine knockback
damageDef->dict.GetInt( "knockback", "20", knockback );
//HUMANHEAD: aob - added surface type check
if( physicsObj.GetGroundMatterType() == SURFTYPE_WALLWALK ) { // no more knockback when dead
knockback = 0;
}
// HUMANHEAD END
if ( knockback != 0 && !fl.noknockback ) {
if ( attacker == this ) {
damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
} else {
attackerPushScale = 1.0f;
}
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 ) );
}
/* HUMANHEAD aob: we have no armour
// give feedback on the player view and audibly when armor is helping
if ( armorSave ) {
inventory.armor -= armorSave;
if ( gameLocal.time > lastArmorPulse + 200 ) {
StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
}
lastArmorPulse = gameLocal.time;
}
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 );
}
}
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
damage_from = dir;
damage_from.Normalize();
viewAxis.ProjectVector( damage_from, localDamageVector );
// add to the damage inflicted on a player this frame
// the total will be turned into screen blends and view angle kicks
// at the end of the frame
if ( health > 0 ) {
playerView.DamageImpulse( localDamageVector, &damageDef->dict );
}
// do the damage
if ( damage > 0 ) {
/* HUMANHEAD CJR: Commented out their dynamic health scaling for testing purposes.
// We can possibly base our system upon this, though
if ( !gameLocal.isMultiplayer ) {
float scale = g_damageScale.GetFloat();
if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
scale -= 0.05f;
g_damageScale.SetFloat( scale );
}
}
if ( scale > 0.0f ) {
damage *= scale;
}
}
*/
if ( damage < 1 ) {
damage = 1;
}
int oldHealth = health;
health -= damage;
//HUMANHEAD rww - don't die in the shuttle, or while in dialog mode
if (bDialogDamageMode || (gameLocal.isMultiplayer && InVehicle())) {
if (health <= 0) {
health = 1;
}
}
//HUMANHEAD END
if ( health <= 0 ) {
if ( health < -999 ) {
health = -999;
}
isTelefragged = damageDef->dict.GetBool( "telefrag" );
lastDmgTime = gameLocal.time;
Killed( inflictor, attacker, damage, dir, location );
} else {
// force a blink
blink_time = 0;
// let the anim script know we took damage
AI_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 );
}
}
lastDamageDef = damageDef->Index();
lastDamageDir = damage_from;
lastDamageLocation = location;
}
/*
===========
idPlayer::Teleport
============
*/
void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
/* HUMANHEAD cjr: overridden
if ( !gameLocal.isClient && !noclip ) {
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, bool noHide ) {
privateCameraView = camView;
//CancelEvents(&EV_SetOverlayMaterial); //HUMANHEAD rww
//playerView.SetViewOverlayMaterial( NULL ); bjk: removed and is handled by scripters
if ( camView ) {
StopFiring();
if (!noHide) {
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 > 110.0f ) {
return 110.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 + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
}
if ( influenceFov ) {
return influenceFov;
}
if ( zoomFov.IsDone( gameLocal.time ) ) {
fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->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;
}
if (bBufferNextSnapAngles) { //HUMANHEAD rww - teleported
return a;
}
//HUMANHEAD rww - for other clients, do not add gun offset, it isn't predicted well
if (gameLocal.isMultiplayer && !gameLocal.isServer && gameLocal.localClientNum != -1 && gameLocal.localClientNum != entityNumber) {
return a;
}
//HUMANHEAD END
idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
idAngles av, base;
int weaponAngleOffsetAverages;
float weaponAngleOffsetScale, weaponAngleOffsetMax;
#ifdef HUMANHEAD //jsh Since we constantly respawn our weapons, there's a better chance the entity will be invalid
if( weapon.IsValid() ) {
weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
} else {
return a;
}
#else
weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
#endif
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, weaponOffsetScale;
ofs.Zero();
#ifdef HUMANHEAD //jsh Since we constantly respawn our weapons, there's a better chance the entity will be invalid
if( weapon.IsValid() ) {
weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
} else {
return ofs;
}
#else
weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
#endif
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 = ( 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;
// 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() );
// 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 ( bobCycle & 128 ) {
scale = -xyspeed;
} else {
scale = xyspeed;
}
// gun angles from bobbing
angles.roll = scale * bobfracsin * 0.005f;
angles.yaw = scale * bobfracsin * 0.01f;
angles.pitch = xyspeed * bobfracsin * 0.005f;
// gun angles from turning
if ( gameLocal.isMultiplayer ) {
idAngles offset = GunTurningOffset();
offset *= g_mpWeaponAngleScale.GetFloat();
angles += offset;
} else {
angles += GunTurningOffset();
}
idVec3 gravity = physicsObj.GetGravityNormal();
// drop the weapon when landing after a jump / fall
delta = gameLocal.time - landTime;
if ( delta < LAND_DEFLECT_TIME ) {
origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
} else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
}
// speed sensitive idle drift
scale = xyspeed + 40.0f;
fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
angles.roll += fracsin;
angles.yaw += fracsin;
angles.pitch += fracsin;
axis = angles.ToMat3() * viewAxis;
}
/*
===============
idPlayer::OffsetThirdPersonView
===============
*/
void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
/* HUMANHEAD pdm: Overridden
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;
view.z += 8 + height;
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 ) );
gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
if ( trace.fraction != 1.0f ) {
view = trace.endpos;
view.z += ( 1.0f - trace.fraction ) * 32.0f;
// try another trace to this position, because a tunnel may have the ceiling
// close enough that this is poking out
gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
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( atan2( 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;
// use the smoothed origin if spectating another player in multiplayer
if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
org = smoothedOrigin;
} else {
org = GetPhysics()->GetOrigin();
}
return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
}
/*
===============
idPlayer::GetViewPos
===============
*/
void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) {
idAngles angles;
// if dead, fix the angle and don't add any kick
// HUMANHEAD cjr: Replaced health <= 0 with IsDead() call for deathwalk override
if ( IsDead() ) {
// HUMANHEAD END
angles.yaw = viewAngles.yaw;
angles.roll = 40;
angles.pitch = -15;
axis = angles.ToMat3();
origin = GetEyePosition();
} else {
origin = GetEyePosition() + viewBob;
angles = viewAngles + viewBobAngles + playerView.AngleOffset(0, 0);
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 ) && IsDead() ) ) { // HUMANHEAD cjr: Replaced health <= 0 with IsDead() call for deathwalk override
// 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(0, 0);
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 );
#if 0
// shakefrom sound stuff only happens in first person
firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
#endif
}
}
/*
==================
idPlayer::GetRenderView
Returns the renderView that was calculated for this tic
==================
*/
renderView_t *idPlayer::GetRenderView( void ) {
return renderView;
}
/*
==================
idPlayer::CalculateRenderView
create the renderView for the current tic
==================
*/
void idPlayer::CalculateRenderView( void ) {
int i;
float range;
if ( !renderView ) {
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 {
if ( g_stopTime.GetBool() ) {
renderView->vieworg = firstPersonViewOrigin;
renderView->viewaxis = firstPersonViewAxis;
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() ) {
OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
} else if ( IsDead() && DoThirdPersonDeath() ) { //HUMANHEAD rww - added IsDead() check, changed check to use util function
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 );
} else {
renderView->vieworg = firstPersonViewOrigin;
renderView->viewaxis = firstPersonViewAxis;
// 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::AddAIKill
=============
*/
void idPlayer::AddAIKill( void ) {
// HUMANHEAD pdm: not used
}
/*
=============
idPlayer::AddProjectilesFired
=============
*/
void idPlayer::AddProjectilesFired( int count ) {
numProjectilesFired += count;
}
/*
=============
idPlayer::AddProjectileHites
=============
*/
void idPlayer::AddProjectileHits( int count ) {
numProjectileHits += count;
}
/*
=============
idPlayer::SetLastHitTime
=============
*/
void idPlayer::SetLastHitTime( int time ) {
idPlayer *aimed = NULL;
if ( time && lastHitTime != time ) {
lastHitToggle ^= 1;
}
lastHitTime = time;
if ( !time ) {
// level start and inits
return;
}
if ( gameLocal.isMultiplayer && ( time - lastSndHitTime ) > 10 ) {
lastSndHitTime = time;
StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
}
if ( cursor ) {
cursor->HandleNamedEvent( "hitTime" );
}
#if !HUMANHEAD // HUMANHEAD pdm
if ( hud ) {
if ( MPAim != -1 ) {
if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
}
assert( aimed );
// full highlight, no fade till loosing aim
hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
if ( aimed ) {
hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
}
hud->HandleNamedEvent( "aim_flash" );
MPAimHighlight = true;
MPAimFadeTime = 0;
} else if ( lastMPAim != -1 ) {
if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
}
assert( aimed );
// start fading right away
hud->SetStateString( "aim_text", gameLocal.userInfo[ lastMPAim ].GetString( "ui_name" ) );
if ( aimed ) {
hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
}
hud->HandleNamedEvent( "aim_flash" );
hud->HandleNamedEvent( "aim_fade" );
MPAimHighlight = false;
MPAimFadeTime = gameLocal.realClientTime;
}
}
#endif // HUMANHEAD END
}
/*
================
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_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 ) {
hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
weaponEnabled = true;
if ( weapon.GetEntity() ) {
weapon.GetEntity()->ExitCinematic();
}
}
/*
==================
idPlayer::Event_DisableWeapon
==================
*/
void idPlayer::Event_DisableWeapon( void ) {
hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
weaponEnabled = false;
if ( weapon.GetEntity() ) {
weapon.GetEntity()->EnterCinematic();
}
}
/*
==================
idPlayer::Event_GetCurrentWeapon
==================
*/
void idPlayer::Event_GetCurrentWeapon( void ) {
const char *weapon;
if ( currentWeapon >= 0 ) {
weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
idThread::ReturnString( weapon );
} else {
idThread::ReturnString( "" );
}
}
/*
==================
idPlayer::Event_GetPreviousWeapon
==================
*/
void idPlayer::Event_GetPreviousWeapon( void ) {
const char *weapon;
if ( previousWeapon >= 0 ) {
int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
idThread::ReturnString( weapon );
} else {
idThread::ReturnString( spawnArgs.GetString( "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;
}
if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
idealWeapon = weapon_fists;
weapon.GetEntity()->HideWeapon();
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 ) ) {
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_GetWeaponEntity
==================
*/
void idPlayer::Event_GetWeaponEntity( void ) {
idThread::ReturnEntity( weapon.GetEntity() );
}
/*
==================
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.GetEntity();
if ( !exitEnt ) {
common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
return;
}
pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
if ( gameLocal.isServer ) {
ServerSendEvent( 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();
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::ClientPredictionThink
================
*/
void idPlayer::ClientPredictionThink( void ) {
renderEntity_t *headRenderEnt;
oldFlags = usercmd.flags;
oldButtons = usercmd.buttons;
usercmd = gameLocal.usercmds[ entityNumber ];
//HUMANHEAD rww - "no good for predictions"? i have never seen evidence to support that, and it is necessary for predicted
//projectiles, so i am leaving this id code out.
/*
if ( entityNumber != gameLocal.localClientNum ) {
// ignore attack button of other clients. that's no good for predictions
usercmd.buttons &= ~BUTTON_ATTACK;
}
*/
//HUMANHEAD END
buttonMask &= usercmd.buttons;
usercmd.buttons &= ~buttonMask;
// 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 );
}
}
scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
AdjustSpeed();
UpdateViewAngles();
//HUMANHEAD rww - moved id angle smoothing code into UpdateViewAngles
if ( !af.IsActive() ) {
AdjustBodyAngles();
}
//HUMANHEAD rww - not while in a vehicle
if ( !isLagged && !InVehicle() ) {
//HUMANHEAD END
// don't allow client to move when lagged
Move();
}
// 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
AI_PAIN = false;
// 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 ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
UpdateWeapon();
}
UpdateDeathSkin( false );
if ( head.GetEntity() ) {
headRenderEnt = head.GetEntity()->GetRenderEntity();
} else {
headRenderEnt = NULL;
}
if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
renderEntity.suppressShadowInViewID = 0;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = 0;
}
} else {
renderEntity.suppressShadowInViewID = entityNumber+1;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInViewID = entityNumber+1;
}
}
// never cast shadows from our first-person muzzle flashes
renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
if ( headRenderEnt ) {
headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
}
if ( !gameLocal.inCinematic ) {
UpdateAnimation();
}
if ( gameLocal.isMultiplayer ) {
DrawPlayerIcons();
}
Present();
UpdateDamageEffects();
if (gameLocal.isNewFrame) {
UpdateWounds(); //HUMANHEAD rww
}
LinkCombat();
if ( gameLocal.isNewFrame && entityNumber == gameLocal.localClientNum ) {
playerView.CalculateShake();
}
}
/*
================
idPlayer::GetPhysicsToVisualTransform
================
*/
bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if ( af.IsActive() ) {
af.GetPhysicsToVisualTransform( origin, axis );
return true;
}
// smoothen the rendered origin and angles of other clients
// smooth self origin if snapshots are telling us prediction is off
if ( gameLocal.isClient && gameLocal.framenum >= smoothedFrame && ( entityNumber != gameLocal.localClientNum || selfSmooth ) ) {
// render origin and axis
idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
// update the smoothed origin
if ( !smoothedOriginUpdated ) {
idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
// smoothen by pushing back to the previous position
if ( selfSmooth ) {
assert( entityNumber == gameLocal.localClientNum );
renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
} else {
renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
}
}
smoothedOrigin = renderOrigin;
smoothedFrame = gameLocal.framenum;
smoothedOriginUpdated = true;
}
axis = idAngles( 0.0f, smoothedAngles.yaw, 0.0f ).ToMat3();
origin = ( smoothedOrigin - 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 {
physicsObj.WriteToSnapshot( msg, false ); //HUMANHEAD rww - vehicle param
WriteBindToSnapshot( msg );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
msg.WriteShort( health );
msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, 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( weapon.GetSpawnId(), 32 );
msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
msg.WriteBits( lastHitToggle, 1 );
msg.WriteBits( weaponGone, 1 );
msg.WriteBits( isLagged, 1 );
msg.WriteBits( isChatting, 1 );
}
/*
================
idPlayer::ReadFromSnapshot
================
*/
void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
int i, oldHealth, newIdealWeapon, weaponSpawnId;
bool newHitToggle, stateHitch;
if ( snapshotSequence - lastSnapshotSequence > 1 ) {
stateHitch = true;
} else {
stateHitch = false;
}
lastSnapshotSequence = snapshotSequence;
oldHealth = health;
physicsObj.ReadFromSnapshot( msg, false ); //HUMANHEAD rww - vehicle param
ReadBindFromSnapshot( msg );
deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
health = msg.ReadShort();
lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, 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 );
spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
newHitToggle = msg.ReadBits( 1 ) != 0;
weaponGone = msg.ReadBits( 1 ) != 0;
isLagged = msg.ReadBits( 1 ) != 0;
isChatting = msg.ReadBits( 1 ) != 0;
// no msg reading below this
if ( weapon.SetSpawnId( weaponSpawnId ) ) {
//HUMANHEAD rww - i am getting crashes here and the stack is all messed up,
//claiming that SetOwner is off into null, when supposedly the entity is not.
//rearranging this code so it's easier to see what's going on in a crash.
hhWeapon *weapEnt = weapon.GetEntity();
if ( weapEnt ) {
// maintain ownership locally
weapEnt->SetOwner( this );
}
currentWeapon = -1;
}
// if not a local client assume the client has all ammo types
if ( entityNumber != gameLocal.localClientNum ) {
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
inventory.ammo[ i ] = 999;
}
}
if ( oldHealth > 0 && health <= 0 ) {
if ( stateHitch ) {
// so we just hide and don't show a death skin
UpdateDeathSkin( true );
}
// die
AI_DEAD = true;
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
SetWaitState( "" );
animator.ClearAllJoints();
//HUMANHEAD rww - don't want this
/*
if ( entityNumber == gameLocal.localClientNum ) {
playerView.Fade( colorBlack, 12000 );
}
*/
//HUMANHEAD END
StartRagdoll();
physicsObj.SetMovementType( PM_DEAD );
if ( !stateHitch ) {
//HUMANHEAD PCF rww 09/15/06 - female mp sounds
if (IsFemale()) {
StartSound( "snd_death_female", SND_CHANNEL_VOICE, 0, false, NULL );
}
else {
//HUMANHEAD END
StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
}
}
if ( weapon.GetEntity() ) {
weapon.GetEntity()->OwnerDied();
}
//HUMANHEAD rww
GetPhysics()->SetContents(0);
Hide();
//so camera can operate on client.
minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
maxRespawnTime = minRespawnTime + 10000;
//HUMANHEAD END
} else if ( oldHealth <= 0 && health > 0 ) {
// respawn
Init();
StopRagdoll();
SetPhysics( &physicsObj );
physicsObj.EnableClip();
SetCombatContents( true );
//HUMANHEAD rww
if (!spectating) {
Show();
}
//HUMANHEAD END
} else if ( health < oldHealth && 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 ) {
playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
AI_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 the player is alive, restore proper physics object
if ( health > 0 && IsActiveAF() ) {
StopRagdoll();
SetPhysics( &physicsObj );
physicsObj.EnableClip();
SetCombatContents( true );
}
if ( idealWeapon != newIdealWeapon ) {
if ( stateHitch ) {
weaponCatchup = true;
}
idealWeapon = newIdealWeapon;
UpdateHudWeapon();
}
if ( lastHitToggle != newHitToggle ) {
SetLastHitTime( gameLocal.realClientTime );
}
if ( msg.HasChanged() ) {
UpdateVisuals();
}
}
/*
================
idPlayer::WritePlayerStateToSnapshot
================
*/
void idPlayer::WritePlayerStateToSnapshot( idBitMsgDelta &msg ) const {
int i;
msg.WriteByte( bobCycle );
msg.WriteLong( stepUpTime );
msg.WriteFloat( stepUpDelta );
msg.WriteShort( inventory.weapons );
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
}
//HUMANHEAD rww - don't need the clip count for all weapons on the client, and weapon handles sending its own clip
/*
for( i = 0; i < MAX_WEAPONS; i++ ) {
msg.WriteBits( inventory.clip[i], ASYNC_PLAYER_INV_CLIP_BITS );
}
*/
//HUMANHEAD END
}
/*
================
idPlayer::ReadPlayerStateFromSnapshot
================
*/
void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
int i, ammo;
bobCycle = msg.ReadByte();
stepUpTime = msg.ReadLong();
stepUpDelta = msg.ReadFloat();
inventory.weapons = msg.ReadShort();
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
if ( gameLocal.time >= inventory.ammoPredictTime ) {
inventory.ammo[ i ] = ammo;
}
}
//HUMANHEAD rww - don't need the clip count for all weapons on the client, and weapon handles sending its own clip
/*
for( i = 0; i < MAX_WEAPONS; i++ ) {
inventory.clip[i] = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
}
*/
//HUMANHEAD END
}
//HUMANHEAD rww
bool idPlayer::DoThirdPersonDeath(void) {
if (gameLocal.isMultiplayer) {
return pm_thirdPersonDeathMP.GetBool();
}
return pm_thirdPersonDeath.GetBool();
}
//HUMANHEAD END
/*
================
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: {
PerformImpulse( msg.ReadBits( 6 ) );
return true;
}
default: {
return false;
}
}
}
/*
================
idPlayer::ClientReceiveEvent
================
*/
bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
switch ( event ) {
case EVENT_EXIT_TELEPORTER:
Event_ExitTeleporter();
return true;
case EVENT_ABORT_TELEPORTER:
SetPrivateCameraView( NULL );
return true;
case EVENT_SPECTATE: {
bool spectate = ( msg.ReadBits( 1 ) != 0 );
Spectate( spectate );
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 );
}
//HUMANHEAD rww
case EVENT_MENUEVENT: { //a bit crude, handle menu events for pickups that don't get predicted. (allow for multiple in 1 event)
assert(entityNumber == gameLocal.localClientNum);
int menuGetEvents = msg.ReadBits(MENU_NET_EVENT_NUM);
if (hud) {
if (menuGetEvents & (1<<MENU_NET_EVENT_INVPICKUP)) {
int itemSpawnId = msg.ReadBits(32);
idEntityPtr<idEntity> ent;
ent.SetSpawnId(itemSpawnId);
if (hud && ent.IsValid() && ent->IsType(idItem::Type)) { //if the item still exists on the client, do the menu event.
idItemInfo info;
const char* itemName = ent->spawnArgs.GetString( "inv_name" );
if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
info.name = common->GetLanguageDict()->GetString( itemName );
} else {
info.name = itemName;
}
info.icon = ent->spawnArgs.GetString( "inv_icon" );
info.bDoubleWide = ent->spawnArgs.FindKey("inv_weapon") != NULL;
inventory.AddPickupName(info.name, info.icon, info.bDoubleWide);
hud->SetStateString( "itemicon", info.icon );
hud->HandleNamedEvent( "invPickup" );
}
}
if (menuGetEvents & (1<<MENU_NET_EVENT_HEALTHPULSE)) {
hud->HandleNamedEvent( "healthPulse" );
StartSound( "snd_healthpulse", SND_CHANNEL_ITEM, 0, false, NULL );
}
if (menuGetEvents & (1<<MENU_NET_EVENT_MAXHEALTHPULSE)) {
hud->HandleNamedEvent( "maxHealthPulse" );
}
if (menuGetEvents & (1<<MENU_NET_EVENT_SPIRITPULSE)) {
hud->HandleNamedEvent( "spiritPulse" );
StartSound( "snd_spiritpulse", SND_CHANNEL_ITEM, 0, false, NULL );
}
if (menuGetEvents & (1<<MENU_NET_EVENT_AMMOPULSE)) {
hud->HandleNamedEvent( "ammoPulse" );
}
if (menuGetEvents & (1<<MENU_NET_EVENT_WEAPONPULSE)) {
UpdateHudWeapon();
hud->HandleNamedEvent( "weaponPulse" );
}
}
return true;
}
//HUMANHEAD END
//HUMANHEAD rww
case EVENT_HITNOTIFICATION: {
int hitSpawnId = msg.ReadBits(32);
idEntityPtr<idEntity> hit;
hit.SetSpawnId(hitSpawnId);
if (hit.IsValid() && hit->IsType(hhPlayer::Type)) { //set the shaderparms locally if it's still valid
hhPlayer *playerHit = static_cast<hhPlayer *>(hit.GetEntity());
//don't provide visual indicator when shooting a teammate
if (gameLocal.gameType == GAME_TDM && gameLocal.GetLocalPlayer() && playerHit->team == gameLocal.GetLocalPlayer()->team) {
StartSound( "snd_hitTeamFeedback", SND_CHANNEL_ITEM, 0, false, NULL );
}
else {
//hardcoded health ranges for the various flash colors
float h;
if (playerHit->health > 100) {
h = 0.0f;
}
else if (playerHit->health > 75) {
h = 0.25f;
}
else if (playerHit->health > 25) {
h = 0.50f;
}
else {
h = 0.75f;
}
playerHit->SetShaderParm(3, h);
playerHit->SetShaderParm(5, -MS2SEC(gameLocal.time)*2);
if (playerHit->IsSpiritOrDeathwalking()) {
StartSound( "snd_hitSpiritFeedback", SND_CHANNEL_ITEM, 0, false, NULL );
}
else {
StartSound( "snd_hitFeedback", SND_CHANNEL_ITEM, 0, false, NULL );
}
}
}
return true;
}
//HUMANHEAD END
//HUMANHEAD rww
case EVENT_PORTALLED: {
int portalSpawnId = msg.ReadBits(32);
idEntityPtr<idEntity> portal;
portal.SetSpawnId(portalSpawnId);
if (portal.IsValid() && portal->IsType(hhPortal::Type)) {
assert(IsType(hhPlayer::Type)); //we really should consolidate hhPlayer and idPlayer one of these days.
hhPlayer *hhSelf = static_cast<hhPlayer *>(this);
hhSelf->Portalled(portal.GetEntity());
}
return true;
}
//HUMANHEAD END
//HUMANHEAD rww
case EVENT_REPORTATTACK: {
idEntityPtr<idEntity> attacker;
attacker.SetSpawnId(msg.ReadBits(32));
if (attacker.IsValid()) { //if attacker is valid on client, report the attack
assert(IsType(hhPlayer::Type)); //we really should consolidate hhPlayer and idPlayer one of these days.
hhPlayer *hhSelf = static_cast<hhPlayer *>(this);
hhSelf->ReportAttack(attacker.GetEntity());
}
return true;
}
//HUMANHEAD END
default: {
return idActor::ClientReceiveEvent( event, time, msg );
}
}
return false;
}
/*
================
idPlayer::Hide
================
*/
void idPlayer::Hide( void ) {
hhWeapon *weap; // Changed from idWeapon to hhWeapon
idActor::Hide();
weap = weapon.GetEntity();
if ( weap ) {
weap->HideWorldModel();
}
}
/*
================
idPlayer::Show
================
*/
void idPlayer::Show( void ) {
#if 0 // HUMANHEAD mdl: Overridden in hhPlayer
hhWeapon *weap; // Changed from idWeapon to hhWeapon
idActor::Show();
weap = weapon.GetEntity();
if ( weap ) {
weap->ShowWorldModel();
}
#endif // HUMANHEAD END
}
/*
===============
idPlayer::ShowTip
HUMANHEAD pdm: rewritten
===============
*/
void idPlayer::ShowTip( const char *keyMaterial, const char *tip, const char *key, const char *topMaterial, bool keywide, int xOffset, int yOffset ) {
if ( tipUp ) {
return;
}
hud->SetStateBool( "keywide", keywide );
hud->SetStateString( "tip", tip ? tip : "" );
hud->SetStateString( "key", key ? key : "" );
hud->SetStateString( "keyMaterial", keyMaterial ? keyMaterial : "" );
hud->SetStateString( "topMaterial", topMaterial ? topMaterial : "" );
hud->SetStateInt( "tipOffsetX", xOffset);
hud->SetStateInt( "tipOffsetY", yOffset);
hud->HandleNamedEvent( "tipWindowUp" );
tipUp = true;
}
/*
===============
idPlayer::HideTip
HUMANHEAD pdm: rewritten
===============
*/
void idPlayer::HideTip( void ) {
hud->HandleNamedEvent( "tipWindowDown" );
tipUp = false;
}
/*
===============
idPlayer::Event_HideTip
===============
*/
void idPlayer::Event_HideTip( void ) {
HideTip();
}
/*
===============
idPlayer::SetSpectateOrigin
===============
*/
void idPlayer::SetSpectateOrigin( void ) {
idVec3 neworig;
neworig = GetPhysics()->GetOrigin();
//HUMANHEAD rww - don't artificially boost the z anymore
//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::Event_Gibbed
===============
*/
void idPlayer::Event_Gibbed( void ) {
}
/*
===============
idPlayer::UpdatePlayerIcons
===============
*/
void idPlayer::UpdatePlayerIcons( void ) {
//HUMANHEAD rww
if (IsType(hhArtificialPlayer::Type)) {
isLagged = false;
}
else {
//HUMANHEAD END
int time = networkSystem->ServerGetClientTimeSinceLastPacket( entityNumber );
if ( time > cvarSystem->GetCVarInteger( "net_clientMaxPrediction" ) ) {
isLagged = true;
} else {
isLagged = false;
}
}
}
/*
===============
idPlayer::DrawPlayerIcons
===============
*/
void idPlayer::DrawPlayerIcons( void ) {
if ( !NeedsIcon() ) {
playerIcon.FreeIcon();
playerTeamIcon.FreeIcon(); //HUMANHEAD rww
return;
}
playerIcon.Draw( this, headJoint );
playerTeamIcon.Draw( this, headJoint ); //HUMANHEAD rww
}
/*
===============
idPlayer::HidePlayerIcons
===============
*/
void idPlayer::HidePlayerIcons( void ) {
playerIcon.FreeIcon();
playerTeamIcon.FreeIcon(); //HUMANHEAD rww
}
/*
===============
idPlayer::NeedsIcon
==============
*/
bool idPlayer::NeedsIcon( void ) {
// local clients don't render their own icons... they're only info for other clients
return !spectating && entityNumber != gameLocal.localClientNum && ( isLagged || isChatting || (gameLocal.gameType == GAME_TDM && health > 0) ) && gameLocal.EntInClientSnapshot(entityNumber); //HUMANHEAD rww - added gametype condition (and snapshot condition (and some other conditions))
}
idVec4 idPlayer::GetTeamColor() {
int index = 0;
idVec4 v;
if (gameLocal.gameType == GAME_TDM) {
index = (team == 0) ? 1 : 3; // team0==red, team1==blue (table: red=1, blue=3, yellow=4)
}
else {
index = 4; // Yellow
}
idVec3 v3 = idPlayer::colorBarTable[index];
v.x = v3.x;
v.y = v3.y;
v.z = v3.z;
v.w = 1.0f;
return v;
}