mirror of
https://github.com/DrBeef/Doom3Quest.git
synced 2025-02-25 05:10:51 +00:00
- Remove dodgy weapon angle adjust (not needed as there is already implementation in there to adjust) - Added method to get frame number from the engine
17153 lines
509 KiB
C++
17153 lines
509 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/LangDict.h"
|
|
#include "framework/async/NetworkSystem.h"
|
|
#include "framework/DeclEntityDef.h"
|
|
#include "renderer/RenderSystem.h"
|
|
#include "renderer/ModelManager.h"
|
|
|
|
#include "gamesys/SysCvar.h"
|
|
#include "script/Script_Thread.h"
|
|
#include "ai/AI.h"
|
|
#include "WorldSpawn.h"
|
|
#include "Player.h"
|
|
#include "../idlib/geometry/JointTransform.h"
|
|
#include "Camera.h"
|
|
#include "Fx.h"
|
|
#include "Misc.h"
|
|
#include "Vr.h"
|
|
#include "idlib/Lib.h"
|
|
#include "Mover.h"
|
|
#include "sys/sys_public.h"
|
|
|
|
|
|
const int ASYNC_PLAYER_INV_AMMO_BITS = idMath::BitsForInteger( 999 ); // 9 bits to cover the range [0, 999]
|
|
const int ASYNC_PLAYER_INV_CLIP_BITS = -7; // -7 bits to cover the range [-1, 60]
|
|
|
|
//#define ANGLE2SHORT(x) ( idMath::Ftoi( (x) * 65536.0f / 360.0f ) & 65535 )
|
|
|
|
idCVar flashlight_batteryDrainTimeMS( "flashlight_batteryDrainTimeMS", "30000", CVAR_INTEGER, "amount of time (in MS) it takes for full battery to drain (-1 == no battery drain)" );
|
|
idCVar flashlight_batteryChargeTimeMS( "flashlight_batteryChargeTimeMS", "3000", CVAR_INTEGER, "amount of time (in MS) it takes to fully recharge battery" );
|
|
idCVar flashlight_minActivatePercent( "flashlight_minActivatePercent", ".25", CVAR_FLOAT, "( 0.0 - 1.0 ) minimum amount of battery (%) needed to turn on flashlight" );
|
|
idCVar flashlight_batteryFlickerPercent( "flashlight_batteryFlickerPercent", ".1", CVAR_FLOAT, "chance of flickering when battery is low" );
|
|
|
|
// Client-authoritative stuff
|
|
idCVar pm_clientAuthoritative_debug( "pm_clientAuthoritative_debug", "0", CVAR_BOOL, "" );
|
|
idCVar pm_controllerShake_damageMaxMag( "pm_controllerShake_damageMaxMag", "60.0f", CVAR_FLOAT, "" );
|
|
idCVar pm_controllerShake_damageMaxDur( "pm_controllerShake_damageMaxDur", "60.0f", CVAR_FLOAT, "" );
|
|
|
|
idCVar pm_clientAuthoritative_warnDist( "pm_clientAuthoritative_warnDist", "100.0f", CVAR_FLOAT, "" );
|
|
idCVar pm_clientAuthoritative_minDistZ( "pm_clientAuthoritative_minDistZ", "1.0f", CVAR_FLOAT, "" );
|
|
idCVar pm_clientAuthoritative_minDist( "pm_clientAuthoritative_minDist", "-1.0f", CVAR_FLOAT, "" );
|
|
idCVar pm_clientAuthoritative_Lerp( "pm_clientAuthoritative_Lerp", "0.9f", CVAR_FLOAT, "" );
|
|
|
|
idCVar pm_clientAuthoritative_Divergence( "pm_clientAuthoritative_Divergence", "200.0f", CVAR_FLOAT, "" );
|
|
idCVar pm_clientInterpolation_Divergence( "pm_clientInterpolation_Divergence", "5000.0f", CVAR_FLOAT, "" );
|
|
|
|
idCVar pm_clientAuthoritative_minSpeedSquared( "pm_clientAuthoritative_minSpeedSquared", "1000.0f", CVAR_FLOAT, "" );
|
|
|
|
idCVar vr_wipScale( "vr_wipScale", "1.0", CVAR_FLOAT | CVAR_ARCHIVE, "" );
|
|
|
|
idCVar vr_debugGui( "vr_debugGui", "1", CVAR_BOOL, "" );
|
|
idCVar vr_guiFocusPitchAdj( "vr_guiFocusPitchAdj", "7", CVAR_FLOAT | CVAR_ARCHIVE, "View pitch adjust to help activate in game touch screens" );
|
|
|
|
idCVar vr_bx1( "vr_bx1", "5", CVAR_FLOAT, "");
|
|
idCVar vr_bx2( "vr_bx2", "5", CVAR_FLOAT, "" );
|
|
idCVar vr_by1( "vr_by1", "1", CVAR_FLOAT, "" );
|
|
idCVar vr_by2( "vr_by2", "0", CVAR_FLOAT, "" );
|
|
idCVar vr_bz1( "vr_bz1", "0", CVAR_FLOAT, "" );
|
|
idCVar vr_bz2( "vr_bz2", "0", CVAR_FLOAT, "" );
|
|
|
|
idCVar vr_teleportVel( "vr_teleportVel", "650", CVAR_FLOAT,"" );
|
|
idCVar vr_teleportDist( "vr_teleportDist", "60", CVAR_FLOAT,"" );
|
|
idCVar vr_teleportMaxPoints( "vr_teleportMaxPoints", "24", CVAR_FLOAT, "" );
|
|
idCVar vr_teleportMaxDrop( "vr_teleportMaxDrop", "360", CVAR_FLOAT, "" );
|
|
|
|
idCVar vr_laserSightUseOffset( "vr_laserSightUseOffset", "1", CVAR_BOOL | CVAR_ARCHIVE, " 0 = lasersight emits straight from barrel.\n 1 = use offsets from weapon def" );
|
|
|
|
// for testing
|
|
idCVar ftx( "ftx", "0", CVAR_FLOAT, "" );
|
|
idCVar fty( "fty", "0", CVAR_FLOAT, "" );
|
|
idCVar ftz( "ftz", "0", CVAR_FLOAT, "" );
|
|
|
|
extern idCVar g_demoMode;
|
|
|
|
const idVec3 neckOffset( -3, 0, -6 );
|
|
const int waistZ = -22.f;
|
|
|
|
idCVar vr_slotDebug( "vr_slotDebug", "0", CVAR_BOOL, "slot debug visualation" );
|
|
idCVar vr_slotMag( "vr_slotMag", "0.1", CVAR_FLOAT | CVAR_ARCHIVE, "slot vibration magnitude (0 is off)" );
|
|
idCVar vr_slotDur( "vr_slotDur", "18", CVAR_INTEGER | CVAR_ARCHIVE, "slot vibration duration in milliseconds" );
|
|
idCVar vr_slotDisable( "vr_slotDisable", "0", CVAR_BOOL | CVAR_ARCHIVE, "slot disable" );
|
|
|
|
slot_t slots[ SLOT_COUNT ] = {
|
|
{ idVec3( 0, 10, -4 ), 9.0f * 9.0f },
|
|
{ idVec3( 0, -10, -4 ), 9.0f * 9.0f },
|
|
{ idVec3( -9, -4, 4 ), 9.0f * 9.0f },
|
|
{ idVec3( -9, -4,-waistZ - neckOffset.z ), 9.0f * 9.0f },
|
|
{ idVec3( 4, 8, -waistZ + 2 ), 9.0f * 9.0f },
|
|
{ idVec3( -neckOffset.x, 0, -waistZ - neckOffset.z + 7 ), 9.0f * 9.0f },
|
|
};
|
|
|
|
idAngles pdaAngle1( 0, -90, 0);
|
|
idAngles pdaAngle2( 0, 0, 76.5);
|
|
idAngles pdaAngle3( 0, 0, 0);
|
|
|
|
extern idCVar g_useWeaponDepthHack;
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
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 = 25;
|
|
|
|
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_OpenPDA( "openPDA" );
|
|
const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
|
|
const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
|
|
const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
|
|
const idEventDef EV_Player_HideTip( "hideTip" );
|
|
const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
|
|
const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
|
|
const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
|
|
// Koz begin - let scripts query which hand does what when using motion controls
|
|
const idEventDef EV_Player_GetWeaponHand( "getWeaponHand", NULL, 'd' );
|
|
const idEventDef EV_Player_GetFlashHand( "getFlashHand", NULL, 'd' ); // get flashlight hand
|
|
const idEventDef EV_Player_GetWeaponHandState( "getWeaponHandState", NULL, 'd' );
|
|
const idEventDef EV_Player_GetFlashHandState( "getFlashHandState", NULL, 'd' ); // get flashlight hand state
|
|
const idEventDef EV_Player_GetFlashState( "getFlashState", NULL, 'd' ); // get flashlight state
|
|
|
|
// Koz end
|
|
|
|
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_OpenPDA, idPlayer::Event_OpenPDA )
|
|
EVENT( EV_Player_InPDA, idPlayer::Event_InPDA )
|
|
EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
|
|
EVENT( EV_Player_StopAudioLog, idPlayer::Event_StopAudioLog )
|
|
EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
|
|
EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
|
|
EVENT( EV_Gibbed, idPlayer::Event_Gibbed )
|
|
EVENT( EV_Player_GetIdealWeapon, idPlayer::Event_GetIdealWeapon )
|
|
// Koz begin
|
|
EVENT( EV_Player_GetWeaponHand, idPlayer::Event_GetWeaponHand )
|
|
EVENT( EV_Player_GetFlashHand, idPlayer::Event_GetFlashHand ) // get flashlight hand
|
|
EVENT( EV_Player_GetWeaponHandState, idPlayer::Event_GetWeaponHandState )
|
|
EVENT( EV_Player_GetFlashHandState, idPlayer::Event_GetFlashHandState ) // get flashlight hand state
|
|
EVENT( EV_Player_GetFlashState, idPlayer::Event_GetFlashState ) // get flashlight state
|
|
// Koz end
|
|
END_CLASS
|
|
|
|
const int MAX_RESPAWN_TIME = 10000;
|
|
const int RAGDOLL_DEATH_TIME = 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 )
|
|
};
|
|
|
|
/* Carl: Teleport
|
|
================
|
|
idPlayer::CanReachPosition
|
|
================
|
|
*/
|
|
bool idPlayer::CanReachPosition( const idVec3& pos, idVec3& betterPos )
|
|
{
|
|
aasPath_t path;
|
|
int toAreaNum;
|
|
int areaNum;
|
|
idVec3 origin;
|
|
|
|
toAreaNum = PointReachableAreaNum(pos);
|
|
betterPos = pos;
|
|
if (aas)
|
|
aas->PushPointIntoAreaNum( toAreaNum, betterPos );
|
|
|
|
idVec3 floorPos = betterPos;
|
|
|
|
origin = physicsObj.GetOrigin();
|
|
areaNum = PointReachableAreaNum(origin);
|
|
|
|
// check relative to the AAS area's official floor
|
|
if (aas)
|
|
{
|
|
floorPos.z -= 1000;
|
|
aas->PushPointIntoAreaNum(toAreaNum, floorPos);
|
|
// sloped floors will change x or y, not just z, wrecking our algorithm
|
|
if (floorPos.x != betterPos.x || floorPos.y != betterPos.y)
|
|
floorPos = betterPos;
|
|
// AAS areas have a valid floor (except for stairs), but not a valid ceiling
|
|
// if it's stairs, or our point is higher, then use our point
|
|
if (floorPos.z - pos.z < pm_stepsize.GetFloat() + 2 )
|
|
betterPos.z = pos.z;
|
|
// but if our point is too much lower than the AAS floor, use the AAS floor
|
|
}
|
|
// if in the same area, check relative to our feet
|
|
if (toAreaNum == areaNum)
|
|
{
|
|
floorPos.z = origin.z;
|
|
}
|
|
float height = pos.z - floorPos.z;
|
|
|
|
// if it's higher off the floor than we can jump, or lower than we can fall, then give up now
|
|
if (height > pm_jumpheight.GetFloat() + 2 || height < -140)
|
|
return false;
|
|
|
|
// if there's no AAS, we can teleport anywhere horizontal we can see, as long as it's height is within jumping or falling height
|
|
if (!aas)
|
|
return true;
|
|
|
|
if (ai_debugMove.GetBool())
|
|
{
|
|
aas->DrawArea(areaNum);
|
|
aas->DrawArea(toAreaNum);
|
|
}
|
|
if (!toAreaNum)
|
|
return false;
|
|
if (ai_debugMove.GetBool())
|
|
aas->ShowWalkPath(origin, toAreaNum, betterPos, travelFlags);
|
|
if (areaNum == toAreaNum)
|
|
return true;
|
|
|
|
aas->PushPointIntoAreaNum(areaNum, origin);
|
|
idReachability* reach = NULL;
|
|
int travelTime;
|
|
|
|
bool result = aas->RouteToGoalArea(areaNum, origin, toAreaNum, travelFlags, travelTime, &reach) && reach && (travelTime <= vr_teleportMaxTravel.GetInteger())
|
|
&& CheckTeleportPath(betterPos, toAreaNum);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::Clear
|
|
==============
|
|
*/
|
|
void idInventory::Clear( void ) {
|
|
maxHealth = 0;
|
|
weapons = 0;
|
|
powerups = 0;
|
|
armor = 0;
|
|
maxarmor = 0;
|
|
deplete_armor = 0;
|
|
deplete_rate = 0.0f;
|
|
deplete_ammount = 0;
|
|
nextArmorDepleteTime = 0;
|
|
|
|
memset( ammo, 0, sizeof( ammo ) );
|
|
|
|
ClearPowerUps();
|
|
|
|
// 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 );
|
|
memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
|
|
pdas.Clear();
|
|
videos.Clear();
|
|
emails.Clear();
|
|
selVideo = 0;
|
|
selEMail = 0;
|
|
selPDA = 0;
|
|
selAudio = 0;
|
|
pdaOpened = false;
|
|
turkeyScore = false;
|
|
|
|
levelTriggers.Clear();
|
|
|
|
nextItemPickup = 0;
|
|
nextItemNum = 1;
|
|
onePickupTime = 0;
|
|
pickupItemNames.Clear();
|
|
objectiveNames.Clear();
|
|
|
|
ammoPredictTime = 0;
|
|
|
|
lastGiveTime = 0;
|
|
|
|
ammoPulse = false;
|
|
weaponPulse = false;
|
|
armorPulse = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::GivePowerUp
|
|
==============
|
|
*/
|
|
void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
|
|
if ( !msec ) {
|
|
// get the duration from the .def files
|
|
const idDeclEntityDef *def = NULL;
|
|
switch ( powerup ) {
|
|
case BERSERK:
|
|
def = gameLocal.FindEntityDef( "powerup_berserk", false );
|
|
break;
|
|
case INVISIBILITY:
|
|
def = gameLocal.FindEntityDef( "powerup_invisibility", false );
|
|
break;
|
|
case MEGAHEALTH:
|
|
def = gameLocal.FindEntityDef( "powerup_megahealth", false );
|
|
break;
|
|
case ADRENALINE:
|
|
def = gameLocal.FindEntityDef( "powerup_adrenaline", false );
|
|
break;
|
|
}
|
|
assert( def );
|
|
msec = def->dict.GetInt( "time" ) * 1000;
|
|
}
|
|
powerups |= 1 << powerup;
|
|
powerupEndTime[ powerup ] = gameLocal.time + msec;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::ClearPowerUps
|
|
==============
|
|
*/
|
|
void idInventory::ClearPowerUps( void ) {
|
|
int i;
|
|
for ( i = 0; i < MAX_POWERUPS; i++ ) {
|
|
powerupEndTime[ i ] = 0;
|
|
}
|
|
powerups = 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::GetPersistantData
|
|
==============
|
|
*/
|
|
void idInventory::GetPersistantData( idDict &dict ) {
|
|
int i;
|
|
int num;
|
|
idDict *item;
|
|
idStr key;
|
|
const idKeyValue *kv;
|
|
const char *name;
|
|
|
|
// armor
|
|
dict.SetInt( "armor", armor );
|
|
|
|
// 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 ) {
|
|
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" );
|
|
|
|
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 );
|
|
}
|
|
|
|
num = dict.GetInt( "levelTriggers" );
|
|
for ( i = 0; i < num; i++ ) {
|
|
sprintf( itemname, "levelTrigger_Level_%i", i );
|
|
idLevelTriggerInfo lti;
|
|
lti.levelName = dict.GetString( itemname );
|
|
sprintf( itemname, "levelTrigger_Trigger_%i", i );
|
|
lti.triggerName = dict.GetString( itemname );
|
|
levelTriggers.Append( lti );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::Save
|
|
==============
|
|
*/
|
|
void idInventory::Save( idSaveGame *savefile ) const {
|
|
int i;
|
|
|
|
savefile->WriteInt( maxHealth );
|
|
savefile->WriteInt( weapons );
|
|
savefile->WriteInt( powerups );
|
|
savefile->WriteInt( armor );
|
|
savefile->WriteInt( maxarmor );
|
|
savefile->WriteInt( ammoPredictTime );
|
|
savefile->WriteInt( deplete_armor );
|
|
savefile->WriteFloat( deplete_rate );
|
|
savefile->WriteInt( deplete_ammount );
|
|
savefile->WriteInt( nextArmorDepleteTime );
|
|
|
|
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
|
savefile->WriteInt( ammo[ i ] );
|
|
}
|
|
for( i = 0; i < MAX_WEAPONS; i++ ) {
|
|
savefile->WriteInt( clip[ i ] );
|
|
}
|
|
for( i = 0; i < MAX_POWERUPS; i++ ) {
|
|
savefile->WriteInt( powerupEndTime[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( items.Num() );
|
|
for( i = 0; i < items.Num(); i++ ) {
|
|
savefile->WriteDict( items[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( pdasViewed[0] );
|
|
savefile->WriteInt( pdasViewed[1] );
|
|
savefile->WriteInt( pdasViewed[2] );
|
|
savefile->WriteInt( pdasViewed[3] );
|
|
|
|
savefile->WriteInt( selPDA );
|
|
savefile->WriteInt( selVideo );
|
|
savefile->WriteInt( selEMail );
|
|
savefile->WriteInt( selAudio );
|
|
savefile->WriteBool( pdaOpened );
|
|
savefile->WriteBool( turkeyScore );
|
|
|
|
savefile->WriteInt( pdas.Num() );
|
|
for( i = 0; i < pdas.Num(); i++ ) {
|
|
savefile->WriteString( pdas[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( pdaSecurity.Num() );
|
|
for( i=0; i < pdaSecurity.Num(); i++ ) {
|
|
savefile->WriteString( pdaSecurity[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( videos.Num() );
|
|
for( i = 0; i < videos.Num(); i++ ) {
|
|
savefile->WriteString( videos[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( emails.Num() );
|
|
for ( i = 0; i < emails.Num(); i++ ) {
|
|
savefile->WriteString( emails[ i ] );
|
|
}
|
|
|
|
savefile->WriteInt( nextItemPickup );
|
|
savefile->WriteInt( nextItemNum );
|
|
savefile->WriteInt( onePickupTime );
|
|
|
|
savefile->WriteInt( pickupItemNames.Num() );
|
|
for( i = 0; i < pickupItemNames.Num(); i++ ) {
|
|
savefile->WriteString( pickupItemNames[i].icon );
|
|
savefile->WriteString( pickupItemNames[i].name );
|
|
}
|
|
|
|
savefile->WriteInt( objectiveNames.Num() );
|
|
for( i = 0; i < objectiveNames.Num(); i++ ) {
|
|
savefile->WriteString( objectiveNames[i].screenshot );
|
|
savefile->WriteString( objectiveNames[i].text );
|
|
savefile->WriteString( objectiveNames[i].title );
|
|
}
|
|
|
|
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 );
|
|
savefile->WriteBool( armorPulse );
|
|
|
|
savefile->WriteInt( lastGiveTime );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::Restore
|
|
==============
|
|
*/
|
|
void idInventory::Restore( idRestoreGame *savefile ) {
|
|
int i, num;
|
|
|
|
savefile->ReadInt( maxHealth );
|
|
savefile->ReadInt( weapons );
|
|
savefile->ReadInt( powerups );
|
|
savefile->ReadInt( armor );
|
|
savefile->ReadInt( maxarmor );
|
|
savefile->ReadInt( ammoPredictTime );
|
|
savefile->ReadInt( deplete_armor );
|
|
savefile->ReadFloat( deplete_rate );
|
|
savefile->ReadInt( deplete_ammount );
|
|
savefile->ReadInt( nextArmorDepleteTime );
|
|
|
|
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
|
savefile->ReadInt( ammo[ i ] );
|
|
}
|
|
for( i = 0; i < MAX_WEAPONS; i++ ) {
|
|
savefile->ReadInt( clip[ i ] );
|
|
}
|
|
for( i = 0; i < MAX_POWERUPS; i++ ) {
|
|
savefile->ReadInt( powerupEndTime[ i ] );
|
|
}
|
|
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idDict *itemdict = new idDict;
|
|
|
|
savefile->ReadDict( itemdict );
|
|
items.Append( itemdict );
|
|
}
|
|
|
|
// pdas
|
|
savefile->ReadInt( pdasViewed[0] );
|
|
savefile->ReadInt( pdasViewed[1] );
|
|
savefile->ReadInt( pdasViewed[2] );
|
|
savefile->ReadInt( pdasViewed[3] );
|
|
|
|
savefile->ReadInt( selPDA );
|
|
savefile->ReadInt( selVideo );
|
|
savefile->ReadInt( selEMail );
|
|
savefile->ReadInt( selAudio );
|
|
savefile->ReadBool( pdaOpened );
|
|
savefile->ReadBool( turkeyScore );
|
|
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idStr strPda;
|
|
savefile->ReadString( strPda );
|
|
pdas.Append( strPda );
|
|
}
|
|
|
|
// pda security clearances
|
|
savefile->ReadInt( num );
|
|
for ( i = 0; i < num; i++ ) {
|
|
idStr invName;
|
|
savefile->ReadString( invName );
|
|
pdaSecurity.Append( invName );
|
|
}
|
|
|
|
// videos
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idStr strVideo;
|
|
savefile->ReadString( strVideo );
|
|
videos.Append( strVideo );
|
|
}
|
|
|
|
// email
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idStr strEmail;
|
|
savefile->ReadString( strEmail );
|
|
emails.Append( strEmail );
|
|
}
|
|
|
|
savefile->ReadInt( nextItemPickup );
|
|
savefile->ReadInt( nextItemNum );
|
|
savefile->ReadInt( onePickupTime );
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idItemInfo info;
|
|
|
|
savefile->ReadString( info.icon );
|
|
savefile->ReadString( info.name );
|
|
|
|
pickupItemNames.Append( info );
|
|
}
|
|
|
|
savefile->ReadInt( num );
|
|
for( i = 0; i < num; i++ ) {
|
|
idObjectiveInfo obj;
|
|
|
|
savefile->ReadString( obj.screenshot );
|
|
savefile->ReadString( obj.text );
|
|
savefile->ReadString( obj.title );
|
|
|
|
objectiveNames.Append( obj );
|
|
}
|
|
|
|
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 );
|
|
savefile->ReadBool( armorPulse );
|
|
|
|
savefile->ReadInt( lastGiveTime );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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::CanGive
|
|
===============
|
|
*/
|
|
bool idInventory::CanGive( idPlayer* owner, const idDict& spawnArgs, const char* statname, const char* value )
|
|
{
|
|
//GBFIX I presume this replaces Player::CanGive - needs investigating
|
|
return true;
|
|
/*
|
|
if( !idStr::Icmp( statname, "ammo_bloodstone" ) )
|
|
{
|
|
int max = MaxAmmoForAmmoClass( owner, statname );
|
|
int i = AmmoIndexForAmmoClass( statname );
|
|
|
|
if( max <= 0 )
|
|
{
|
|
//No Max
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
//Already at or above the max so don't allow the give
|
|
if( ammo[ i ].Get() >= max )
|
|
{
|
|
ammo[ i ] = max;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) )
|
|
{
|
|
// ignore these as they're handled elsewhere
|
|
//These items should not be considered as succesful gives because it messes up the max ammo items
|
|
return false;
|
|
}
|
|
return true;*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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 ) {
|
|
const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
|
|
if ( !decl ) {
|
|
gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
|
|
}
|
|
if ( ammoRequired ) {
|
|
*ammoRequired = decl->dict.GetInt( "ammoRequired" );
|
|
}
|
|
ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
|
|
return ammo_i;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idInventory::AddPickupName
|
|
==============
|
|
*/
|
|
void idInventory::AddPickupName( const char *name, const char *icon ) {
|
|
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 ) {
|
|
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 ) {
|
|
//Loop through and print out spawnArgs
|
|
spawnArgs.Print();
|
|
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 && ( weapons & ( 1 << i ) ) && ( duplicateWeapons & ( 1 << i ) ) && ( weaponDecl != NULL ) && !weaponDecl->dict.GetInt( "ammoRequired" ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
|
|
if ( ( weapons & ( 1 << i ) ) == 0 || ( duplicateWeapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
|
|
tookWeapon = true;
|
|
|
|
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;
|
|
if( ( weapons & ( 1 << i ) ) == 0 )
|
|
weapons |= ( 1 << i );
|
|
else
|
|
duplicateWeapons |= ( 1 << i );
|
|
foundWeapons |= weapons;
|
|
|
|
if( weaponName != "weapon_pda" )
|
|
{
|
|
for( int index = 0; index < NUM_QUICK_SLOTS; ++index )
|
|
{
|
|
if( owner->GetQuickSlot( index ) == -1 )
|
|
{
|
|
owner->SetQuickSlot( index, i );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
bool idPlayer::HasHoldableFlashlight()
|
|
{
|
|
int fm = commonVr->currentFlashlightMode;
|
|
if( vr_flashlightStrict.GetBool() )
|
|
{
|
|
if( fm != FLASHLIGHT_HAND && fm != FLASHLIGHT_INVENTORY )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// In non-strict mode you can move the flashlight from your armour or helmet to your hand,
|
|
// but you can't move it from your weapon.
|
|
if( fm == FLASHLIGHT_GUN || fm == FLASHLIGHT_PISTOL || fm == FLASHLIGHT_NONE )
|
|
return false;
|
|
}
|
|
// Carl: I'm not checking the weapons in the inventory. Maybe I should check that too? But I don't trust those values for the flashlight.
|
|
return flashlight && flashlight->IsLinked() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInventory::UpdateArmor
|
|
===============
|
|
*/
|
|
void idInventory::UpdateArmor( void ) {
|
|
if ( deplete_armor != 0.0f && deplete_armor < armor ) {
|
|
if ( !nextArmorDepleteTime ) {
|
|
nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
|
|
} else if ( gameLocal.time > nextArmorDepleteTime ) {
|
|
armor -= deplete_ammount;
|
|
if ( armor < deplete_armor ) {
|
|
armor = deplete_armor;
|
|
}
|
|
nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::idPlayer
|
|
==============
|
|
*/
|
|
idPlayer::idPlayer() {
|
|
memset( &usercmd, 0, sizeof( usercmd ) );
|
|
|
|
aas = NULL;
|
|
travelFlags = TFL_WALK | TFL_AIR | TFL_CROUCH | TFL_WALKOFFLEDGE | TFL_BARRIERJUMP | TFL_JUMP | TFL_LADDER | TFL_WATERJUMP | TFL_ELEVATOR | TFL_SPECIAL;
|
|
aimValidForTeleport = false;
|
|
|
|
teleportAimPoint = vec3_zero;
|
|
teleportPoint = vec3_zero;
|
|
teleportAimPointPitch = 0.0f;
|
|
|
|
flashlightPreviouslyInHand = false;
|
|
warpMove = false;
|
|
warpAim = false;
|
|
warpVel = vec3_zero;
|
|
|
|
noclip = false;
|
|
godmode = false;
|
|
|
|
spawnAnglesSet = false;
|
|
spawnAngles = ang_zero;
|
|
viewAngles = ang_zero;
|
|
cmdAngles = ang_zero;
|
|
|
|
// Koz begin : for independent weapon aiming in VR.
|
|
commonVr->independentWeaponYaw = 0.0f;
|
|
commonVr->independentWeaponPitch = 0.0f;
|
|
// Koz end
|
|
|
|
oldButtons = 0;
|
|
buttonMask = 0;
|
|
oldFlags = 0;
|
|
|
|
lastHitTime = 0;
|
|
lastSndHitTime = 0;
|
|
lastSavingThrowTime = 0;
|
|
|
|
pdaModelDefHandle = -1;
|
|
memset( &pdaRenderEntity, 0, sizeof( pdaRenderEntity ) );
|
|
|
|
holsterModelDefHandle = -1;
|
|
memset( &holsterRenderEntity, 0, sizeof( holsterRenderEntity ) );
|
|
|
|
headingBeamHandle = -1;
|
|
memset( &headingBeamEntity, 0, sizeof( headingBeamEntity ) );
|
|
|
|
hud = NULL;
|
|
objectiveSystem = NULL;
|
|
objectiveSystemOpen = false;
|
|
|
|
heartRate = BASE_HEARTRATE;
|
|
heartInfo.Init( 0, 0, 0, 0 );
|
|
lastHeartAdjust = 0;
|
|
lastHeartBeat = 0;
|
|
lastDmgTime = 0;
|
|
deathClearContentsTime = 0;
|
|
lastArmorPulse = -10000;
|
|
stamina = 0.0f;
|
|
healthPool = 0.0f;
|
|
nextHealthPulse = 0;
|
|
healthPulse = false;
|
|
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;
|
|
|
|
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;
|
|
|
|
weaponEnabled = true;
|
|
risingWeaponHand = -1;
|
|
weapon_soulcube = -1;
|
|
weapon_pda = -1;
|
|
weapon_fists = -1;
|
|
weapon_chainsaw = -1;
|
|
showWeaponViewModel = true;
|
|
|
|
flashlightModelDefHandle = -1;
|
|
|
|
hudHandle = -1;
|
|
memset( &hudEntity, 0, sizeof( hudEntity ) );
|
|
|
|
skin = NULL;
|
|
powerUpSkin = NULL;
|
|
baseSkinName = "";
|
|
|
|
numProjectilesFired = 0;
|
|
numProjectileHits = 0;
|
|
|
|
airless = false;
|
|
airTics = 0;
|
|
lastAirDamage = 0;
|
|
|
|
gibDeath = false;
|
|
gibsLaunched = false;
|
|
gibsDir = vec3_zero;
|
|
|
|
zoomFov.Init( 0, 0, 0, 0 );
|
|
centerView.Init( 0, 0, 0, 0 );
|
|
fxFov = false;
|
|
|
|
influenceFov = 0;
|
|
influenceActive = 0;
|
|
influenceRadius = 0.0f;
|
|
influenceEntity = NULL;
|
|
influenceMaterial = NULL;
|
|
influenceSkin = NULL;
|
|
|
|
privateCameraView = NULL;
|
|
|
|
memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
|
|
memset( loggedAccel, 0, sizeof( loggedAccel ) );
|
|
currentLoggedAccel = 0;
|
|
|
|
focusTime = 0;
|
|
focusGUIent = NULL;
|
|
focusUI = NULL;
|
|
focusCharacter = NULL;
|
|
talkCursor = 0;
|
|
focusVehicle = NULL;
|
|
cursor = NULL;
|
|
|
|
oldMouseX = 0;
|
|
oldMouseY = 0;
|
|
|
|
pdaAudio = "";
|
|
pdaVideo = "";
|
|
pdaVideoWave = "";
|
|
|
|
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;
|
|
objectiveUp = false;
|
|
teleportEntity = NULL;
|
|
teleportKiller = -1;
|
|
respawning = false;
|
|
ready = false;
|
|
leader = false;
|
|
lastSpectateChange = 0;
|
|
lastTeleFX = -9999;
|
|
weaponCatchup = false;
|
|
lastSnapshotSequence = 0;
|
|
|
|
MPAim = -1;
|
|
lastMPAim = -1;
|
|
lastMPAimTime = 0;
|
|
MPAimFadeTime = 0;
|
|
MPAimHighlight = false;
|
|
|
|
spawnedTime = 0;
|
|
lastManOver = false;
|
|
lastManPlayAgain = false;
|
|
lastManPresent = false;
|
|
|
|
isTelefragged = false;
|
|
|
|
isLagged = false;
|
|
isChatting = false;
|
|
|
|
selfSmooth = false;
|
|
|
|
ResetControllerShake();
|
|
blink = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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" );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetupWeaponEntity
|
|
==============
|
|
*/
|
|
void idPlayer::SetupWeaponEntity( void ) {
|
|
int w;
|
|
const char *weap;
|
|
|
|
// Carl: dual wielding
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].weapon )
|
|
{
|
|
// get rid of old weapon
|
|
hands[h].weapon->Clear();
|
|
hands[h].currentWeapon = -1; // carl: todo dual wielding
|
|
}
|
|
else if( !gameLocal.isClient )
|
|
{
|
|
hands[h].weapon = static_cast< idWeapon* >( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
|
|
hands[h].weapon->SetOwner( this, h );
|
|
hands[h].currentWeapon = -1; // carl: todo dual wielding
|
|
}
|
|
}
|
|
if( flashlight )
|
|
{
|
|
}
|
|
else if( !gameLocal.isClient )
|
|
{
|
|
// flashlight
|
|
flashlight = static_cast<idWeapon*>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
|
|
flashlight->SetFlashlightOwner( this );
|
|
//FlashlightOff();
|
|
|
|
}
|
|
|
|
|
|
for( w = 0; w < MAX_WEAPONS; w++ ) {
|
|
weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
if ( weap && *weap ) {
|
|
idWeapon::CacheWeapon( weap );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::ShouldBlink
|
|
|
|
Returns true if the view needs to be darkened
|
|
==================
|
|
*/
|
|
bool idPlayer::ShouldBlink()
|
|
{
|
|
return ( blink || commonVr->leanBlank );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idWeaponHolder::idWeaponHolder
|
|
==============
|
|
*/
|
|
idWeaponHolder::idWeaponHolder()
|
|
{
|
|
owner = NULL;
|
|
currentWeapon = 0;
|
|
isTheDuplicate = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idWeaponHolder::~idWeaponHolder
|
|
==============
|
|
*/
|
|
idWeaponHolder::~idWeaponHolder()
|
|
{
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idWeaponHolder::Init
|
|
==============
|
|
*/
|
|
void idWeaponHolder::Init( idPlayer* player )
|
|
{
|
|
owner = player;
|
|
currentWeapon = owner->weapon_fists;
|
|
isTheDuplicate = false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idWeaponHolder::isEmpty
|
|
==============
|
|
*/
|
|
bool idWeaponHolder::isEmpty()
|
|
{
|
|
if( currentWeapon < 0 || !owner )
|
|
return true;
|
|
return currentWeapon == owner->weapon_fists;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::Init
|
|
==============
|
|
*/
|
|
void idPlayer::Init( void ) {
|
|
const char *value;
|
|
const idKeyValue *kv;
|
|
|
|
flashlightPreviouslyInHand = false;
|
|
|
|
noclip = false;
|
|
godmode = false;
|
|
|
|
oldButtons = 0;
|
|
oldFlags = 0;
|
|
|
|
weaponEnabled = true;
|
|
weapon_soulcube = SlotForWeapon( "weapon_soulcube" );
|
|
weapon_pda = SlotForWeapon( "weapon_pda" );
|
|
weapon_fists = SlotForWeapon( "weapon_fists" );
|
|
weapon_flashlight = SlotForWeapon( "weapon_flashlight" );
|
|
weapon_chainsaw = SlotForWeapon( "weapon_chainsaw" );
|
|
showWeaponViewModel = GetUserInfo()->GetBool( "ui_showGun" );
|
|
harvest_lock = false;
|
|
|
|
// Koz begin;
|
|
weapon_pistol = SlotForWeapon( "weapon_pistol" );
|
|
weapon_shotgun = SlotForWeapon( "weapon_shotgun" );
|
|
weapon_machinegun = SlotForWeapon( "weapon_machinegun" );
|
|
weapon_chaingun = SlotForWeapon( "weapon_chaingun" );
|
|
weapon_handgrenade = SlotForWeapon( "weapon_handgrenade" );
|
|
weapon_plasmagun = SlotForWeapon( "weapon_plasmagun" );
|
|
weapon_rocketlauncher = SlotForWeapon( "weapon_rocketlauncher" );
|
|
weapon_bfg = SlotForWeapon( "weapon_bfg" );
|
|
weapon_flashlight_new = SlotForWeapon( "weapon_flashlight_new" );
|
|
// Koz end
|
|
|
|
|
|
lastDmgTime = 0;
|
|
lastArmorPulse = -10000;
|
|
lastHeartAdjust = 0;
|
|
lastHeartBeat = 0;
|
|
heartInfo.Init( 0, 0, 0, 0 );
|
|
|
|
bobCycle = 0;
|
|
bobFrac = 0.0f;
|
|
landChange = 0;
|
|
landTime = 0;
|
|
zoomFov.Init( 0, 0, 0, 0 );
|
|
centerView.Init( 0, 0, 0, 0 );
|
|
fxFov = false;
|
|
|
|
influenceFov = 0;
|
|
influenceActive = 0;
|
|
influenceRadius = 0.0f;
|
|
influenceEntity = NULL;
|
|
influenceMaterial = NULL;
|
|
influenceSkin = NULL;
|
|
|
|
//mountedObject = NULL;
|
|
currentLoggedAccel = 0;
|
|
|
|
handRaised = false;
|
|
handLowered = false;
|
|
|
|
focusTime = 0;
|
|
focusGUIent = NULL;
|
|
focusUI = NULL;
|
|
focusCharacter = NULL;
|
|
talkCursor = 0;
|
|
focusVehicle = NULL;
|
|
|
|
pVRClientInfo = NULL;
|
|
|
|
// remove any damage effects
|
|
playerView.ClearEffects();
|
|
|
|
// damage values
|
|
fl.takedamage = true;
|
|
ClearPain();
|
|
|
|
// Koz reset holster slots
|
|
|
|
holsteredWeapon = weapon_fists;
|
|
extraHolsteredWeapon = weapon_fists;
|
|
extraHolsteredWeaponModel = NULL;
|
|
|
|
// restore persistent data
|
|
RestorePersistantInfo();
|
|
|
|
bobCycle = 0;
|
|
stamina = 0.0f;
|
|
healthPool = 0.0f;
|
|
nextHealthPulse = 0;
|
|
healthPulse = false;
|
|
nextHealthTake = 0;
|
|
healthTake = false;
|
|
|
|
for( int hand = 0; hand < 2; hand++ )
|
|
hands[ hand ].Init( this, hand );
|
|
SetupWeaponEntity();
|
|
|
|
heartRate = BASE_HEARTRATE;
|
|
AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
|
|
|
|
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();
|
|
|
|
value = spawnArgs.GetString( "model" );
|
|
if ( value && ( *value != 0 ) ) {
|
|
SetModel( value );
|
|
}
|
|
|
|
if ( cursor ) {
|
|
cursor->SetStateInt( "talkcursor", 0 );
|
|
cursor->SetStateString( "combatcursor", "1" );
|
|
cursor->SetStateString( "itemcursor", "0" );
|
|
cursor->SetStateString( "guicursor", "0" );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
InitPlayerBones();
|
|
|
|
commonVr->currentFlashlightMode = vr_flashlightMode.GetInteger();
|
|
|
|
commonVr->thirdPersonMovement = false;
|
|
commonVr->thirdPersonDelta = 0.0f;
|
|
commonVr->thirdPersonHudPos = vec3_zero;
|
|
commonVr->thirdPersonHudAxis = mat3_identity;
|
|
|
|
// 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_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;
|
|
objectiveUp = false;
|
|
teleportEntity = NULL;
|
|
teleportKiller = -1;
|
|
leader = false;
|
|
|
|
SetPrivateCameraView( NULL );
|
|
|
|
lastSnapshotSequence = 0;
|
|
|
|
MPAim = -1;
|
|
lastMPAim = -1;
|
|
lastMPAimTime = 0;
|
|
MPAimFadeTime = 0;
|
|
MPAimHighlight = false;
|
|
|
|
flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger(); // fully charged
|
|
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "aim_clear" );
|
|
}
|
|
|
|
cvarSystem->SetCVarBool( "ui_chat", false );
|
|
|
|
SetupPDASlot( true );
|
|
|
|
//Dr Beef - Implementation
|
|
//SetupFlashlightHolster();
|
|
//SetupLaserSight();
|
|
|
|
// Koz begin
|
|
|
|
// model to place hud in 3d space
|
|
memset( &hudEntity, 0, sizeof( hudEntity ) );
|
|
hudEntity.hModel = renderModelManager->FindModel( "/models/mapobjects/hud.lwo" );
|
|
hudEntity.customShader = declManager->FindMaterial( "vr/hud" );
|
|
hudEntity.weaponDepthHack = vr_hudOcclusion.GetBool();
|
|
|
|
skinCrosshairDot = declManager->FindSkin( "skins/vr/crosshairDot" );
|
|
skinCrosshairCircleDot = declManager->FindSkin( "skins/vr/crosshairCircleDot" );
|
|
skinCrosshairCross = declManager->FindSkin( "skins/vr/crosshairCross" );
|
|
|
|
|
|
// heading indicator for VR - point the direction the body is facing.
|
|
/*
|
|
* memset( &headingBeamEntity, 0, sizeof( headingBeamEntity ) );
|
|
headingBeamEntity.hModel = renderModelManager->FindModel( "/models/mapobjects/headingbeam.lwo" );
|
|
skinHeadingSolid = declManager->FindSkin( "skins/models/headingbeamsolid" );
|
|
skinHeadingArrows = declManager->FindSkin( "skins/models/headingbeamarrows" );
|
|
skinHeadingArrowsScroll = declManager->FindSkin( "skins/models/headingbeamarrowsscroll" );*/
|
|
|
|
//GBFix Not in Original
|
|
hudActive = true;
|
|
PDAorigin = vec3_zero;
|
|
PDAaxis = mat3_identity;
|
|
InitTeleportTarget();
|
|
|
|
aasState = 0;
|
|
// Koz end
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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 );
|
|
|
|
SetAAS();
|
|
InitAASLocation();
|
|
|
|
skin = renderEntity.customSkin;
|
|
|
|
// only the local player needs guis
|
|
if ( !gameLocal.isMultiplayer || entityNumber == gameLocal.localClientNum ) {
|
|
|
|
// load HUD
|
|
if ( gameLocal.isMultiplayer ) {
|
|
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 ) {
|
|
// DG: make it scale to 4:3 so crosshair looks properly round
|
|
// yes, like so many scaling-related things this is a bit hacky
|
|
// and note that this is special cased in StateChanged and you
|
|
// can *not* generally set windowDef properties like this.
|
|
cursor->SetStateBool("scaleto43", true);
|
|
cursor->StateChanged(gameLocal.time); // DG end
|
|
|
|
cursor->Activate( true, gameLocal.time );
|
|
}
|
|
|
|
objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true );
|
|
objectiveSystemOpen = false;
|
|
}
|
|
|
|
SetLastHitTime( 0 );
|
|
|
|
// load the armor sound feedback
|
|
declManager->FindSound( "player_sounds_hitArmor" );
|
|
|
|
// 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( this );
|
|
|
|
// supress model in non-player views, but allow it in mirrors and remote views
|
|
//renderEntity.suppressSurfaceInViewID = entityNumber+1;
|
|
//Carl: don't suppress drawing the player's body in 1st person if we want to see it (in VR)
|
|
renderEntity.suppressSurfaceInViewID = 0;
|
|
|
|
// don't project shadow on self or weapon
|
|
renderEntity.noSelfShadow = true;
|
|
|
|
idAFAttachment *headEnt = head;
|
|
if ( headEnt ) {
|
|
headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
|
|
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
|
|
if ( !gameLocal.isMultiplayer && gameLocal.serverInfo.FindKey( "devmap" ) ) {
|
|
// fire a trigger with the name "devmap"
|
|
idEntity *ent = gameLocal.FindEntity( "devmap" );
|
|
if ( ent ) {
|
|
ent->ActivateTargets( this );
|
|
}
|
|
}
|
|
if ( hud ) {
|
|
// We can spawn with a full soul cube, so we need to make sure the hud knows this
|
|
if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
|
|
int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
|
|
if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_souls" ) ] >= max_souls ) {
|
|
hud->HandleNamedEvent( "soulCubeReady" );
|
|
}
|
|
}
|
|
hud->HandleNamedEvent( "itemPickup" );
|
|
}
|
|
|
|
if ( GetPDA() ) {
|
|
// Add any emails from the inventory
|
|
for ( int i = 0; i < inventory.emails.Num(); i++ ) {
|
|
GetPDA()->AddEmail( inventory.emails[i] );
|
|
}
|
|
GetPDA()->SetSecurity( common->GetLanguageDict()->GetString( "#str_00066" ) );
|
|
}
|
|
|
|
if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
|
|
hiddenWeapon = true;
|
|
// Carl: dual wielding
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].weapon )
|
|
{
|
|
if( !game->isVR ) hands[ h ].weapon->LowerWeapon(); // Koz
|
|
}
|
|
hands[ h ].idealWeapon = weapon_fists;
|
|
}
|
|
} else {
|
|
hiddenWeapon = false;
|
|
}
|
|
|
|
if ( hud ) {
|
|
UpdateHudWeapon(vr_weaponHand.GetInteger());
|
|
hud->StateChanged( gameLocal.time );
|
|
}
|
|
|
|
tipUp = false;
|
|
objectiveUp = false;
|
|
|
|
if ( inventory.levelTriggers.Num() ) {
|
|
PostEventMS( &EV_Player_LevelTrigger, 0 );
|
|
}
|
|
|
|
inventory.pdaOpened = false;
|
|
inventory.selPDA = 0;
|
|
|
|
if ( !gameLocal.isMultiplayer ) {
|
|
if ( g_skill.GetInteger() < 2 ) {
|
|
if ( health < 25 ) {
|
|
health = 25;
|
|
}
|
|
if ( g_useDynamicProtection.GetBool() ) {
|
|
g_damageScale.SetFloat( 1.0f );
|
|
}
|
|
} else {
|
|
g_damageScale.SetFloat( 1.0f );
|
|
g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
|
|
|
|
if ( g_skill.GetInteger() == 3 ) {
|
|
healthTake = true;
|
|
nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Setup the weapon toggle lists
|
|
const idKeyValue* kv;
|
|
kv = spawnArgs.MatchPrefix( "weapontoggle", NULL );
|
|
while( kv )
|
|
{
|
|
WeaponToggle_t newToggle;
|
|
strcpy( newToggle.name, kv->GetKey().c_str() );
|
|
|
|
idStr toggleData = kv->GetValue();
|
|
|
|
idLexer src;
|
|
idToken token;
|
|
src.LoadMemory( toggleData, toggleData.Length(), "toggleData" );
|
|
while( 1 )
|
|
{
|
|
if( !src.ReadToken( &token ) )
|
|
{
|
|
break;
|
|
}
|
|
int index = atoi( token.c_str() );
|
|
newToggle.toggleList.Append( index );
|
|
|
|
//Skip the ,
|
|
src.ReadToken( &token );
|
|
}
|
|
newToggle.lastUsed = 0;
|
|
weaponToggles.Set( newToggle.name, newToggle );
|
|
|
|
kv = spawnArgs.MatchPrefix( "weapontoggle", kv );
|
|
}
|
|
|
|
OrientHMDBody();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::~idPlayer()
|
|
|
|
Release any resources used by the player.
|
|
==============
|
|
*/
|
|
idPlayer::~idPlayer() {
|
|
FreePDASlot();
|
|
FreeHolsterSlot();
|
|
|
|
delete flashlight.GetEntity();
|
|
flashlight = NULL;
|
|
|
|
//GBFIX Not in New
|
|
/*delete weapon;
|
|
weapon = NULL;*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayerHand::~idPlayerHand()
|
|
|
|
Release any resources used by the player's hand.
|
|
==============
|
|
*/
|
|
idPlayerHand::~idPlayerHand()
|
|
{
|
|
delete weapon;
|
|
weapon = NULL;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayerHand::idPlayerHand
|
|
==============
|
|
*/
|
|
idPlayerHand::idPlayerHand():
|
|
idealWeapon( -1 )
|
|
{
|
|
weapon = NULL;
|
|
weaponGone = false;
|
|
playerPdaPos = vec3_zero;
|
|
currentWeapon = -1;
|
|
previousWeapon = -1;
|
|
isTheDuplicate = false;
|
|
weaponSwitchTime = 0;
|
|
|
|
laserSightHandle = -1;
|
|
memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
|
|
|
|
crosshairHandle = -1;
|
|
memset( &crosshairEntity, 0, sizeof( crosshairEntity ) );
|
|
|
|
lastCrosshairMode = -1;
|
|
|
|
throwDirection = vec3_zero;
|
|
throwVelocity = 0.0f;
|
|
frameTime[10] = { 0 };
|
|
position[10] = { vec3_zero };
|
|
frameNum = -1;
|
|
curTime = 0;
|
|
timeDelta = 0;
|
|
startFrameNum = 0;
|
|
|
|
grabbingWorld = false;
|
|
triggerDown = false;
|
|
thumbDown = false;
|
|
oldGrabbingWorld = false;
|
|
oldTriggerDown = false;
|
|
oldFlashlightTriggerDown = false;
|
|
oldThumbDown = false;
|
|
|
|
// Koz begin
|
|
//laserSightActive = vr_weaponSight.GetInteger() == 0;
|
|
// Koz end
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
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->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 );
|
|
|
|
// Carl: todo dual wielding
|
|
hands[vr_weaponHand.GetInteger()].weapon.Save( savefile ); // Carl: Don't make saves incompatible. Note, we are saving the entity number here, not the object
|
|
hands[1 - vr_weaponHand.GetInteger()].weapon.Save( savefile ); // GB: Do makes saves incompatible, because I am just so very tired
|
|
|
|
for( int i = 0; i < NUM_QUICK_SLOTS; ++i )
|
|
{
|
|
savefile->WriteInt( quickSlot[ i ] );
|
|
}
|
|
|
|
savefile->WriteUserInterface( hud, false );
|
|
savefile->WriteUserInterface( objectiveSystem, false );
|
|
savefile->WriteBool( objectiveSystemOpen );
|
|
|
|
savefile->WriteInt( weapon_soulcube );
|
|
savefile->WriteInt( weapon_pda );
|
|
savefile->WriteInt( weapon_fists );
|
|
savefile->WriteInt( weapon_flashlight );
|
|
savefile->WriteInt( weapon_chainsaw );
|
|
// Koz
|
|
savefile->WriteInt( weapon_pistol );
|
|
savefile->WriteInt( weapon_shotgun );
|
|
savefile->WriteInt( weapon_machinegun );
|
|
savefile->WriteInt( weapon_chaingun );
|
|
savefile->WriteInt( weapon_handgrenade );
|
|
savefile->WriteInt( weapon_plasmagun );
|
|
savefile->WriteInt( weapon_rocketlauncher );
|
|
savefile->WriteInt( weapon_bfg );
|
|
savefile->WriteInt( weapon_flashlight_new );
|
|
// Koz end
|
|
savefile->WriteBool( harvest_lock );
|
|
savefile->WriteInt( heartRate );
|
|
|
|
savefile->WriteFloat( heartInfo.GetStartTime() );
|
|
savefile->WriteFloat( heartInfo.GetDuration() );
|
|
savefile->WriteFloat( heartInfo.GetStartValue() );
|
|
savefile->WriteFloat( heartInfo.GetEndValue() );
|
|
|
|
savefile->WriteInt( lastHeartAdjust );
|
|
savefile->WriteInt( lastHeartBeat );
|
|
savefile->WriteInt( lastDmgTime );
|
|
savefile->WriteInt( deathClearContentsTime );
|
|
savefile->WriteBool( doingDeathSkin );
|
|
savefile->WriteInt( lastArmorPulse );
|
|
savefile->WriteFloat( stamina );
|
|
savefile->WriteFloat( healthPool );
|
|
savefile->WriteInt( nextHealthPulse );
|
|
savefile->WriteBool( healthPulse );
|
|
savefile->WriteInt( nextHealthTake );
|
|
savefile->WriteBool( healthTake );
|
|
|
|
savefile->WriteBool( hiddenWeapon );
|
|
soulCubeProjectile.Save( savefile );
|
|
|
|
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 );
|
|
// Koz begin
|
|
savefile->WriteJoint( neckJoint );
|
|
savefile->WriteJoint( chestPivotJoint );
|
|
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
savefile->WriteJoint( ik_hand[i] );
|
|
savefile->WriteJoint( ik_elbow[i] );
|
|
savefile->WriteJoint( ik_shoulder[i] );
|
|
savefile->WriteJoint( ik_handAttacher[i] );
|
|
}
|
|
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
for ( int j = 0; j < 32; j++ )
|
|
{
|
|
savefile->WriteMat3( ik_handCorrectAxis[i][j] );
|
|
savefile->WriteVec3( handWeaponAttachertoWristJointOffset[i][j] );
|
|
savefile->WriteVec3( handWeaponAttacherToDefaultOffset[i][j] );
|
|
}
|
|
}
|
|
|
|
savefile->WriteBool( handLowered );
|
|
savefile->WriteBool( handRaised );
|
|
savefile->WriteBool( commonVr->handInGui );
|
|
// Koz end
|
|
|
|
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( hands[ vr_weaponHand.GetInteger() ].currentWeapon );
|
|
savefile->WriteInt( hands[ vr_weaponHand.GetInteger() ].idealWeapon );
|
|
savefile->WriteInt( hands[ vr_weaponHand.GetInteger() ].previousWeapon );
|
|
savefile->WriteInt( hands[ vr_weaponHand.GetInteger() ].weaponSwitchTime );
|
|
|
|
savefile->WriteInt( hands[ 1 - vr_weaponHand.GetInteger() ].currentWeapon );
|
|
savefile->WriteInt( hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon );
|
|
savefile->WriteInt( hands[ 1 - vr_weaponHand.GetInteger() ].previousWeapon );
|
|
savefile->WriteInt( hands[ 1 - vr_weaponHand.GetInteger() ].weaponSwitchTime );
|
|
|
|
savefile->WriteBool( weaponEnabled );
|
|
savefile->WriteBool( showWeaponViewModel );
|
|
|
|
savefile->WriteSkin( skin );
|
|
savefile->WriteSkin( powerUpSkin );
|
|
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->WriteObject( focusCharacter );
|
|
savefile->WriteInt( talkCursor );
|
|
savefile->WriteInt( focusTime );
|
|
savefile->WriteObject( focusVehicle );
|
|
savefile->WriteUserInterface( cursor, false );
|
|
|
|
savefile->WriteInt( oldMouseX );
|
|
savefile->WriteInt( oldMouseY );
|
|
|
|
savefile->WriteString( pdaAudio );
|
|
savefile->WriteString( pdaVideo );
|
|
savefile->WriteString( pdaVideoWave );
|
|
|
|
savefile->WriteBool( tipUp );
|
|
savefile->WriteBool( objectiveUp );
|
|
|
|
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 );
|
|
|
|
savefile->WriteFloat( pm_stamina.GetFloat() );
|
|
|
|
savefile->WriteInt( weaponToggles.Num() );
|
|
for( i = 0; i < weaponToggles.Num(); i++ )
|
|
{
|
|
WeaponToggle_t* weaponToggle = weaponToggles.GetIndex( i );
|
|
savefile->WriteString( weaponToggle->name );
|
|
savefile->WriteInt( weaponToggle->toggleList.Num() );
|
|
for( int j = 0; j < weaponToggle->toggleList.Num(); j++ )
|
|
{
|
|
savefile->WriteInt( weaponToggle->toggleList[j] );
|
|
}
|
|
}
|
|
|
|
savefile->WriteObject( flashlight );
|
|
savefile->WriteInt( flashlightBattery );
|
|
|
|
// Koz begin
|
|
savefile->WriteBool( hands[0].laserSightActive || hands[1].laserSightActive );
|
|
savefile->WriteBool( headingBeamActive );
|
|
savefile->WriteBool( hudActive );
|
|
|
|
savefile->WriteInt( commonVr->currentFlashlightMode );
|
|
savefile->WriteSkin( hands[vr_weaponHand.GetInteger()].crosshairEntity.customSkin );
|
|
|
|
savefile->WriteBool( hands[0].PDAfixed || hands[1].PDAfixed ); // Carl: Dual wielding, don't make saves incompatible
|
|
savefile->WriteVec3( PDAorigin );
|
|
savefile->WriteMat3( PDAaxis );
|
|
|
|
|
|
//blech. Im going to pad the savegame file with a few diff var types,
|
|
// so if more changes are needed in the future, maybe save game compat can be preserved.
|
|
|
|
//padded ints have been used now.
|
|
savefile->WriteInt( 666 ); // flag that holster has been saved.
|
|
savefile->WriteInt( holsteredWeapon );
|
|
savefile->WriteInt( extraHolsteredWeapon );
|
|
savefile->WriteInt( (int) holsterModelDefHandle );
|
|
|
|
|
|
savefile->WriteFloat( 0 );
|
|
savefile->WriteFloat( 0 );
|
|
savefile->WriteFloat( 0 );
|
|
savefile->WriteFloat( 0 );
|
|
savefile->WriteBool( false );
|
|
savefile->WriteBool( false );
|
|
savefile->WriteBool( false );
|
|
savefile->WriteBool( false );
|
|
savefile->WriteVec3( vec3_zero );
|
|
savefile->WriteVec3( vec3_zero );
|
|
savefile->WriteVec3( vec3_zero );
|
|
savefile->WriteVec3( vec3_zero );
|
|
|
|
savefile->WriteMat3( holsterAxis );
|
|
savefile->WriteMat3( mat3_identity );
|
|
savefile->WriteMat3( mat3_identity );
|
|
savefile->WriteMat3( mat3_identity );
|
|
|
|
// end padding
|
|
//GBFIX
|
|
//savefile->WriteRenderEntity( holsterRenderEntity ); // have to check if this has been saved
|
|
//savefile->WriteString( extraHolsteredWeaponModel );
|
|
|
|
// Koz end
|
|
|
|
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);
|
|
savefile->ReadAngles(viewAngles);
|
|
savefile->ReadAngles(cmdAngles);
|
|
|
|
memset(usercmd.angles, 0, sizeof(usercmd.angles));
|
|
SetViewAngles(viewAngles);
|
|
spawnAnglesSet = true;
|
|
|
|
savefile->ReadInt(buttonMask);
|
|
savefile->ReadInt(oldButtons);
|
|
savefile->ReadInt(oldFlags);
|
|
|
|
usercmd.flags = 0;
|
|
oldFlags = 0;
|
|
|
|
savefile->ReadInt(lastHitTime);
|
|
savefile->ReadInt(lastSndHitTime);
|
|
savefile->ReadInt(lastSavingThrowTime);
|
|
|
|
// Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
|
|
LinkScriptVariables();
|
|
|
|
// re-init the hand's laser model (and other things)
|
|
for( int hand = 0; hand < 2; hand++ )
|
|
hands[hand].Init( this, hand );
|
|
|
|
inventory.Restore(savefile);
|
|
idEntityPtr<idWeapon> weapon;
|
|
weapon.Restore(savefile);
|
|
// Carl: if it's the PDA, then we saved the PDA hand, otherwise we saved the weapon hand.
|
|
int savedWeaponHand;
|
|
if( weapon && weapon->IdentifyWeapon() == WEAPON_PDA )
|
|
savedWeaponHand = 1 - vr_weaponHand.GetInteger();
|
|
else
|
|
savedWeaponHand = vr_weaponHand.GetInteger();
|
|
hands[savedWeaponHand].weapon = weapon;
|
|
|
|
weapon.Restore(savefile);
|
|
hands[1 - savedWeaponHand].weapon = weapon;
|
|
|
|
// Carl: other hand's weapon isn't saved yet, so recreate it
|
|
//hands[ 1 - savedWeaponHand ].weapon = static_cast< idWeapon* >( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
|
|
//hands[ 1 - savedWeaponHand ].weapon->SetOwner( this, 1 - savedWeaponHand );
|
|
|
|
for (i = 0; i < inventory.emails.Num(); i++) {
|
|
GetPDA()->AddEmail(inventory.emails[i]);
|
|
}
|
|
|
|
for( int i = 0; i < NUM_QUICK_SLOTS; ++i )
|
|
{
|
|
savefile->ReadInt( quickSlot[ i ] );
|
|
}
|
|
|
|
savefile->ReadUserInterface(hud);
|
|
savefile->ReadUserInterface(objectiveSystem);
|
|
savefile->ReadBool(objectiveSystemOpen);
|
|
|
|
|
|
savefile->ReadInt(weapon_soulcube);
|
|
savefile->ReadInt(weapon_pda);
|
|
savefile->ReadInt(weapon_fists);
|
|
savefile->ReadInt( weapon_flashlight );
|
|
savefile->ReadInt( weapon_chainsaw );
|
|
|
|
savefile->ReadInt( weapon_pistol );
|
|
savefile->ReadInt( weapon_shotgun );
|
|
savefile->ReadInt( weapon_machinegun );
|
|
savefile->ReadInt( weapon_chaingun );
|
|
savefile->ReadInt( weapon_handgrenade );
|
|
savefile->ReadInt( weapon_plasmagun );
|
|
savefile->ReadInt( weapon_rocketlauncher );
|
|
savefile->ReadInt( weapon_bfg );
|
|
savefile->ReadInt( weapon_flashlight_new );
|
|
savefile->ReadBool( harvest_lock );
|
|
savefile->ReadInt(heartRate);
|
|
|
|
savefile->ReadFloat(set);
|
|
heartInfo.SetStartTime(set);
|
|
savefile->ReadFloat(set);
|
|
heartInfo.SetDuration(set);
|
|
savefile->ReadFloat(set);
|
|
heartInfo.SetStartValue(set);
|
|
savefile->ReadFloat(set);
|
|
heartInfo.SetEndValue(set);
|
|
|
|
savefile->ReadInt(lastHeartAdjust);
|
|
savefile->ReadInt(lastHeartBeat);
|
|
savefile->ReadInt(lastDmgTime);
|
|
savefile->ReadInt(deathClearContentsTime);
|
|
savefile->ReadBool(doingDeathSkin);
|
|
savefile->ReadInt(lastArmorPulse);
|
|
savefile->ReadFloat(stamina);
|
|
savefile->ReadFloat(healthPool);
|
|
savefile->ReadInt(nextHealthPulse);
|
|
savefile->ReadBool(healthPulse);
|
|
savefile->ReadInt(nextHealthTake);
|
|
savefile->ReadBool(healthTake);
|
|
|
|
savefile->ReadBool(hiddenWeapon);
|
|
soulCubeProjectile.Restore(savefile);
|
|
|
|
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);
|
|
// Carl: Don't change save format
|
|
bool gone = false;
|
|
savefile->ReadBool( gone );
|
|
hands[ 0 ].weaponGone = gone;
|
|
hands[ 1 ].weaponGone = gone;
|
|
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->ReadJoint( neckJoint );
|
|
savefile->ReadJoint( chestPivotJoint );
|
|
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
savefile->ReadJoint( ik_hand[i] );
|
|
savefile->ReadJoint( ik_elbow[i] );
|
|
savefile->ReadJoint( ik_shoulder[i] );
|
|
savefile->ReadJoint( ik_handAttacher[i] );
|
|
}
|
|
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
for ( int j = 0; j < 32; j++ )
|
|
{
|
|
savefile->ReadMat3( ik_handCorrectAxis[i][j] );
|
|
savefile->ReadVec3( handWeaponAttachertoWristJointOffset[i][j] );
|
|
savefile->ReadVec3( handWeaponAttacherToDefaultOffset[i][j] );
|
|
}
|
|
}
|
|
|
|
savefile->ReadBool( handLowered );
|
|
savefile->ReadBool( handRaised );
|
|
savefile->ReadBool( commonVr->handInGui );
|
|
|
|
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);
|
|
|
|
// carl: dual wielding, don't change save format
|
|
savefile->ReadInt( hands[ vr_weaponHand.GetInteger() ].currentWeapon );
|
|
int savedIdealWeapon = -1;
|
|
savefile->ReadInt( savedIdealWeapon );
|
|
hands[ vr_weaponHand.GetInteger() ].idealWeapon = savedIdealWeapon;
|
|
savefile->ReadInt( hands[ vr_weaponHand.GetInteger() ].previousWeapon );
|
|
savefile->ReadInt( hands[ vr_weaponHand.GetInteger() ].weaponSwitchTime );
|
|
|
|
// GB Change save format
|
|
savefile->ReadInt( hands[ 1 - vr_weaponHand.GetInteger() ].currentWeapon );
|
|
int savedIdealWeaponOff = -1;
|
|
savefile->ReadInt( savedIdealWeaponOff );
|
|
hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon = savedIdealWeaponOff;
|
|
savefile->ReadInt( hands[ 1 - vr_weaponHand.GetInteger() ].previousWeapon );
|
|
savefile->ReadInt( hands[ 1 - vr_weaponHand.GetInteger() ].weaponSwitchTime );
|
|
|
|
savefile->ReadBool(weaponEnabled);
|
|
savefile->ReadBool(showWeaponViewModel);
|
|
|
|
savefile->ReadSkin(skin);
|
|
savefile->ReadSkin(powerUpSkin);
|
|
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->ReadObject(reinterpret_cast<idClass *&>( focusCharacter ));
|
|
savefile->ReadInt(talkCursor);
|
|
savefile->ReadInt(focusTime);
|
|
savefile->ReadObject(reinterpret_cast<idClass *&>( focusVehicle ));
|
|
savefile->ReadUserInterface(cursor);
|
|
|
|
// DG: make it scale to 4:3 so crosshair looks properly round
|
|
// yes, like so many scaling-related things this is a bit hacky
|
|
// and note that this is special cased in StateChanged and you
|
|
// can *not* generally set windowDef properties like this.
|
|
cursor->SetStateBool("scaleto43", true);
|
|
cursor->StateChanged(gameLocal.time); // DG end
|
|
|
|
savefile->ReadInt(oldMouseX);
|
|
savefile->ReadInt(oldMouseY);
|
|
|
|
savefile->ReadString(pdaAudio);
|
|
savefile->ReadString(pdaVideo);
|
|
savefile->ReadString(pdaVideoWave);
|
|
|
|
savefile->ReadBool(tipUp);
|
|
savefile->ReadBool(objectiveUp);
|
|
|
|
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);
|
|
|
|
// 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();
|
|
|
|
int weaponToggleCount;
|
|
savefile->ReadInt( weaponToggleCount );
|
|
for( i = 0; i < weaponToggleCount; i++ )
|
|
{
|
|
WeaponToggle_t newToggle;
|
|
memset( &newToggle, 0, sizeof( newToggle ) );
|
|
|
|
idStr name;
|
|
savefile->ReadString( name );
|
|
strcpy( newToggle.name, name.c_str() );
|
|
|
|
int indexCount;
|
|
savefile->ReadInt( indexCount );
|
|
for( int j = 0; j < indexCount; j++ )
|
|
{
|
|
int temp;
|
|
savefile->ReadInt( temp );
|
|
newToggle.toggleList.Append( temp );
|
|
}
|
|
newToggle.lastUsed = 0;
|
|
weaponToggles.Set( newToggle.name, newToggle );
|
|
}
|
|
|
|
// flashlight
|
|
idWeapon* tempWeapon;
|
|
savefile->ReadObject( reinterpret_cast<idClass*&>( tempWeapon ) );
|
|
tempWeapon->SetIsPlayerFlashlight( true );
|
|
flashlight = tempWeapon;
|
|
savefile->ReadInt( flashlightBattery );
|
|
|
|
holsteredWeapon = weapon_fists;
|
|
extraHolsteredWeapon = weapon_fists;
|
|
extraHolsteredWeaponModel = NULL;
|
|
|
|
|
|
//------------------------------------------------------------
|
|
// Koz begin - VR specific initialization
|
|
|
|
// Koz fixme ovr_RecenterTrackingOrigin( commonVr->hmdSession ); // Koz reset hmd orientation Koz fixme check if still appropriate here.
|
|
|
|
// make sure the clipmodels for the body and head are re-initialized.
|
|
SetClipModel();
|
|
|
|
// re-init the player render model if we're loading this savegame from a different mod
|
|
memset(&renderEntity, 0, sizeof(renderEntity));
|
|
renderEntity.numJoints = animator.NumJoints();
|
|
animator.GetJoints(&renderEntity.numJoints, &renderEntity.joints);
|
|
renderEntity.hModel = animator.ModelHandle();
|
|
if (renderEntity.hModel)
|
|
{
|
|
//renderEntity.hModel->Reset();
|
|
|
|
renderEntity.hModel->InitFromFile( renderEntity.hModel->Name() );
|
|
animator.ClearAllJoints();
|
|
|
|
renderEntity.bounds = renderEntity.hModel->Bounds(&renderEntity);
|
|
}
|
|
renderEntity.shaderParms[SHADERPARM_RED] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f;
|
|
renderEntity.shaderParms[3] = 1.0f;
|
|
renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = 0.0f;
|
|
renderEntity.shaderParms[5] = 0.0f;
|
|
renderEntity.shaderParms[6] = 0.0f;
|
|
renderEntity.shaderParms[7] = 0.0f;
|
|
|
|
// Koz re-init the player model bones. Changes to player model require this to allow compatability with older savegames.
|
|
InitPlayerBones();
|
|
|
|
//re-init the VR ui models
|
|
headingBeamHandle = -1;
|
|
hudHandle = -1;
|
|
|
|
// re-init hud model
|
|
memset( &hudEntity, 0, sizeof( hudEntity ) );
|
|
hudEntity.hModel = renderModelManager->FindModel( "/models/mapobjects/hud.lwo" );
|
|
hudEntity.customShader = declManager->FindMaterial( "vr/hud" );
|
|
hudEntity.weaponDepthHack = vr_hudOcclusion.GetBool();
|
|
|
|
// re-init the heading beam model
|
|
//memset( &headingBeamEntity, 0, sizeof( headingBeamEntity ) );
|
|
//headingBeamEntity.hModel = renderModelManager->FindModel( "/models/mapobjects/headingbeam.lwo" );
|
|
|
|
// re-init skins for heading indicator, crosshair, and telepad
|
|
skinHeadingSolid = declManager->FindSkin( "skins/models/headingbeamsolid" );
|
|
skinHeadingArrows = declManager->FindSkin( "skins/models/headingbeamarrows" );
|
|
skinHeadingArrowsScroll = declManager->FindSkin( "skins/models/headingbeamarrowsscroll" );
|
|
skinCrosshairDot = declManager->FindSkin( "skins/vr/crosshairDot" );
|
|
skinCrosshairCircleDot = declManager->FindSkin( "skins/vr/crosshairCircleDot" );
|
|
skinCrosshairCross = declManager->FindSkin( "skins/vr/crosshairCross" );
|
|
skinTelepadCrouch = declManager->FindSkin( "skins/vr/padcrouch" );
|
|
|
|
const idDeclSkin* blag;
|
|
// Koz begin
|
|
|
|
bool laserSightActive;
|
|
savefile->ReadBool( laserSightActive );
|
|
hands[ 0 ].laserSightActive = laserSightActive;
|
|
hands[ 1 ].laserSightActive = laserSightActive;
|
|
savefile->ReadBool( headingBeamActive );
|
|
savefile->ReadBool( hudActive );
|
|
|
|
savefile->ReadInt( commonVr->currentFlashlightMode );
|
|
// savefile->ReadSkin( hands[0].crosshairEntity.customSkin );
|
|
savefile->ReadSkin( blag );
|
|
|
|
bool PDAfixed = false;
|
|
savefile->ReadBool( PDAfixed );
|
|
hands[ 1 - vr_weaponHand.GetInteger() ].PDAfixed = PDAfixed;
|
|
hands[ vr_weaponHand.GetInteger() ].PDAfixed = false;
|
|
savefile->ReadVec3( PDAorigin );
|
|
savefile->ReadMat3( PDAaxis );
|
|
|
|
int tempInt;
|
|
float tempFloat;
|
|
bool tempBool;
|
|
idVec3 tempVec3;
|
|
idMat3 tempMat3;
|
|
|
|
int holsterFlag;
|
|
|
|
//blech. Im going to pad the savegame file with a few diff var types,
|
|
// so if more changes are needed in the future, maybe save game compat can be preserved.
|
|
|
|
//ints used saving weapon holsters.
|
|
savefile->ReadInt( holsterFlag );
|
|
if ( holsterFlag == 666 )
|
|
{
|
|
savefile->ReadInt( holsteredWeapon );
|
|
savefile->ReadInt( extraHolsteredWeapon );
|
|
savefile->ReadInt( tempInt );
|
|
holsterModelDefHandle = tempInt;
|
|
}
|
|
else
|
|
{
|
|
savefile->ReadInt( tempInt );
|
|
savefile->ReadInt( tempInt );
|
|
savefile->ReadInt( tempInt );
|
|
}
|
|
|
|
savefile->ReadFloat( tempFloat );
|
|
savefile->ReadFloat( tempFloat );
|
|
savefile->ReadFloat( tempFloat );
|
|
savefile->ReadFloat( tempFloat );
|
|
savefile->ReadBool( tempBool );
|
|
savefile->ReadBool( tempBool );
|
|
savefile->ReadBool( tempBool );
|
|
savefile->ReadBool( tempBool );
|
|
savefile->ReadVec3( tempVec3 );
|
|
savefile->ReadVec3( tempVec3 );
|
|
savefile->ReadVec3( tempVec3 );
|
|
savefile->ReadVec3( tempVec3 );
|
|
|
|
savefile->ReadMat3( tempMat3 );
|
|
if ( holsterFlag == 666 ) holsterAxis = tempMat3;
|
|
|
|
savefile->ReadMat3( tempMat3 );
|
|
savefile->ReadMat3( tempMat3 );
|
|
savefile->ReadMat3( tempMat3 );
|
|
|
|
/*if ( holsterFlag == 666 )
|
|
{
|
|
|
|
idStr ehwm;
|
|
savefile->ReadRenderEntity( holsterRenderEntity );
|
|
savefile->ReadString( ehwm );
|
|
extraHolsteredWeaponModel = ehwm.c_str();
|
|
|
|
if ( extraHolsteredWeapon != weapon_fists )
|
|
{
|
|
|
|
/*
|
|
If the game was autosaved, the holster and holster model will be correct,
|
|
but if the game was saved through the pause menu, the active weapon was pushed to the holster,
|
|
and the holstered weapon was pushed to extraHolsteredWeapon. This is the only time extraholstered
|
|
will not hold weapon_fists.
|
|
|
|
Check if the holstered weapon was pushed to the extraholster, and switch it back on load.
|
|
|
|
|
|
holsteredWeapon = extraHolsteredWeapon;
|
|
extraHolsteredWeapon = weapon_fists;
|
|
//common->Printf( "Loading holster model %s\n", extraHolsteredWeaponModel );
|
|
holsterRenderEntity.hModel = renderModelManager->FindModel( extraHolsteredWeaponModel );
|
|
|
|
if ( strcmp( extraHolsteredWeaponModel, "models/weapons/pistol/w_pistol.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 90, 0, 0 ).ToMat3() * 0.75f;
|
|
}
|
|
else if ( strcmp( extraHolsteredWeaponModel, "models/weapons/shotgun/w_shotgun2.lwo" ) == 0 ||
|
|
strcmp( extraHolsteredWeaponModel, "models/weapons/bfg/bfg_world.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 0, -90, -90 ).ToMat3();
|
|
}
|
|
else if ( strcmp( extraHolsteredWeaponModel, "models/weapons/machinegun/w_machinegun.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 0, 90, 90 ).ToMat3() * 0.75f;
|
|
}
|
|
else if ( strcmp( extraHolsteredWeaponModel, "models/weapons/plasmagun/plasmagun_world.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 0, 90, 90 ).ToMat3() * 0.75f;
|
|
}
|
|
else if ( strcmp( extraHolsteredWeaponModel, "models/weapons/chainsaw/w_chainsaw.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 0, 90, 90 ).ToMat3() * 0.9f;
|
|
}
|
|
else if ( strcmp( extraHolsteredWeaponModel, "models/weapons/chaingun/w_chaingun.lwo" ) == 0 )
|
|
{
|
|
holsterAxis = idAngles( 0, 90, 90 ).ToMat3() * 0.9f;
|
|
}
|
|
else
|
|
{
|
|
holsterAxis = idAngles( 0, 90, 90 ).ToMat3();
|
|
}
|
|
}
|
|
}*/
|
|
armIK.Init( this, IK_ANIM, modelOffset );
|
|
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "\nRestored Hands:\n" );
|
|
hands[HAND_LEFT].debugPrint();
|
|
hands[HAND_RIGHT].debugPrint();
|
|
}
|
|
|
|
vr_weaponSight.SetModified(); // make sure these get initialized properly
|
|
vr_headingBeamMode.SetModified();
|
|
|
|
aasState = 0;
|
|
|
|
// Koz end
|
|
|
|
|
|
|
|
// DG: workaround for lingering messages that are shown forever after loading a savegame
|
|
// (one way to get them is saving again, while the message from first save is still
|
|
// shown, and then load)
|
|
if (hud) {
|
|
hud->SetStateString("message", "");
|
|
}
|
|
|
|
//Have to do this for loaded games
|
|
//GB - I think this is Dr Beef Code
|
|
//weapon_flashlight = SlotForWeapon("weapon_flashlight");
|
|
//SetupFlashlightHolster();
|
|
//SetupLaserSight();
|
|
}
|
|
|
|
void idPlayer::SetupLaserSight()
|
|
{
|
|
laserSightHandle = -1;
|
|
memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
|
|
laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
|
|
laserSightRenderEntity.customShader = declManager->FindMaterial( "_white" );
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_ALPHA ] = 0.4f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[5] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[6] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[7] = 0.0f;
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::PrepareForRestart
|
|
================
|
|
*/
|
|
void idPlayer::PrepareForRestart( void ) {
|
|
ClearPowerUps();
|
|
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 ) {
|
|
// Make sure the weapon spawnId gets re-linked on the next snapshot.
|
|
// Otherwise, its owner might not be set after the map restart, which causes asserts and crashes.
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon = NULL;
|
|
flashlight = NULL;
|
|
Init();
|
|
} else {
|
|
// choose a random spot and prepare the point of view in case player is left spectating
|
|
assert( spectating );
|
|
SpawnFromSpawnSpot();
|
|
}
|
|
|
|
useInitialSpawns = true;
|
|
UpdateSkinSetup();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===========
|
|
idPlayer::SelectInitialSpawnPoint
|
|
|
|
Try to find a spawn point marked 'initial', otherwise
|
|
use normal spawn selection.
|
|
============
|
|
*/
|
|
void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
|
|
idEntity *spot;
|
|
idStr skin;
|
|
|
|
spot = gameLocal.SelectInitialSpawnPoint( this );
|
|
|
|
// 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 );
|
|
|
|
origin = spot->GetPhysics()->GetOrigin();
|
|
origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
|
|
angles = spot->GetPhysics()->GetAxis().ToAngles();
|
|
}
|
|
|
|
/*
|
|
===========
|
|
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 ) {
|
|
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", hands[ GetBestWeaponHand() ].currentWeapon );
|
|
// Koz begin
|
|
playerInfo.SetBool( "headingBeamActive", headingBeamActive );
|
|
playerInfo.SetBool( "laserSightActive", hands[0].laserSightActive || hands[1].laserSightActive ); // Carl: don't change save format
|
|
playerInfo.SetBool( "hudActive", hudActive );
|
|
playerInfo.SetInt( "currentFlashMode", commonVr->currentFlashlightMode );
|
|
|
|
playerInfo.SetInt( "holsteredWeapon", holsteredWeapon );
|
|
playerInfo.SetInt( "extraHolsteredWeapon", extraHolsteredWeapon );
|
|
if ( holsterRenderEntity.hModel )
|
|
{
|
|
playerInfo.Set( "holsteredWeaponModel", holsterRenderEntity.hModel->Name() );
|
|
}
|
|
playerInfo.SetMatrix( "holsterAxis", holsterAxis );
|
|
// Koz end
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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" );
|
|
hands[vr_weaponHand.GetInteger()].idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
|
|
hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon = weapon_fists;
|
|
// Koz begin
|
|
headingBeamActive = spawnArgs.GetBool( "headingBeamActive", "1" );
|
|
bool laserSightActive = spawnArgs.GetBool( "laserSightActive", "1" );
|
|
hands[ 0 ].laserSightActive = laserSightActive;
|
|
hands[ 1 ].laserSightActive = laserSightActive;
|
|
commonVr->currentFlashlightMode = spawnArgs.GetInt( "currentFlashMode", "3" );
|
|
hudActive = spawnArgs.GetBool( "hudActive", "1" );
|
|
|
|
holsteredWeapon = spawnArgs.GetInt( "holsteredWeapon", "-1" );
|
|
extraHolsteredWeapon = spawnArgs.GetInt( "extraHolsteredWeapon", "-1" );
|
|
|
|
idStr hwm;
|
|
|
|
//playerInfo.Get( "holsteredWeaponModel", holsterRenderEntity.hModel->Name() );
|
|
hwm = spawnArgs.GetString( "holsteredWeaponModel", "" );
|
|
holsterAxis = spawnArgs.GetMatrix( "holsterAxis", "" );
|
|
|
|
|
|
holsterRenderEntity.hModel = renderModelManager->FindModel( hwm.c_str() );
|
|
|
|
|
|
common->Printf( "Restored holsteredWeapon %d\n", holsteredWeapon );
|
|
common->Printf( "Restored extraHolsteredWeapon %d\n", extraHolsteredWeapon );
|
|
|
|
|
|
//playerInfo.SetInt( "holsteredWeapon", holsteredWeapon );
|
|
//playerInfo.SetInt( "extraHolsteredWeapon", extraHolsteredWeapon );
|
|
//playerInfo.Set( "extraHolsteredWeaponModel", extraHolsteredWeaponModel );
|
|
// Koz end
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::GetUserInfo
|
|
================
|
|
*/
|
|
idDict *idPlayer::GetUserInfo( void ) {
|
|
return &gameLocal.userInfo[ entityNumber ];
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateSkinSetup
|
|
==============
|
|
*/
|
|
/*
|
|
==============
|
|
idPlayer::UpdateSkinSetup
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateSkinSetup()
|
|
{
|
|
const char* handsOnly = "/vrHandsOnly";
|
|
const char* weaponOnly = "/vrWeaponsOnly";
|
|
const char* body = "/vrBody";
|
|
|
|
gameExpansionType_t gameType;
|
|
|
|
if ( game->isVR )
|
|
{
|
|
//idStr skinN = skin->GetName();
|
|
//GB Force skin
|
|
idStr skinN = "skins/characters/player/greenmarine_arm2";
|
|
|
|
if ( strstr( skinN.c_str(), "skins/characters/player/tshirt_mp" ) )
|
|
{
|
|
skinN = "skins/characters/player/tshirt_mp";
|
|
}
|
|
else if ( strstr( skinN.c_str(), "skins/characters/player/greenmarine_arm2" ) )
|
|
{
|
|
skinN = "skins/characters/player/greenmarine_arm2";
|
|
}
|
|
else if ( strstr( skinN.c_str(), "skins/characters/player/d3xp_sp_vrik" ) )
|
|
{
|
|
skinN = "skins/characters/player/d3xp_sp_vrik";
|
|
}
|
|
else
|
|
{
|
|
gameType = GetExpansionType();
|
|
|
|
if ( gameType == GAME_D3XP || gameType == GAME_D3LE )
|
|
{
|
|
skinN = "skins/characters/player/d3xp_sp_vrik";
|
|
}
|
|
else
|
|
{
|
|
skinN = "skins/characters/player/greenmarine_arm2";
|
|
}
|
|
}
|
|
|
|
if ( commonVr->thirdPersonMovement )
|
|
{
|
|
skinN += body;
|
|
}
|
|
else if ( vr_playerBodyMode.GetInteger() == 1 || ( vr_playerBodyMode.GetInteger() == 2 && (hands[ 0 ].currentWeapon == weapon_fists || hands[ 1 ].currentWeapon == weapon_fists || commonVr->handInGui) ) )
|
|
{
|
|
skinN += handsOnly;
|
|
}
|
|
else if ( vr_playerBodyMode.GetInteger() == 2 )
|
|
{
|
|
skinN += weaponOnly;
|
|
}
|
|
else
|
|
{
|
|
// if crouched more than 16 inches hide the body if enabled.
|
|
if ( (commonVr->headHeightDiff < -16.0f || IsCrouching()) && vr_crouchHideBody.GetBool() )
|
|
{
|
|
skinN += handsOnly;
|
|
}
|
|
else
|
|
{
|
|
skinN += body;
|
|
}
|
|
}
|
|
|
|
skin = declManager->FindSkin( skinN.c_str(), false );
|
|
// common->Printf( "UpdateSkinSetup returning player skin %s\n", skinN.c_str() );
|
|
return;
|
|
}
|
|
|
|
|
|
if ( !gameLocal.isMultiplayer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*if( gameLocal.mpGame.IsGametypeTeamBased() ) // CTF
|
|
{
|
|
skinIndex = team + 1;
|
|
}
|
|
else
|
|
{
|
|
// Each player will now have their Skin Index Reflect their entity number ( host = 0, client 1 = 1, client 2 = 2 etc )
|
|
skinIndex = entityNumber; // session->GetActingGameStateLobbyBase().GetLobbyUserSkinIndex( gameLocal.lobbyUserIDs[entityNumber] );
|
|
}
|
|
const char* baseSkinName = gameLocal.mpGame.GetSkinName( skinIndex );
|
|
skin = declManager->FindSkin( baseSkinName, false );
|
|
if( PowerUpActive( BERSERK ) )
|
|
{
|
|
idStr powerSkinName = baseSkinName;
|
|
powerSkinName.Append( "_berserk" );
|
|
powerUpSkin = declManager->FindSkin( powerSkinName );
|
|
}
|
|
else if( PowerUpActive( INVULNERABILITY ) )
|
|
{
|
|
idStr powerSkinName = baseSkinName;
|
|
powerSkinName.Append( "_invuln" );
|
|
powerUpSkin = declManager->FindSkin( powerSkinName );
|
|
}
|
|
else if( PowerUpActive( INVISIBILITY ) )
|
|
{
|
|
const char* invisibleSkin = "";
|
|
spawnArgs.GetString( "skin_invisibility", "", &invisibleSkin );
|
|
powerUpSkin = declManager->FindSkin( invisibleSkin );
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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( );
|
|
|
|
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, int hand ) {
|
|
int inclip;
|
|
int ammoamount;
|
|
|
|
assert( hands[hand].weapon );
|
|
assert( _hud );
|
|
|
|
inclip = hands[hand].weapon->AmmoInClip();
|
|
ammoamount = hands[hand].weapon->AmmoAvailable();
|
|
if ( ammoamount < 0 || !hands[hand].weapon->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", hands[hand].weapon->ClipSize() ? va( "%i", inclip ) : "--" ); // how much in the current clip
|
|
_hud->SetStateString( "player_clips", hands[hand].weapon->ClipSize() ? va( "%i", ammoamount / hands[hand].weapon->ClipSize() ) : "--" );
|
|
_hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
|
|
}
|
|
|
|
_hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
|
|
_hud->SetStateBool( "player_clip_empty", ( hands[hand].weapon->ClipSize() ? inclip == 0 : false ) );
|
|
_hud->SetStateBool( "player_clip_low", ( hands[hand].weapon->ClipSize() ? inclip <= hands[hand].weapon->LowAmmo() : false ) );
|
|
|
|
_hud->HandleNamedEvent( "updateAmmo" );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::UpdateHudStats
|
|
===============
|
|
*/
|
|
void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
|
|
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( vr_weaponHand.GetInteger() );
|
|
_hud->HandleNamedEvent( "weaponPulse" );
|
|
inventory.weaponPulse = false;
|
|
}
|
|
if ( inventory.armorPulse ) {
|
|
_hud->HandleNamedEvent( "armorPulse" );
|
|
inventory.armorPulse = false;
|
|
}
|
|
|
|
UpdateHudAmmo( _hud, vr_weaponHand.GetInteger());
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::UpdateHudWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::UpdateHudWeapon( int flashWeaponHand ) {
|
|
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 ( hands[ vr_weaponHand.GetInteger() ].idealWeapon == i ) {
|
|
weapstate++;
|
|
}
|
|
}
|
|
hud->SetStateInt( hudWeap, weapstate );
|
|
}
|
|
//GBFIX Doesnt ever seemed to be called
|
|
/*if ( flashWeapon ) {
|
|
hud->HandleNamedEvent( "weaponChange" );
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::DrawHUD
|
|
===============
|
|
*/
|
|
void idPlayer::DrawHUD( idUserInterface *_hud ) {
|
|
|
|
// Koz begin
|
|
if ( game->isVR && vr_hudType.GetInteger() == VR_HUD_NONE)
|
|
{
|
|
return;
|
|
}
|
|
// Koz end
|
|
|
|
if ( !hands[vr_weaponHand.GetInteger()].weapon || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !_hud || !g_showHud.GetBool() ) {
|
|
return;
|
|
}
|
|
|
|
UpdateHudStats( _hud );
|
|
|
|
_hud->SetStateString( "weapicon", hands[ vr_weaponHand.GetInteger() ].weapon->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" ) );
|
|
|
|
hands[ vr_weaponHand.GetInteger() ].weapon->UpdateGUI();
|
|
|
|
_hud->Redraw( gameLocal.realClientTime );
|
|
|
|
// weapon targeting crosshair
|
|
if ( !GuiActive() ) {
|
|
if ( cursor && hands[ vr_weaponHand.GetInteger() ].weapon->ShowCrosshair() ) {
|
|
//GB Fix - we may need this later
|
|
//cursor->Redraw( gameLocal.realClientTime );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetHudAlpha
|
|
===============
|
|
*/
|
|
float idPlayer::GetHudAlpha()
|
|
{
|
|
static int lastFrame = 0;
|
|
static float currentAlpha = 0.0f;
|
|
static float delta = 0.0f;
|
|
int currentFrameNumber = common->GetFrameNumber();
|
|
|
|
delta = vr_hudTransparency.GetFloat() / (125 / (1000 / commonVr->hmdHz));
|
|
|
|
if ( vr_hudType.GetInteger() != VR_HUD_LOOK_DOWN )
|
|
{
|
|
return hudActive ? vr_hudTransparency.GetFloat() : 0;
|
|
}
|
|
|
|
if ( lastFrame == currentFrameNumber ) return currentAlpha;
|
|
|
|
lastFrame = currentFrameNumber;
|
|
|
|
bool force = false;
|
|
|
|
if ( vr_hudLowHealth.GetInteger() >= health && health >= 0 ) force = true;
|
|
|
|
if ( commonVr->lastHMDPitch >= vr_hudRevealAngle.GetFloat() || force ) // fade stats in
|
|
{
|
|
currentAlpha += delta;
|
|
if ( currentAlpha > vr_hudTransparency.GetFloat() ) currentAlpha = vr_hudTransparency.GetFloat();
|
|
}
|
|
else
|
|
{
|
|
currentAlpha -= delta;
|
|
if ( currentAlpha < 0.0f ) currentAlpha = 0.0f;
|
|
}
|
|
|
|
return currentAlpha;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz
|
|
idPlayer::UpdateVrHud
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateVrHud()
|
|
{
|
|
static idVec3 hudOrigin;
|
|
static idMat3 hudAxis;
|
|
float hudPitch;
|
|
|
|
//GBFIX - This is DrBeefs code not FP
|
|
// update the hud model
|
|
/*
|
|
if ( (pVRClientInfo == nullptr) || !pVRClientInfo->visible_hud || gameLocal.inCinematic)
|
|
{
|
|
// hide it
|
|
hudEntity.allowSurfaceInViewID = -1;
|
|
}
|
|
else
|
|
{
|
|
hudEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
|
|
{
|
|
|
|
if (vr_hudmode.GetInteger() == 0)
|
|
{
|
|
// CalculateRenderView must have been called first
|
|
const idVec3 &viewOrigin = firstPersonViewOrigin;
|
|
const idMat3 &viewAxis = firstPersonViewAxis;
|
|
|
|
if (pVRClientInfo)
|
|
{
|
|
float yaw;
|
|
idAngles angles;
|
|
|
|
angles.pitch = pVRClientInfo->offhandangles[PITCH];
|
|
angles.yaw = viewAngles.yaw +
|
|
(pVRClientInfo->offhandangles[YAW] - pVRClientInfo->hmdorientation[YAW]);
|
|
angles.roll = pVRClientInfo->offhandangles[ROLL];
|
|
|
|
hudAxis = angles.ToMat3();
|
|
|
|
idVec3 offpos( -pVRClientInfo->offhandoffset[2],
|
|
-pVRClientInfo->offhandoffset[0],
|
|
pVRClientInfo->offhandoffset[1]);
|
|
|
|
idAngles a(0, viewAngles.yaw - pVRClientInfo->hmdorientation[YAW], 0);
|
|
offpos *= a.ToMat3();
|
|
offpos *= vr_worldscale.GetFloat();
|
|
|
|
{
|
|
hudOrigin = viewOrigin + offpos;
|
|
}
|
|
}
|
|
hudOrigin += hudAxis[2] * 16.0f;
|
|
hudOrigin += hudAxis[1] * -8.5f;
|
|
hudAxis *= 0.7; // scale
|
|
}
|
|
else {
|
|
//Fixed HUD, but make it take a short time to catch up with the player's yaw
|
|
static float yaw_x = 0.0f;
|
|
static float yaw_y = 1.0f;
|
|
yaw_x = 0.97f * yaw_x + 0.03f * cosf(DEG2RAD(viewAngles.yaw));
|
|
yaw_y = 0.97f * yaw_y + 0.03f * sinf(DEG2RAD(viewAngles.yaw));
|
|
|
|
GetViewPos( hudOrigin, hudAxis );
|
|
hudAxis = idAngles( 10.0f, RAD2DEG(atan2(yaw_y, yaw_x)), 0.0f ).ToMat3();
|
|
hudOrigin += hudAxis[0] * 24.0f;
|
|
hudOrigin.z += 4.0f;
|
|
hudOrigin += hudAxis[1] * -8.5f;
|
|
}
|
|
}
|
|
|
|
hudEntity.axis = hudAxis;
|
|
hudEntity.origin = hudOrigin;
|
|
hudEntity.weaponDepthHack = true;
|
|
|
|
}*/
|
|
|
|
// update the hud model
|
|
if (gameLocal.inCinematic)
|
|
//if ( (pVRClientInfo == nullptr) || ( !hudActive && ( vr_hudLowHealth.GetInteger() == 0 ) ) || commonVr->PDAforced || game->IsPDAOpen() || gameLocal.inCinematic)
|
|
{
|
|
// hide it
|
|
hudEntity.allowSurfaceInViewID = -1;
|
|
}
|
|
else
|
|
{
|
|
// always show HUD if in flicksync
|
|
hudEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
|
|
if ( vr_hudPosLock.GetInteger() == 1 && !commonVr->thirdPersonMovement ) // hud in fixed position in space, except if running in third person, then attach to face.
|
|
{
|
|
hudPitch = vr_hudType.GetInteger() == VR_HUD_LOOK_DOWN ? vr_hudPosAngle.GetFloat() : 10.0f;
|
|
|
|
float yaw;
|
|
if( gameLocal.inCinematic )
|
|
{
|
|
yaw = commonVr->lastHMDViewAxis.ToAngles().yaw;
|
|
hudOrigin = commonVr->lastHMDViewOrigin;
|
|
}
|
|
else
|
|
{
|
|
GetViewPos( hudOrigin, hudAxis );
|
|
yaw = viewAngles.yaw;
|
|
}
|
|
hudAxis = idAngles( hudPitch, yaw, 0.0f ).ToMat3();
|
|
|
|
hudOrigin += hudAxis[0] * vr_hudPosDis.GetFloat();
|
|
hudOrigin += hudAxis[1] * vr_hudPosHor.GetFloat();
|
|
hudOrigin.z += vr_hudPosVer.GetFloat();
|
|
}
|
|
else // hud locked to face
|
|
{
|
|
if ( commonVr->thirdPersonMovement )
|
|
{
|
|
hudAxis = commonVr->thirdPersonHudAxis;
|
|
hudOrigin = commonVr->thirdPersonHudPos;
|
|
}
|
|
else
|
|
{
|
|
hudAxis = commonVr->lastHMDViewAxis;
|
|
hudOrigin = commonVr->lastHMDViewOrigin;
|
|
}
|
|
hudOrigin += hudAxis[0] * vr_hudPosDis.GetFloat();
|
|
hudOrigin += hudAxis[1] * vr_hudPosHor.GetFloat();
|
|
hudOrigin += hudAxis[2] * vr_hudPosVer.GetFloat();
|
|
|
|
}
|
|
|
|
hudAxis *= vr_hudScale.GetFloat();
|
|
|
|
hudEntity.axis = hudAxis;
|
|
hudEntity.origin = hudOrigin;
|
|
hudEntity.weaponDepthHack = vr_hudOcclusion.GetBool();
|
|
|
|
}
|
|
|
|
if ( hudHandle == -1 )
|
|
{
|
|
hudHandle = gameRenderWorld->AddEntityDef( &hudEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( hudHandle, &hudEntity );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::EnterCinematic
|
|
===============
|
|
*/
|
|
void idPlayer::EnterCinematic( void ) {
|
|
Hide();
|
|
StopAudioLog();
|
|
StopSound( SND_CHANNEL_PDA, false );
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "radioChatterDown" );
|
|
}
|
|
|
|
physicsObj.SetLinearVelocity( vec3_origin );
|
|
|
|
SetState( "EnterCinematic" );
|
|
UpdateScript();
|
|
|
|
if ( weaponEnabled ) {
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
idWeapon* weapon = GetWeaponInHand( h );
|
|
if ( weapon )
|
|
weapon->EnterCinematic();
|
|
}
|
|
}
|
|
if( flashlight )
|
|
{
|
|
flashlight->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_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 )
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
idWeapon* weapon = GetWeaponInHand( h );
|
|
if( weapon )
|
|
weapon->ExitCinematic();
|
|
}
|
|
}
|
|
if( flashlight )
|
|
{
|
|
flashlight->ExitCinematic();
|
|
}
|
|
// long cinematics would have surpassed the healthTakeTime, causing the player to take damage
|
|
// immediately after the cinematic ends. Instead we start the healthTake cooldown again once
|
|
// the cinematic ends.
|
|
if( g_skill.GetInteger() == 3 )
|
|
{
|
|
nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
|
|
}
|
|
|
|
SetState( "ExitCinematic" );
|
|
UpdateScript();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idPlayer::UpdateConditions
|
|
=====================
|
|
*/
|
|
void idPlayer::UpdateConditions( void ) {
|
|
idVec3 velocity;
|
|
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();
|
|
|
|
if ( influenceActive ) {
|
|
AI_FORWARD = false;
|
|
AI_BACKWARD = false;
|
|
AI_STRAFE_LEFT = false;
|
|
AI_STRAFE_RIGHT = false;
|
|
} else 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( int hand, const idDict *weaponDef ) {
|
|
// force a blink
|
|
blink_time = 0;
|
|
|
|
// play the fire animation
|
|
AI_WEAPON_FIRED = true;
|
|
|
|
// shake controller
|
|
float highMagnitude = weaponDef->GetFloat( "controllerShakeHighMag" );
|
|
int highDuration = weaponDef->GetInt( "controllerShakeHighTime" );
|
|
float lowMagnitude = weaponDef->GetFloat( "controllerShakeLowMag" );
|
|
int lowDuration = weaponDef->GetInt( "controllerShakeLowTime" );
|
|
|
|
// update view feedback
|
|
playerView.WeaponFireFeedback( weaponDef );
|
|
|
|
if( IsLocallyControlled() )
|
|
{
|
|
hands[hand].SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
|
|
}
|
|
}
|
|
|
|
bool idPlayer::WeaponHandImpulseSlot()
|
|
{
|
|
int hand = vr_weaponHand.GetInteger();
|
|
slotIndex_t weaponHandSlot = hands[ hand ].handSlot;
|
|
if( weaponHandSlot == SLOT_WEAPON_HIP )
|
|
{
|
|
if ( objectiveSystemOpen ) // for now this means our weapon hand was empty
|
|
{
|
|
if ( hands[ hand ].previousWeapon == weapon_fists )
|
|
{
|
|
hands[ hand ].previousWeapon = holsteredWeapon;
|
|
}
|
|
TogglePDA( hand );
|
|
}
|
|
else
|
|
{
|
|
// if our hand is too full, we can't pick up the weapon
|
|
if( hands[ hand ].tooFullToInteract() && holsteredWeapon != weapon_fists )
|
|
return false;
|
|
SetupHolsterSlot( hand );
|
|
}
|
|
return true;
|
|
}
|
|
if( weaponHandSlot == SLOT_WEAPON_BACK_BOTTOM )
|
|
{
|
|
if ( objectiveSystemOpen )
|
|
{
|
|
TogglePDA( hand );
|
|
}
|
|
hands[ hand ].PrevWeapon();
|
|
return true;
|
|
}
|
|
if( weaponHandSlot == SLOT_WEAPON_BACK_TOP )
|
|
{
|
|
if ( objectiveSystemOpen )
|
|
{
|
|
TogglePDA( hand );
|
|
}
|
|
hands[ hand ].NextWeapon();
|
|
return true;
|
|
}
|
|
if ( weaponHandSlot == SLOT_PDA_HIP )
|
|
{
|
|
// if we're holding a gun ( not a pointer finger or fist ) then holster the gun
|
|
//if ( !commonVr->PDAforced && !objectiveSystemOpen && currentWeapon != weapon_fists )
|
|
// SetupHolsterSlot( hand );
|
|
// pick up PDA in our weapon hand, or pick up the torch if our hand is a pointer finger
|
|
if ( !gameLocal.isMultiplayer )
|
|
{
|
|
// we don't have a PDA, so toggle the menu instead
|
|
if ( commonVr->PDAforced || inventory.pdas.Num() == 0 )
|
|
{
|
|
if( !commonVr->PDAforced && hands[ vr_weaponHand.GetInteger() ].tooFullToInteract() )
|
|
return false;
|
|
SwapWeaponHand();
|
|
PerformImpulse( 40 );
|
|
}
|
|
else if( objectiveSystemOpen )
|
|
{
|
|
SwapWeaponHand();
|
|
TogglePDA( hand );
|
|
}
|
|
else if( weapon_pda >= 0 )
|
|
{
|
|
if( hands[ hand ].tooFullToInteract() )
|
|
return false;
|
|
SwapWeaponHand();
|
|
SetupPDASlot( false );
|
|
SetupHolsterSlot( 1 - hand, false );
|
|
hands[ hand ].SelectWeapon( weapon_pda, true, false );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
if( weaponHandSlot == SLOT_FLASHLIGHT_HEAD && !commonVr->PDAforced && !objectiveSystemOpen
|
|
&& flashlight.IsValid() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && !vr_flashlightStrict.GetBool() )
|
|
{
|
|
// if the flashlight is mounted on our head, and we're unambiguously trying to grab it with our weapon hand
|
|
if( commonVr->currentFlashlightPosition == FLASHLIGHT_HEAD && (hands[ hand ].currentWeapon == weapon_fists || vr_gripMode.GetInteger() == VR_GRIP_TOGGLE_WITH_DROP ) && HasHoldableFlashlight() )
|
|
{
|
|
// move weapon from hand to inventory
|
|
hands[hand].idealWeapon = weapon_fists;
|
|
SwapWeaponHand();
|
|
// swap flashlight between head and hand
|
|
vr_flashlightMode.SetInteger( FLASHLIGHT_HAND );
|
|
vr_flashlightMode.SetModified();
|
|
return true;
|
|
}
|
|
// if we're unambiguously trying to grab it, but there's nothing there, process and ignore the grip
|
|
else if( vr_gripMode.GetInteger() == VR_GRIP_TOGGLE_WITH_DROP )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if( weaponHandSlot == SLOT_FLASHLIGHT_SHOULDER && !commonVr->PDAforced && !objectiveSystemOpen
|
|
&& flashlight.IsValid() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && !vr_flashlightStrict.GetBool() )
|
|
{
|
|
// if the flashlight is mounted on our shoulder, and we're unambiguously trying to grab it with our weapon hand
|
|
if( commonVr->currentFlashlightPosition == FLASHLIGHT_BODY && ( hands[ hand ].currentWeapon == weapon_fists || vr_gripMode.GetInteger() == VR_GRIP_TOGGLE_WITH_DROP ) && HasHoldableFlashlight() )
|
|
{
|
|
// move weapon from hand to inventory
|
|
hands[hand].idealWeapon = weapon_fists;
|
|
// swap flashlight from shoulder to hand
|
|
SwapWeaponHand();
|
|
vr_flashlightMode.SetInteger( FLASHLIGHT_HAND );
|
|
vr_flashlightMode.SetModified();
|
|
return true;
|
|
}
|
|
// if we're unambiguously trying to grab it, but there's nothing there, process and ignore the grip
|
|
else if( vr_gripMode.GetInteger() == VR_GRIP_TOGGLE_WITH_DROP )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::StopFiring
|
|
===============
|
|
*/
|
|
void idPlayer::StopFiring( void ) {
|
|
AI_ATTACK_HELD = false;
|
|
AI_WEAPON_FIRED = false;
|
|
AI_RELOAD = false;
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].weapon )
|
|
{
|
|
hands[h].weapon->EndAttack();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::FireWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::FireWeapon( int hand, idWeapon *weap ) {
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "Before FireWeapon(%d):\n", hand );
|
|
hands[hand].debugPrint();
|
|
}
|
|
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 && weap->IsReady() ) {
|
|
if( g_infiniteAmmo.GetBool() || weap->AmmoInClip() || weap->AmmoAvailable() )
|
|
{
|
|
weapon_t w = weap->IdentifyWeapon();
|
|
AI_ATTACK_HELD = true;
|
|
weap->BeginAttack();
|
|
|
|
if ( ( weapon_soulcube >= 0 ) && ( w == WEAPON_SOULCUBE ) ) {
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "soulCubeNotReady" );
|
|
}
|
|
hands[hand].SelectWeapon( hands[hand].previousWeapon, false, 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::FlashlightOff
|
|
===============
|
|
*/
|
|
void idPlayer::FlashlightOff()
|
|
{
|
|
if( !flashlight.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
if( !flashlight->lightOn )
|
|
{
|
|
return;
|
|
}
|
|
//flashlight->FlashlightOff();
|
|
flashlight->FlashlightOff();
|
|
|
|
|
|
// Koz
|
|
const function_t* func;
|
|
func = scriptObject.GetFunction( "SetFlashHandPose" ); // Set flashlight hand pose
|
|
if ( func )
|
|
{
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
// Koz debug common->Printf( "Calling SetFlashHandPose\n" ); // Set flashlight hand pose
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
}
|
|
else
|
|
{
|
|
common->Warning( "Can't find function 'SetFlashHandPose' in object '%s'", scriptObject.GetTypeName() ); // Set flashlight hand pose
|
|
return;
|
|
}
|
|
// Koz
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::FlashlightOn
|
|
===============
|
|
*/
|
|
void idPlayer::FlashlightOn()
|
|
{
|
|
if( commonVr->currentFlashlightPosition == FLASHLIGHT_NONE || commonVr->currentFlashlightPosition == FLASHLIGHT_INVENTORY )
|
|
return;
|
|
|
|
if( !flashlight.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
if( flashlightBattery < idMath::Ftoi( flashlight_minActivatePercent.GetFloat() * flashlight_batteryDrainTimeMS.GetFloat() ) )
|
|
{
|
|
return;
|
|
}
|
|
if( gameLocal.inCinematic)
|
|
{
|
|
return;
|
|
}
|
|
if( flashlight->lightOn )
|
|
{
|
|
return;
|
|
}
|
|
if( health <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( spectating )
|
|
{
|
|
return;
|
|
}
|
|
|
|
flashlight->FlashlightOn();
|
|
|
|
// Koz pose the hand
|
|
const function_t* func;
|
|
|
|
func = scriptObject.GetFunction( "SetFlashHandPose" ); // Set flashlight hand pose
|
|
if ( func )
|
|
{
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
// Koz debug common->Printf( "Calling SetFlashHandPose\n" ); // Set flashlight hand pose
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
}
|
|
else
|
|
{
|
|
common->Warning( "Can't find function 'SetFlashHandPose' in object '%s'", scriptObject.GetTypeName() ); // Set flashlight hand pose
|
|
return;
|
|
}
|
|
// Koz end
|
|
|
|
|
|
}
|
|
|
|
void idPlayer::FreeModelDef()
|
|
{
|
|
idAFEntity_Base::FreeModelDef();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::FreePDASlot
|
|
==============
|
|
*/
|
|
void idPlayer::FreePDASlot()
|
|
{
|
|
if( pdaModelDefHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeEntityDef( pdaModelDefHandle );
|
|
pdaModelDefHandle = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::FreeHolsterSlot
|
|
==============
|
|
*/
|
|
void idPlayer::FreeHolsterSlot()
|
|
{
|
|
if( holsterModelDefHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeEntityDef( holsterModelDefHandle );
|
|
holsterModelDefHandle = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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 );
|
|
inventory.foundWeapons &= ~( 1 << w );
|
|
inventory.duplicateWeapons &= ~( 1 << w );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Give
|
|
===============
|
|
*/
|
|
bool idPlayer::Give( const char *statname, const char *value, int hand ) {
|
|
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 {
|
|
if( hand < 0 )
|
|
hand = vr_weaponHand.GetInteger();
|
|
bool ret = inventory.Give( this, spawnArgs, statname, value, &hands[hand].idealWeapon, true);
|
|
return ret;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveHealthPool
|
|
|
|
adds health to the player health pool
|
|
===============
|
|
*/
|
|
void idPlayer::GiveHealthPool( float amt ) {
|
|
|
|
if ( AI_DEAD ) {
|
|
return;
|
|
}
|
|
|
|
if ( health > 0 ) {
|
|
healthPool += amt;
|
|
if ( healthPool > inventory.maxHealth - health ) {
|
|
healthPool = inventory.maxHealth - health;
|
|
}
|
|
nextHealthPulse = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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;
|
|
int numPickup;
|
|
|
|
if ( gameLocal.isMultiplayer && spectating ) {
|
|
return false;
|
|
}
|
|
|
|
if( idStr::FindText( item->GetName(), "weapon_flashlight_new" ) > -1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( idStr::FindText( item->GetName(), "weapon_flashlight" ) > -1 )
|
|
{
|
|
// don't allow flashlight weapon unless classic mode is enabled
|
|
return false;
|
|
}
|
|
|
|
item->GetAttributes( attr );
|
|
|
|
gave = false;
|
|
numPickup = inventory.pickupItemNames.Num();
|
|
for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
|
|
arg = attr.GetKeyVal( i );
|
|
if ( Give( arg->GetKey(), arg->GetValue(), -1) ) {
|
|
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" );
|
|
}
|
|
|
|
// display the pickup feedback on the hud
|
|
if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
|
|
inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ) );
|
|
}
|
|
|
|
return gave;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::PowerUpModifier
|
|
===============
|
|
*/
|
|
float idPlayer::PowerUpModifier( int type ) {
|
|
float mod = 1.0f;
|
|
|
|
if ( PowerUpActive( BERSERK ) ) {
|
|
switch( type ) {
|
|
case SPEED: {
|
|
mod *= 1.7f;
|
|
break;
|
|
}
|
|
case PROJECTILE_DAMAGE: {
|
|
mod *= 2.0f;
|
|
break;
|
|
}
|
|
case MELEE_DAMAGE: {
|
|
mod *= 30.0f;
|
|
break;
|
|
}
|
|
case MELEE_DISTANCE: {
|
|
mod *= 2.0f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( gameLocal.isMultiplayer && !gameLocal.isClient ) {
|
|
if ( PowerUpActive( MEGAHEALTH ) ) {
|
|
if ( healthPool <= 0 ) {
|
|
GiveHealthPool( 100 );
|
|
}
|
|
} else {
|
|
healthPool = 0;
|
|
}
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::PowerUpActive
|
|
===============
|
|
*/
|
|
bool idPlayer::PowerUpActive( int powerup ) const {
|
|
return ( inventory.powerups & ( 1 << powerup ) ) != 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GivePowerUp
|
|
===============
|
|
*/
|
|
bool idPlayer::GivePowerUp( int powerup, int time ) {
|
|
const char *sound;
|
|
const char *skin;
|
|
|
|
if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
|
|
|
|
if ( gameLocal.isServer ) {
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
|
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
|
msg.WriteShort( powerup );
|
|
msg.WriteBits( 1, 1 );
|
|
ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
|
|
}
|
|
|
|
if ( powerup != MEGAHEALTH ) {
|
|
inventory.GivePowerUp( this, powerup, time );
|
|
}
|
|
|
|
const idDeclEntityDef *def = NULL;
|
|
|
|
switch( powerup ) {
|
|
case BERSERK: {
|
|
if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) ) {
|
|
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
|
|
}
|
|
if ( baseSkinName.Length() ) {
|
|
powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
|
|
}
|
|
if ( !gameLocal.isClient ) {
|
|
hands[ 0 ].idealWeapon = weapon_fists;
|
|
hands[ 1 ].idealWeapon = weapon_fists;
|
|
}
|
|
break;
|
|
}
|
|
case INVISIBILITY: {
|
|
spawnArgs.GetString( "skin_invisibility", "", &skin );
|
|
powerUpSkin = declManager->FindSkin( skin );
|
|
// remove any decals from the model
|
|
if ( modelDefHandle != -1 ) {
|
|
gameRenderWorld->RemoveDecals( modelDefHandle );
|
|
}
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].weapon.GetEntity() )
|
|
{
|
|
hands[h].weapon.GetEntity()->UpdateSkin();
|
|
}
|
|
}
|
|
if( flashlight.GetEntity() )
|
|
{
|
|
flashlight.GetEntity()->UpdateSkin();
|
|
}
|
|
if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
|
|
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
break;
|
|
}
|
|
case ADRENALINE: {
|
|
stamina = 100.0f;
|
|
break;
|
|
}
|
|
case MEGAHEALTH: {
|
|
if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
|
|
StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
def = gameLocal.FindEntityDef( "powerup_megahealth", false );
|
|
if ( def ) {
|
|
health = def->dict.GetInt( "inv_health" );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "itemPickup" );
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::ClearPowerup
|
|
==============
|
|
*/
|
|
void idPlayer::ClearPowerup( int i ) {
|
|
|
|
if ( gameLocal.isServer ) {
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
|
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
|
msg.WriteShort( i );
|
|
msg.WriteBits( 0, 1 );
|
|
ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
|
|
}
|
|
|
|
powerUpSkin = NULL;
|
|
inventory.powerups &= ~( 1 << i );
|
|
inventory.powerupEndTime[ i ] = 0;
|
|
switch( i ) {
|
|
case BERSERK: {
|
|
StopSound( SND_CHANNEL_DEMONIC, false );
|
|
break;
|
|
}
|
|
case INVISIBILITY: {
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon.GetEntity() )
|
|
{
|
|
hands[ h ].weapon.GetEntity()->UpdateSkin();
|
|
}
|
|
}
|
|
if( flashlight.GetEntity() )
|
|
{
|
|
flashlight.GetEntity()->UpdateSkin();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdatePowerUps
|
|
==============
|
|
*/
|
|
void idPlayer::UpdatePowerUps( void ) {
|
|
int i;
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
for ( i = 0; i < MAX_POWERUPS; i++ ) {
|
|
if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
|
|
ClearPowerup( i );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( health > 0 ) {
|
|
if ( powerUpSkin ) {
|
|
renderEntity.customSkin = powerUpSkin;
|
|
} else {
|
|
renderEntity.customSkin = skin;
|
|
}
|
|
}
|
|
|
|
if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
|
|
assert( !gameLocal.isClient ); // healthPool never be set on client
|
|
int amt = ( healthPool > 5 ) ? 5 : healthPool;
|
|
health += amt;
|
|
if ( health > inventory.maxHealth ) {
|
|
health = inventory.maxHealth;
|
|
healthPool = 0;
|
|
} else {
|
|
healthPool -= amt;
|
|
}
|
|
nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
|
|
healthPulse = true;
|
|
}
|
|
|
|
if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
|
|
assert( !gameLocal.isClient ); // healthPool never be set on client
|
|
health -= g_healthTakeAmt.GetInteger();
|
|
if ( health < g_healthTakeLimit.GetInteger() ) {
|
|
health = g_healthTakeLimit.GetInteger();
|
|
}
|
|
nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
|
|
healthTake = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::ClearPowerUps
|
|
===============
|
|
*/
|
|
void idPlayer::ClearPowerUps( void ) {
|
|
int i;
|
|
for ( i = 0; i < MAX_POWERUPS; i++ ) {
|
|
if ( PowerUpActive( i ) ) {
|
|
ClearPowerup( i );
|
|
}
|
|
}
|
|
inventory.ClearPowerUps();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveInventoryItem
|
|
===============
|
|
*/
|
|
bool idPlayer::GiveInventoryItem( idDict *item ) {
|
|
if ( gameLocal.isMultiplayer && spectating ) {
|
|
return false;
|
|
}
|
|
inventory.items.Append( new idDict( *item ) );
|
|
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" );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateObjectiveInfo
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateObjectiveInfo( void ) {
|
|
if ( objectiveSystem == NULL ) {
|
|
return;
|
|
}
|
|
objectiveSystem->SetStateString( "objective1", "" );
|
|
objectiveSystem->SetStateString( "objective2", "" );
|
|
objectiveSystem->SetStateString( "objective3", "" );
|
|
for ( int i = 0; i < inventory.objectiveNames.Num(); i++ ) {
|
|
objectiveSystem->SetStateString( va( "objective%i", i+1 ), "1" );
|
|
objectiveSystem->SetStateString( va( "objectivetitle%i", i+1 ), inventory.objectiveNames[i].title.c_str() );
|
|
objectiveSystem->SetStateString( va( "objectivetext%i", i+1 ), inventory.objectiveNames[i].text.c_str() );
|
|
objectiveSystem->SetStateString( va( "objectiveshot%i", i+1 ), inventory.objectiveNames[i].screenshot.c_str() );
|
|
}
|
|
objectiveSystem->StateChanged( gameLocal.time );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveObjective
|
|
===============
|
|
*/
|
|
void idPlayer::GiveObjective( const char *title, const char *text, const char *screenshot ) {
|
|
idObjectiveInfo info;
|
|
info.title = title;
|
|
info.text = text;
|
|
info.screenshot = screenshot;
|
|
inventory.objectiveNames.Append( info );
|
|
ShowObjective( "newObjective" );
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "newObjective" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::CompleteObjective
|
|
===============
|
|
*/
|
|
void idPlayer::CompleteObjective( const char *title ) {
|
|
int c = inventory.objectiveNames.Num();
|
|
for ( int i = 0; i < c; i++ ) {
|
|
if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
|
|
inventory.objectiveNames.RemoveIndex( i );
|
|
break;
|
|
}
|
|
}
|
|
ShowObjective( "newObjectiveComplete" );
|
|
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "newObjectiveComplete" );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveVideo
|
|
===============
|
|
*/
|
|
void idPlayer::GiveVideo( const char *videoName, idDict *item ) {
|
|
|
|
if ( videoName == NULL || *videoName == 0 ) {
|
|
return;
|
|
}
|
|
|
|
inventory.videos.AddUnique( videoName );
|
|
|
|
if ( item ) {
|
|
idItemInfo info;
|
|
info.name = item->GetString( "inv_name" );
|
|
info.icon = item->GetString( "inv_icon" );
|
|
inventory.pickupItemNames.Append( info );
|
|
}
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "videoPickup" );
|
|
}
|
|
}
|
|
|
|
// Carl: Context sensitive VR grabs, and dual wielding
|
|
// 0 = right hand, 1 = left hand; true if pressed, false if released; returns true if handled as grab
|
|
// WARNING! Called from the input thread?
|
|
bool idPlayer::GrabWorld( int hand, bool pressed )
|
|
{
|
|
bool b;
|
|
if( !pressed )
|
|
{
|
|
b = hands[ hand ].grabbingWorld;
|
|
hands[ hand ].grabbingWorld = false;
|
|
// We're releasing something
|
|
if( vr_gripMode.GetInteger() == VR_GRIP_HOLD )
|
|
{
|
|
hands[ hand ].releaseVirtualGrab();
|
|
// releasing outside a holster will drop the weapon
|
|
// releasing inside an empty holster should place the weapon in the holster
|
|
// releasing inside a full holster, when vr_mustEmptyHands is true, should drop the weapon
|
|
// releasing inside a full holster, when vr_mustEmptyHands is false, should put the holster in an intermediate state
|
|
// where it contains two weapons, your hand contains nothing, and you are waiting to pick up the other weapon
|
|
// if you then grab in that state, it should pick up the original weapon that was in the holster and show the gun you just put there
|
|
// if you move your hand away from the holster in that state, we could do one of three things (make it a cvar?)
|
|
// 1. move the original weapon into the inventory (accessed via your back) and show the new weapon in the holster
|
|
// 2. keep the original weapon in the holster and drop the weapon you tried to stash on the floor
|
|
// 3. move the new weapon into the inventory and keep the original weapon in the holster
|
|
// releasing inside a back holster should place the weapon in your inventory
|
|
// but should not change the next/previous weapon until your hand leaves the holster
|
|
}
|
|
else if( vr_gripMode.GetInteger() == VR_GRIP_DEAD_AND_BURIED )
|
|
{
|
|
hands[ hand ].releaseVirtualGrab();
|
|
// releasing outside a holster will return the weapon to the holster it came from
|
|
// (if it didn't come from a holster, move it to the hip holster on the same side as the hand)
|
|
// (anything in that holster always gets moved to inventory)
|
|
// releasing inside an empty holster should place the weapon in the holster
|
|
// releasing inside a full holster, when vr_mustEmptyHands is true, should return the weapon to the holster it came from
|
|
// releasing inside a full holster, when vr_mustEmptyHands is false, should put the holster in an intermediate state
|
|
// (option 2. above would seem weird in this case, but we should probably still allow it)
|
|
}
|
|
|
|
return b;
|
|
}
|
|
if ( hand == vr_weaponHand.GetInteger() )
|
|
b = WeaponHandImpulseSlot();
|
|
else
|
|
b = OtherHandImpulseSlot();
|
|
hands[hand].grabbingWorld = b;
|
|
if( vr_gripMode.GetInteger() == VR_GRIP_TOGGLE_WITH_DROP )
|
|
{
|
|
if( !b ) // if we didn't grab a holster
|
|
{
|
|
if( hands[ hand ].holdingSomethingDroppable() )
|
|
{
|
|
b = hands[ hand ].releaseVirtualGrab();
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveSecurity
|
|
===============
|
|
*/
|
|
void idPlayer::GiveSecurity( const char *security ) {
|
|
GetPDA()->SetSecurity( security );
|
|
if ( hud ) {
|
|
hud->SetStateString( "pda_security", "1" );
|
|
hud->HandleNamedEvent( "securityPickup" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GiveEmail
|
|
===============
|
|
*/
|
|
void idPlayer::GiveEmail( const char *emailName ) {
|
|
|
|
if ( emailName == NULL || *emailName == 0 ) {
|
|
return;
|
|
}
|
|
|
|
inventory.emails.AddUnique( emailName );
|
|
GetPDA()->AddEmail( emailName );
|
|
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "emailPickup" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GivePDA
|
|
===============
|
|
*/
|
|
void idPlayer::GivePDA( const char *pdaName, idDict *item, bool toggle )
|
|
{
|
|
if ( gameLocal.isMultiplayer && spectating ) {
|
|
return;
|
|
}
|
|
|
|
if ( item ) {
|
|
inventory.pdaSecurity.AddUnique( item->GetString( "inv_name" ) );
|
|
}
|
|
|
|
if ( pdaName == NULL || *pdaName == 0 ) {
|
|
pdaName = "personal";
|
|
}
|
|
|
|
const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, pdaName ) );
|
|
|
|
inventory.pdas.AddUnique( pdaName );
|
|
|
|
// Copy any videos over
|
|
for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
|
|
const idDeclVideo *video = pda->GetVideoByIndex( i );
|
|
if ( video ) {
|
|
inventory.videos.AddUnique( video->GetName() );
|
|
}
|
|
}
|
|
|
|
// This is kind of a hack, but it works nicely
|
|
// We don't want to display the 'you got a new pda' message during a map load
|
|
if ( gameLocal.GetFrameNum() > 10 ) {
|
|
if ( pda && hud ) {
|
|
idStr pdaName = pda->GetPdaName();
|
|
pdaName.RemoveColors();
|
|
hud->SetStateString( "pda", "1" );
|
|
hud->SetStateString( "pda_text", pdaName );
|
|
const char *sec = pda->GetSecurity();
|
|
hud->SetStateString( "pda_security", ( sec && *sec ) ? "1" : "0" );
|
|
hud->HandleNamedEvent( "pdaPickup" );
|
|
}
|
|
|
|
if ( inventory.pdas.Num() == 1 ) {
|
|
GetPDA()->RemoveAddedEmailsAndVideos();
|
|
if ( !objectiveSystemOpen ) {
|
|
if ( toggle ) // Koz: toggle pda renders a fullscreen PDA in normal play, for VR we need to select the pda 'weapon'.
|
|
{
|
|
common->Printf( "idPlayer::GivePDA calling Select Weapon for PDA\n" );
|
|
SetupPDASlot( false ); // show flashlight in PDA holster
|
|
SetupHolsterSlot( vr_weaponHand.GetInteger(), false ); // stash weapon hand's gun in weapon holster
|
|
hands[ 1 - vr_weaponHand.GetInteger() ].SelectWeapon( weapon_pda, true, false );
|
|
}
|
|
}
|
|
objectiveSystem->HandleNamedEvent( "showPDATip" );
|
|
//ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
|
|
}
|
|
|
|
if ( inventory.pdas.Num() > 1 && pda->GetNumVideos() > 0 && hud ) {
|
|
hud->HandleNamedEvent( "videoPickup" );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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 );
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "itemPickup" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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;
|
|
}
|
|
|
|
void idPlayer::SnapBodyToView()
|
|
{
|
|
idAngles newBodyAngles;
|
|
newBodyAngles = viewAngles;
|
|
newBodyAngles.pitch = 0;
|
|
newBodyAngles.roll = 0;
|
|
newBodyAngles.yaw += commonVr->lastHMDYaw - commonVr->bodyYawOffset;
|
|
newBodyAngles.Normalize180();
|
|
SetViewAngles( newBodyAngles );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Reload
|
|
===============
|
|
*/
|
|
void idPlayer::Reload( void ) {
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
if ( spectating || gameLocal.inCinematic || influenceActive ) {
|
|
return;
|
|
}
|
|
|
|
// Carl: Dual wielding, just reload both weapons
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
idWeapon* weapon = GetWeaponInHand( h );
|
|
if( weapon && weapon->IsLinked() && weapon->hideOffset == 0.0f ) // Koz don't reload when in gui
|
|
{
|
|
weapon->Reload();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::NextBestWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::NextBestWeapon( void ) {
|
|
hands[ vr_weaponHand.GetInteger() ].NextBestWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::NextWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::NextWeapon( void ) {
|
|
hands[ vr_weaponHand.GetInteger() ].NextWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::PrevWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::PrevWeapon( void ) {
|
|
hands[ vr_weaponHand.GetInteger() ].PrevWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::SelectWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::SelectWeapon( int num, bool force, bool specific ) {
|
|
if( hands[vr_weaponHand.GetInteger()].handExists() )
|
|
hands[vr_weaponHand.GetInteger()].SelectWeapon( num, force, specific );
|
|
else
|
|
hands[1 - vr_weaponHand.GetInteger()].SelectWeapon( num, force, specific );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::DropWeapon
|
|
=================
|
|
*/
|
|
void idPlayer::DropWeapons( bool died )
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].DropWeapon( died );
|
|
// Carl: Note that the old drop weapon code here looked like this, so perhaps we should pass a parameter to change the throw speed and drop time?
|
|
// item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
|
|
}
|
|
|
|
void idPlayerHand::GetControllerShake( int & highMagnitude, int & lowMagnitude ) const
|
|
{
|
|
if( gameLocal.inCinematic )
|
|
{
|
|
// no controller shake during cinematics
|
|
highMagnitude = 0;
|
|
lowMagnitude = 0;
|
|
return;
|
|
}
|
|
|
|
float lowMag = 0.0f;
|
|
float highMag = 0.0f;
|
|
|
|
lowMagnitude = 0;
|
|
highMagnitude = 0;
|
|
|
|
// use highest values from active buffers
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
if( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeLowTime[i] )
|
|
{
|
|
if( controllerShakeLowMag[i] > lowMag )
|
|
{
|
|
lowMag = controllerShakeLowMag[i];
|
|
}
|
|
}
|
|
if( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeHighTime[i] )
|
|
{
|
|
if( controllerShakeHighMag[i] > highMag )
|
|
{
|
|
highMag = controllerShakeHighMag[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
lowMagnitude = idMath::Ftoi( lowMag * 65535.0f );
|
|
highMagnitude = idMath::Ftoi( highMag * 65535.0f );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayerHand::GetCurrentWeaponString
|
|
=================
|
|
*/
|
|
idStr idPlayerHand::GetCurrentWeaponString()
|
|
{
|
|
const char* weapon;
|
|
|
|
if( currentWeapon >= 0 && owner )
|
|
{
|
|
weapon = owner->spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
|
|
return weapon;
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayerHand::Init()
|
|
==============
|
|
*/
|
|
void idPlayerHand::Init( idPlayer* player, int hand )
|
|
{
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "\nBefore Init():\n" );
|
|
debugPrint();
|
|
}
|
|
owner = player;
|
|
whichHand = hand;
|
|
|
|
currentWeapon = -1;
|
|
idealWeapon = -1;
|
|
previousWeapon = -1;
|
|
weaponSwitchTime = 0;
|
|
|
|
grabbingWorld = false;
|
|
triggerDown = false;
|
|
thumbDown = false;
|
|
oldGrabbingWorld = false;
|
|
oldTriggerDown = false;
|
|
oldFlashlightTriggerDown = false;
|
|
oldThumbDown = false;
|
|
|
|
laserSightHandle = -1;
|
|
// laser sight for 3DTV
|
|
memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
|
|
laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
|
|
laserSightRenderEntity.customShader = declManager->FindMaterial( "_white" );
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_ALPHA ] = 0.4f;
|
|
laserSightRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[5] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[6] = 0.0f;
|
|
laserSightRenderEntity.shaderParms[7] = 0.0f;
|
|
|
|
crosshairHandle = -1;
|
|
// model to place crosshair or red dot into 3d space
|
|
memset( &crosshairEntity, 0, sizeof( crosshairEntity ) );
|
|
crosshairEntity.hModel = renderModelManager->FindModel( "/models/mapobjects/weaponsight.lwo" );
|
|
crosshairEntity.weaponDepthHack = true;
|
|
|
|
lastCrosshairMode = -1;
|
|
|
|
throwDirection = vec3_zero;
|
|
throwVelocity = 0.0f;
|
|
|
|
PDAfixed = false;
|
|
lastPdaFixed = PDAfixed;
|
|
playerPdaPos = vec3_zero;
|
|
motionPosition = vec3_zero;
|
|
// motionRotation = idQuat_zero;
|
|
wasPDA = false;
|
|
|
|
// Koz begin
|
|
// laserSightActive = vr_weaponSight.GetInteger() == 0;
|
|
// Koz end
|
|
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "\nAfter Init():\n" );
|
|
debugPrint();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerHand::PrevWeapon
|
|
===============
|
|
*/
|
|
void idPlayerHand::PrevWeapon()
|
|
{
|
|
NextWeapon( -1 );
|
|
}
|
|
|
|
void idPlayerHand::NextBestWeapon()
|
|
{
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "\nBefore NextBestWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
const char* weap;
|
|
int w = MAX_WEAPONS;
|
|
|
|
if( !owner->weaponEnabled || !handExists() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Carl: Dual wielding, handle switching from flashlight properly
|
|
if( holdingFlashlight() )
|
|
{
|
|
if( vr_flashlightStrict.GetBool() )
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_INVENTORY;
|
|
else
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HEAD;
|
|
}
|
|
|
|
while( w > 0 )
|
|
{
|
|
w--;
|
|
if( w == owner->weapon_flashlight )
|
|
{
|
|
continue;
|
|
}
|
|
weap = owner->spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
if( !weap[ 0 ] || ( ( owner->inventory.weapons & ( 1 << w ) ) == 0 ) || ( !owner->inventory.HasAmmo( weap ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
if( !owner->spawnArgs.GetBool( va( "weapon%d_best", w ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Some weapons will report having ammo but the clip is empty and
|
|
//will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
|
|
//We need to skip these weapons because they cannot be used
|
|
/*if( owner->inventory.HasEmptyClipCannotRefill( weap, owner, isTheDuplicate ) )
|
|
{
|
|
continue;
|
|
}*/
|
|
|
|
|
|
// Carl: dual wielding
|
|
if( w != owner->weapon_fists )
|
|
{
|
|
int availableWeaponsOfThisType = 1;
|
|
if( owner->inventory.duplicateWeapons & ( 1 << w ) )
|
|
availableWeaponsOfThisType++;
|
|
// Carl: skip weapons in the holster unless we have a duplicate, TODO make it optional
|
|
if( w == owner->holsteredWeapon )
|
|
{
|
|
availableWeaponsOfThisType--;
|
|
if( availableWeaponsOfThisType <= 0 )
|
|
continue;
|
|
}
|
|
// Carl: skip weapons in the other hand unless we have a duplicate (dual wielding)
|
|
if( w == owner->hands[ 1 - whichHand ].idealWeapon)
|
|
{
|
|
availableWeaponsOfThisType--;
|
|
if( availableWeaponsOfThisType <= 0 )
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Carl: Because this is an automatic weapon switch, we don't want to change our other hand, so just skip weapons we can't dual wield
|
|
if( !owner->CanDualWield( w ) && !owner->CanDualWield( owner->hands[1 - whichHand].currentWeapon ) && !owner->CanDualWield( owner->hands[1 - whichHand].idealWeapon) )
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
idealWeapon = w;
|
|
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
|
|
owner->UpdateHudWeapon( whichHand );
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "After NextBestWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerHand::NextWeapon
|
|
===============
|
|
*/
|
|
void idPlayerHand::NextWeapon( int dir )
|
|
{
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
if ( dir > 0 )
|
|
common->Printf( "\nBefore NextWeapon():\n" );
|
|
else
|
|
common->Printf( "\nBefore PrevWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
|
|
// Koz dont change weapon if in gui
|
|
if( !owner || commonVr->handInGui || !owner->weaponEnabled || owner->spectating || owner->hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || owner->health < 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !handExists() )
|
|
return;
|
|
|
|
// check if we have any weapons
|
|
if( !owner->inventory.weapons )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Carl: Dual wielding, handle switching from flashlight properly
|
|
if( holdingFlashlight() )
|
|
{
|
|
if( vr_flashlightStrict.GetBool() )
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_INVENTORY;
|
|
else
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HEAD;
|
|
}
|
|
|
|
int w = idealWeapon;
|
|
while( 1 )
|
|
{
|
|
w += dir;
|
|
if( w >= MAX_WEAPONS )
|
|
{
|
|
w = 0;
|
|
}
|
|
else if( w < 0 )
|
|
{
|
|
w = MAX_WEAPONS - 1;
|
|
}
|
|
if( w == idealWeapon )
|
|
{
|
|
w = owner->weapon_fists;
|
|
break;
|
|
}
|
|
// if we don't have the weapon in our inventory, skip it
|
|
if( ( ( owner->inventory.weapons & ( 1 << w ) ) == 0 && w != owner->weapon_flashlight ) || ( w == owner->weapon_flashlight && !owner->HasHoldableFlashlight() ) )
|
|
{
|
|
continue;
|
|
}
|
|
// Carl: dual wielding
|
|
if( w != owner->weapon_fists )
|
|
{
|
|
int availableWeaponsOfThisType = 1;
|
|
if( owner->inventory.duplicateWeapons & ( 1 << w ) )
|
|
availableWeaponsOfThisType++;
|
|
// Carl: skip weapons in the other hand unless we have a duplicate (dual wielding)
|
|
if( w == owner->hands[1 - whichHand].idealWeapon )
|
|
{
|
|
availableWeaponsOfThisType--;
|
|
if( availableWeaponsOfThisType <= 0 )
|
|
continue;
|
|
}
|
|
// Carl: optionally skip weapons in the holster unless we have a duplicate
|
|
if( w == owner->holsteredWeapon )
|
|
{
|
|
availableWeaponsOfThisType--;
|
|
if( availableWeaponsOfThisType <= 0 )
|
|
{
|
|
vr_weaponcycle_t c = (vr_weaponcycle_t)vr_weaponCycleMode.GetInteger();
|
|
if( c == VR_WEAPONCYCLE_INCLUDE_HOLSTERED || c == VR_WEAPONCYCLE_HOLSTERED_AND_FLASHLIGHT || c == VR_WEAPONCYCLE_HOLSTERED_AND_FLASHLIGHT_AND_PDA )
|
|
{
|
|
// Carl: draw weapon from holster
|
|
owner->FreeHolsterSlot();
|
|
owner->holsteredWeapon = owner->weapon_fists;
|
|
memset( &owner->holsterRenderEntity, 0, sizeof( owner->holsterRenderEntity ) );
|
|
availableWeaponsOfThisType++;
|
|
}
|
|
else
|
|
{
|
|
// Skip holstered weapon
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const char* weap = owner->spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
if( !weap[0] )
|
|
{
|
|
continue;
|
|
}
|
|
// Carl: skip weapons if we can't automagically empty our other hand, and we can't dual wield this weapon.
|
|
// I'm making it so we can't automagically empty the weapon hand to please the non-weapon hand,
|
|
// so the non-weapon hand will only cycle through the dual-wieldable weapons.
|
|
if( ( vr_mustEmptyHands.GetBool() || whichHand == ( 1 - vr_weaponHand.GetInteger() ) ) && !owner->CanDualWield( w ) && !owner->CanDualWield( owner->hands[1 - whichHand].currentWeapon ) && !owner->CanDualWield( owner->hands[1 - whichHand].idealWeapon ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//GB don't let it cycle to the flashlight if we're the weapon hand
|
|
if(w == owner->weapon_flashlight && whichHand == vr_weaponHand.GetInteger())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Cycle to the flashlight if we're using our flashlight hand and the flashlight is in our inventory, OR if we set to include the flashlight in the weapon cycle.
|
|
// todo: don't let it cycle to the flashlight if it's in the other hand and we're the weapon hand
|
|
if( w == owner->weapon_flashlight && ( vr_weaponCycleMode.GetInteger() == VR_WEAPONCYCLE_INCLUDE_FLASHLIGHT || vr_weaponCycleMode.GetInteger() == VR_WEAPONCYCLE_HOLSTERED_AND_FLASHLIGHT
|
|
|| vr_weaponCycleMode.GetInteger() == VR_WEAPONCYCLE_HOLSTERED_AND_FLASHLIGHT_AND_PDA || ( whichHand == ( 1 - vr_weaponHand.GetInteger() ) && commonVr->currentFlashlightMode == FLASHLIGHT_INVENTORY ) ) )
|
|
{
|
|
break;
|
|
}
|
|
else if( w == owner->weapon_pda && vr_weaponCycleMode.GetInteger() == VR_WEAPONCYCLE_HOLSTERED_AND_FLASHLIGHT_AND_PDA )
|
|
{
|
|
// skip the pda if we haven't been given one yet
|
|
if( owner->weapon_pda >= 0 && owner->inventory.pdas.Num() == 0 )
|
|
continue;
|
|
break;
|
|
}
|
|
else if( !owner->spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( owner->inventory.HasAmmo( weap) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if( ( w != currentWeapon ) && ( w != idealWeapon ) )
|
|
{
|
|
// Carl: If we can't dual-wield it?
|
|
if( !owner->CanDualWield( w ) && !owner->CanDualWield( owner->hands[1 - whichHand].currentWeapon ) && !owner->CanDualWield( owner->hands[1 - whichHand].idealWeapon) )
|
|
{
|
|
// if we can't automagically empty our other hand, do nothing
|
|
if( vr_mustEmptyHands.GetBool() )
|
|
return;
|
|
// otherwise, first automagically empty our other hand
|
|
if( owner->CanDualWield( owner->weapon_fists ) )
|
|
owner->hands[1 - whichHand].SelectWeapon( owner->weapon_fists, true, true );
|
|
else
|
|
owner->hands[1 - whichHand].SelectWeapon( weapon_empty_hand, true, true );
|
|
}
|
|
|
|
// Carl: Dual wielding, handle switching to flashlight properly
|
|
if( w == owner->weapon_flashlight )
|
|
{
|
|
w = owner->weapon_fists;
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HAND;
|
|
commonVr->currentFlashlightPosition = FLASHLIGHT_HAND;
|
|
vr_weaponHand.SetInteger( 1 - whichHand );
|
|
}
|
|
|
|
idealWeapon = w;
|
|
weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
|
|
owner->UpdateHudWeapon( whichHand );
|
|
if( vr_debugHands.GetBool() )
|
|
common->Printf( "Changing weapon\n" );
|
|
}
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
if( dir > 0 )
|
|
common->Printf( "After NextWeapon():\n" );
|
|
else
|
|
common->Printf( "After PrevWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
}
|
|
|
|
void idPlayerHand::ResetControllerShake()
|
|
{
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
controllerShakeHighTime[i] = 0;
|
|
}
|
|
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
controllerShakeHighMag[i] = 0.0f;
|
|
}
|
|
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
controllerShakeLowTime[i] = 0;
|
|
}
|
|
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
controllerShakeLowMag[i] = 0.0f;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayerHand::SelectWeapon
|
|
===============
|
|
*/
|
|
void idPlayerHand::SelectWeapon( int num, bool force, bool specific )
|
|
{
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "Before SelectWeapon(%d, %d, %d):\n", num, force, specific );
|
|
debugPrint();
|
|
}
|
|
const char* weap;
|
|
|
|
if( !owner->weaponEnabled || owner->spectating || gameLocal.inCinematic || owner->health < 0 || commonVr->handInGui ) // Koz don't let the player change weapons if hand is currently in a gui
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !handExists() )
|
|
return;
|
|
|
|
if( num == weapon_empty_hand )
|
|
{
|
|
idealWeapon = owner->weapon_fists;
|
|
// Carl: TODO
|
|
return;
|
|
}
|
|
|
|
if( ( num < 0 ) || ( num >= MAX_WEAPONS ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( ( num != owner->weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
num = owner->weapon_fists;
|
|
owner->hiddenWeapon ^= 1;
|
|
if( owner->hiddenWeapon && (!game->isVR || commonVr->handInGui == true) )
|
|
{
|
|
if( weapon )
|
|
weapon->LowerWeapon(); // Koz
|
|
}
|
|
else
|
|
{
|
|
if( weapon )
|
|
weapon->RaiseWeapon();
|
|
}
|
|
}
|
|
|
|
// Carl: Dual wielding, handle switching to and from flashlight properly
|
|
if( num == owner->weapon_flashlight || num == owner->weapon_flashlight_new )
|
|
{
|
|
// if we don't have a holdable flashlight, do nothing
|
|
if( !owner->HasHoldableFlashlight() )
|
|
return;
|
|
// if we can't dual wield a flashlight...
|
|
if( !owner->CanDualWield( num ) && !owner->CanDualWield( owner->hands[1 - whichHand].currentWeapon ) && !owner->CanDualWield( owner->hands[1 - whichHand].idealWeapon ) )
|
|
{
|
|
// if we can't automagically empty our other hand, do nothing
|
|
if( vr_mustEmptyHands.GetBool() )
|
|
return;
|
|
// otherwise, first automagically empty our other hand
|
|
if( owner->CanDualWield( owner->weapon_fists ) )
|
|
owner->hands[1 - whichHand].SelectWeapon( owner->weapon_fists, true, true );
|
|
else
|
|
owner->hands[1 - whichHand].SelectWeapon( weapon_empty_hand, true, true );
|
|
}
|
|
|
|
idealWeapon = owner->weapon_fists;
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HAND;
|
|
vr_weaponHand.SetInteger( 1 - whichHand );
|
|
return;
|
|
}
|
|
else if( holdingFlashlight() )
|
|
{
|
|
if( vr_flashlightStrict.GetBool() )
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_INVENTORY;
|
|
else
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HEAD;
|
|
commonVr->currentFlashlightPosition = commonVr->currentFlashlightMode;
|
|
}
|
|
|
|
//Is the weapon a toggle weapon (eg, fists/chainsaw/grabber, or shotgun/super-shotgun, or artifact/soulcube)
|
|
WeaponToggle_t* weaponToggle;
|
|
if( !specific && owner->weaponToggles.Get( va( "weapontoggle%d", num ), &weaponToggle ) )
|
|
{
|
|
int weaponToggleIndex = 0;
|
|
|
|
//Find the current Weapon in the list
|
|
int currentIndex = -1;
|
|
for( int i = 0; i < weaponToggle->toggleList.Num(); i++ )
|
|
{
|
|
if( weaponToggle->toggleList[i] == idealWeapon )
|
|
{
|
|
currentIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if( currentIndex == -1 )
|
|
{
|
|
//Didn't find the current weapon so select the first item
|
|
weaponToggleIndex = weaponToggle->lastUsed;
|
|
}
|
|
else
|
|
{
|
|
//Roll to the next available item in the list
|
|
weaponToggleIndex = currentIndex;
|
|
weaponToggleIndex++;
|
|
if( weaponToggleIndex >= weaponToggle->toggleList.Num() )
|
|
{
|
|
weaponToggleIndex = 0;
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < weaponToggle->toggleList.Num(); i++ )
|
|
{
|
|
int weapNum = weaponToggle->toggleList[weaponToggleIndex];
|
|
// Carl: dual wielding
|
|
int availableWeaponsOfThisType = 0;
|
|
if( weapNum == owner->weapon_fists )
|
|
{
|
|
if( owner->inventory.weapons & ( 1 << weapNum ) )
|
|
availableWeaponsOfThisType++;
|
|
}
|
|
else
|
|
{
|
|
if( owner->inventory.weapons & ( 1 << weapNum ) )
|
|
availableWeaponsOfThisType++;
|
|
if( owner->inventory.duplicateWeapons & ( 1 << weapNum ) )
|
|
availableWeaponsOfThisType++;
|
|
// Carl: skip weapons in the holster unless we have a duplicate, TODO make it optional
|
|
if( weapNum == owner->holsteredWeapon )
|
|
availableWeaponsOfThisType--;
|
|
// Carl: skip weapons in the other hand unless we have a duplicate (dual wielding)
|
|
if( weapNum == owner->hands[ 1 - whichHand ].idealWeapon )
|
|
availableWeaponsOfThisType--;
|
|
}
|
|
|
|
//Is it available
|
|
if( availableWeaponsOfThisType > 0 )
|
|
{
|
|
//Do we have ammo for it
|
|
if( owner->inventory.HasAmmo( owner->spawnArgs.GetString( va( "def_weapon%d", weapNum ) )) || owner->spawnArgs.GetBool( va( "weapon%d_allowempty", weapNum ) ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
weaponToggleIndex++;
|
|
if( weaponToggleIndex >= weaponToggle->toggleList.Num() )
|
|
{
|
|
weaponToggleIndex = 0;
|
|
}
|
|
}
|
|
weaponToggle->lastUsed = weaponToggleIndex;
|
|
num = weaponToggle->toggleList[weaponToggleIndex];
|
|
}
|
|
|
|
// Is there an actual weapon for this weapon slot?
|
|
weap = owner->spawnArgs.GetString( va( "def_weapon%d", num ) );
|
|
if( !weap[ 0 ] )
|
|
{
|
|
gameLocal.Printf( "Invalid weapon\n" );
|
|
return;
|
|
}
|
|
|
|
// Carl: dual wielding
|
|
int availableWeaponsOfThisType = 0;
|
|
if( num == owner->weapon_fists )
|
|
{
|
|
if( owner->inventory.weapons & ( 1 << num ) )
|
|
availableWeaponsOfThisType++;
|
|
}
|
|
else
|
|
{
|
|
if( owner->inventory.weapons & ( 1 << num ) )
|
|
availableWeaponsOfThisType++;
|
|
if( owner->inventory.duplicateWeapons & ( 1 << num ) )
|
|
availableWeaponsOfThisType++;
|
|
// Carl: skip weapons in the holster unless we have a duplicate, TODO make it optional
|
|
if( num == owner->holsteredWeapon )
|
|
availableWeaponsOfThisType--;
|
|
// Carl: skip weapons in the other hand unless we have a duplicate (dual wielding)
|
|
if( num == owner->hands[ 1 - whichHand ].idealWeapon )
|
|
availableWeaponsOfThisType--;
|
|
}
|
|
|
|
if( force || availableWeaponsOfThisType > 0 )
|
|
{
|
|
if( !owner->inventory.HasAmmo( weap) && !owner->spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) )
|
|
{
|
|
return;
|
|
}
|
|
if( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( owner->spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) )
|
|
{
|
|
weap = owner->spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
|
|
if( !owner->inventory.HasAmmo( weap ) && !owner->spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) )
|
|
{
|
|
return;
|
|
}
|
|
num = previousWeapon;
|
|
}
|
|
else if( ( owner->weapon_pda >= 0 ) && ( num == owner->weapon_pda ) && ( owner->inventory.pdas.Num() == 0 ) )
|
|
{
|
|
|
|
if ( game->isVR )
|
|
{
|
|
//GivePDA( NULL, NULL, false ); // hack to allow the player to change system settings in the mars city level before the PDA is given by the receptionist.
|
|
//idealWeapon = num;
|
|
}
|
|
else
|
|
{
|
|
owner->ShowTip( owner->spawnArgs.GetString( "text_infoTitle" ), owner->spawnArgs.GetString( "text_noPDA" ), true );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we can't we dual-wield it?
|
|
if( !owner->CanDualWield( num ) && !owner->CanDualWield( owner->hands[1 - whichHand].currentWeapon ) && !owner->CanDualWield( owner->hands[1 - whichHand].idealWeapon ) )
|
|
{
|
|
// if we can't automagically empty our other hand, do nothing
|
|
if( vr_mustEmptyHands.GetBool() )
|
|
return;
|
|
// otherwise, first automagically empty our other hand
|
|
if( owner->CanDualWield( owner->weapon_fists ) )
|
|
owner->hands[1 - whichHand].SelectWeapon( owner->weapon_fists, true, true );
|
|
else
|
|
owner->hands[1 - whichHand].SelectWeapon( weapon_empty_hand, true, true );
|
|
}
|
|
|
|
// If we made it all this way, we want weapon "num" and we can switch to it
|
|
idealWeapon = num;
|
|
owner->UpdateHudWeapon( whichHand );
|
|
}
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "After SelectWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idPlayerHand::SetControllerShake
|
|
========================
|
|
*/
|
|
void idPlayerHand::SetControllerShake( float highMagnitude, int highDuration, float lowMagnitude, int lowDuration )
|
|
{
|
|
// the main purpose of having these buffer is so multiple, individual shake events can co-exist with each other,
|
|
// for instance, a constant low rumble from the chainsaw when it's idle and a harsh rumble when it's being used.
|
|
|
|
// find active buffer with similar magnitude values
|
|
int activeBufferWithSimilarMags = -1;
|
|
int inactiveBuffer = -1;
|
|
for( int i = 0; i < MAX_SHAKE_BUFFER; i++ )
|
|
{
|
|
if( gameLocal.GetTime() <= controllerShakeHighTime[i] || gameLocal.GetTime() <= controllerShakeLowTime[i] )
|
|
{
|
|
if( idMath::Fabs( highMagnitude - controllerShakeHighMag[i] ) <= 0.1f && idMath::Fabs( lowMagnitude - controllerShakeLowMag[i] ) <= 0.1f )
|
|
{
|
|
activeBufferWithSimilarMags = i;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( inactiveBuffer == -1 )
|
|
{
|
|
inactiveBuffer = i; // first, inactive buffer..
|
|
}
|
|
}
|
|
}
|
|
|
|
if( activeBufferWithSimilarMags > -1 )
|
|
{
|
|
// average the magnitudes and adjust the time
|
|
controllerShakeHighMag[activeBufferWithSimilarMags] += highMagnitude;
|
|
controllerShakeHighMag[activeBufferWithSimilarMags] *= 0.5f;
|
|
|
|
controllerShakeLowMag[activeBufferWithSimilarMags] += lowMagnitude;
|
|
controllerShakeLowMag[activeBufferWithSimilarMags] *= 0.5f;
|
|
|
|
controllerShakeHighTime[activeBufferWithSimilarMags] = gameLocal.GetTime() + highDuration;
|
|
controllerShakeLowTime[activeBufferWithSimilarMags] = gameLocal.GetTime() + lowDuration;
|
|
//controllerShakeTimeGroup = gameLocal.selectedGroup;
|
|
return;
|
|
}
|
|
|
|
if( inactiveBuffer == -1 )
|
|
{
|
|
inactiveBuffer = 0; // FIXME: probably want to use the oldest buffer..
|
|
}
|
|
|
|
controllerShakeHighMag[inactiveBuffer] = highMagnitude;
|
|
controllerShakeLowMag[inactiveBuffer] = lowMagnitude;
|
|
controllerShakeHighTime[inactiveBuffer] = gameLocal.GetTime() + highDuration;
|
|
controllerShakeLowTime[inactiveBuffer] = gameLocal.GetTime() + lowDuration;
|
|
//controllerShakeTimeGroup = gameLocal.selectedGroup;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz idPlayerHand::TrackWeaponDirection
|
|
keep track of weapon movement to determine direction of motion
|
|
==============
|
|
*/
|
|
void idPlayerHand::TrackWeaponDirection( idVec3 origin )
|
|
{
|
|
frameNum += 1;
|
|
if ( frameNum > 9 ) frameNum = 0;
|
|
frameTime[frameNum] = gameLocal.GetTime();
|
|
position[frameNum] = origin;
|
|
|
|
startFrameNum = frameNum - 5;
|
|
if ( startFrameNum < 0 ) startFrameNum = 9 - frameNum;
|
|
|
|
timeDelta = frameTime[frameNum] - frameTime[startFrameNum];
|
|
if ( timeDelta == 0 ) timeDelta = 1;
|
|
|
|
throwDirection = position[frameNum] - position[startFrameNum];
|
|
throwVelocity = ( throwDirection.Length() / timeDelta ) * 1000;
|
|
}
|
|
|
|
bool idPlayerHand::contextToggleVirtualGrab()
|
|
{
|
|
return false;
|
|
}
|
|
bool idPlayerHand::holdingFlashlight()
|
|
{
|
|
// Carl: weapon must be set to fist or empty hand when we're using the flashlight
|
|
if( whichHand == vr_weaponHand.GetInteger() || commonVr->currentFlashlightPosition != FLASHLIGHT_HAND || !owner || !owner->flashlight || !owner->flashlight->IsLinked()
|
|
|| owner->spectating || !owner->weaponEnabled || owner->hiddenWeapon || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool idPlayerHand::holdingWeapon()
|
|
{
|
|
if( !controllingWeapon() )
|
|
return false;
|
|
int w = owner->GetCurrentWeaponSlot();
|
|
return w != owner->weapon_fists && w != owner->weapon_soulcube;
|
|
}
|
|
|
|
bool idPlayerHand::floatingWeapon()
|
|
{
|
|
if( !controllingWeapon() )
|
|
return false;
|
|
int w = owner->GetCurrentWeaponSlot();
|
|
return w == owner->weapon_soulcube;
|
|
}
|
|
|
|
bool idPlayerHand::controllingWeapon()
|
|
{
|
|
if( currentWeapon < 0 || !owner || !weapon || weapon->IdentifyWeapon() == WEAPON_PDA || holdingFlashlight()
|
|
|| owner->spectating || !owner->weaponEnabled || owner->hiddenWeapon || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool idPlayerHand::holdingPDA()
|
|
{
|
|
return (weapon && weapon->IdentifyWeapon() == WEAPON_PDA); // Carl: Is this the only way? Or do I need to check for forced pda?
|
|
}
|
|
|
|
bool idPlayerHand::holdingPhysics()
|
|
{
|
|
// Carl todo
|
|
return false;
|
|
}
|
|
|
|
bool idPlayerHand::holdingItem()
|
|
{
|
|
// Carl todo
|
|
return false;
|
|
}
|
|
|
|
bool idPlayerHand::holdingSomethingDroppable()
|
|
{
|
|
return holdingWeapon() || holdingFlashlight() || holdingPDA() || holdingPhysics() || holdingItem();
|
|
}
|
|
|
|
bool idPlayerHand::isOverMountedFlashlight()
|
|
{
|
|
if( !owner || !owner->flashlight.IsValid() || owner->spectating || !owner->weaponEnabled || owner->hiddenWeapon || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
return false;
|
|
else if( handSlot == SLOT_FLASHLIGHT_HEAD )
|
|
return commonVr->currentFlashlightPosition == FLASHLIGHT_HEAD;
|
|
else if( handSlot == SLOT_FLASHLIGHT_SHOULDER )
|
|
return commonVr->currentFlashlightPosition == FLASHLIGHT_BODY;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool idPlayerHand::tooFullToInteract()
|
|
{
|
|
return (vr_mustEmptyHands.GetBool() && ( holdingWeapon() || holdingFlashlight() || holdingPDA() || holdingPhysics() || holdingItem() )) || !handExists();
|
|
}
|
|
|
|
bool idPlayerHand::handExists()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool idPlayerHand::startVirtualGrab()
|
|
{
|
|
virtualGrabDown = true;
|
|
return true;
|
|
}
|
|
|
|
void idPlayerHand::DropWeapon( bool died )
|
|
{
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "Before DropWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
idVec3 forward, up;
|
|
|
|
assert( !gameLocal.isClient );
|
|
|
|
if( !owner || owner->spectating || weaponGone || weapon.GetEntity() == NULL )
|
|
return;
|
|
|
|
if( handExists() )
|
|
return;
|
|
|
|
idWeapon* weap = weapon.GetEntity();
|
|
|
|
if( !weap || ( !died && !weap->IsReady() ) || weap->IsReloading() )
|
|
return;
|
|
|
|
// ammoavailable is how many shots we can fire
|
|
// inclip is which amount is in clip right now
|
|
int ammoavailable = weap->AmmoAvailable();
|
|
int inclip = weap->AmmoInClip();
|
|
|
|
// don't drop a grenade if we have none left
|
|
if( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weap->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ammoavailable += inclip;
|
|
|
|
// 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 < 0 ) )
|
|
{
|
|
common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
|
|
return;
|
|
}
|
|
idEntity* item = NULL;
|
|
// Carl: todo throwing a weapon when you let go of it (also dual wielding)
|
|
if( died )
|
|
{
|
|
// ain't gonna throw you no weapon if I'm dead
|
|
item = weap->DropItem( vec3_origin, 0, -1, died );
|
|
}
|
|
else
|
|
{
|
|
// Carl If we drop it straight down, we'll automatically pick it up immediately (if we're alive), so throw it forward a bit.
|
|
owner->viewAngles.ToVectors( &forward, NULL, &up );
|
|
item = weap->DropItem( 150.0f * forward + 50.0f * up, 500, -1, died );
|
|
}
|
|
if( !item )
|
|
return;
|
|
// set the appropriate ammo in the dropped object
|
|
if( !died )
|
|
ammoavailable = inclip; // Carl: TODO work out what these keys do, don't put ALL our ammo in the drop
|
|
const idKeyValue* keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
|
|
if( keyval )
|
|
{
|
|
item->spawnArgs.SetInt( keyval->GetKey(), 0 );// , ammoavailable );
|
|
idStr inclipKey = keyval->GetKey();
|
|
inclipKey.Insert( "inclip_", 4 );
|
|
inclipKey.Insert( va( "%.2d", currentWeapon ), 11 );
|
|
item->spawnArgs.SetInt( inclipKey, inclip );
|
|
}
|
|
if( !died )
|
|
{
|
|
bool isDuplicate = false;
|
|
if( !isDuplicate )
|
|
{
|
|
// remove from our local inventory completely
|
|
owner->inventory.Drop( owner->spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
|
|
weap->ResetAmmoClip();
|
|
SelectWeapon( owner->weapon_fists, true, true );
|
|
weap->WeaponStolen();
|
|
weaponGone = true;
|
|
}
|
|
else
|
|
{
|
|
// just remove from our hand
|
|
}
|
|
}
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "After DropWeapon():\n" );
|
|
debugPrint();
|
|
}
|
|
}
|
|
|
|
bool idPlayerHand::releaseVirtualGrab()
|
|
{
|
|
if( holdingWeapon() )
|
|
{
|
|
virtualGrabDown = false;
|
|
if( vr_gripMode.GetInteger() != VR_GRIP_DEAD_AND_BURIED )
|
|
DropWeapon( false );
|
|
else
|
|
{
|
|
// return weapon to holster it was drawn from
|
|
// if that holster is full, move its contents to inventory
|
|
// if its contents can't be stored in the inventory, then store the ammo but drop the weapon
|
|
}
|
|
return true;
|
|
}
|
|
else if( holdingFlashlight() )
|
|
{
|
|
// now dropping the flashlight moves it to your inventory
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_INVENTORY;
|
|
//vr_flashlightMode.SetInteger( FLASHLIGHT_BODY );
|
|
//vr_flashlightMode.SetModified();
|
|
return true;
|
|
}
|
|
else if( holdingPDA() )
|
|
{
|
|
// for now, dropping any PDA will put it away
|
|
return true;
|
|
}
|
|
else if( holdingItem() )
|
|
{
|
|
// either use the item
|
|
// or drop the item
|
|
// depending on the settings and where our hand is
|
|
return true;
|
|
}
|
|
else if( holdingPhysics() )
|
|
{
|
|
// throw the physics object
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void idPlayerHand::debugPrint()
|
|
{
|
|
if( !vr_debugHands.GetBool() )
|
|
return;
|
|
idStr s = "";
|
|
if( vr_weaponHand.GetInteger() == whichHand )
|
|
{
|
|
s = "(Weapon hand) ";
|
|
}
|
|
else if( vr_weaponHand.GetInteger() < 0 || vr_weaponHand.GetInteger() >= 2 )
|
|
{
|
|
s = "(INVALID vr_weaponHand) ";
|
|
common->Printf( "vr_weaponHand = %d\n", vr_weaponHand.GetInteger() );
|
|
}
|
|
else if( commonVr->currentFlashlightPosition == FLASHLIGHT_HAND )
|
|
{
|
|
s = "(Flashlight in hand) ";
|
|
}
|
|
else if( commonVr->currentFlashlightMode == FLASHLIGHT_HAND )
|
|
{
|
|
s = "(Flashlight hand) ";
|
|
}
|
|
if (whichHand == HAND_RIGHT)
|
|
common->Printf( "*** Right Hand %s***\n", s.c_str() );
|
|
else if( whichHand == HAND_LEFT )
|
|
common->Printf( "*** Left Hand %s***\n", s.c_str() );
|
|
else
|
|
common->Printf( "*** Hand %d %s***\n", whichHand, s.c_str() );
|
|
if( !this->owner )
|
|
common->Printf( " ERROR: No owner!\n" );
|
|
else
|
|
common->Printf( " owner = '%s'\n", owner->name.c_str() );
|
|
if( !handExists() )
|
|
common->Printf( " doesn't exist!\n" );
|
|
|
|
// gestures
|
|
common->Printf( " in holster slot %d\n", handSlot );
|
|
s = "";
|
|
if( grabbingWorld )
|
|
s += "grip ";
|
|
if( virtualGrabDown )
|
|
s += "vGrab ";
|
|
if( triggerDown )
|
|
s += "trigger ";
|
|
if( thumbDown )
|
|
s += "thumb ";
|
|
common->Printf( " buttons = %s", s.c_str() );
|
|
s = "";
|
|
if( oldGrabbingWorld )
|
|
s += "grip ";
|
|
if( oldVirtualGrabDown )
|
|
s += "vGrab ";
|
|
if( oldTriggerDown )
|
|
s += "trigger ";
|
|
if( oldFlashlightTriggerDown )
|
|
s += "flashlight ";
|
|
if( oldThumbDown )
|
|
s += "thumb ";
|
|
common->Printf( ", old = %s\n", s.c_str() );
|
|
|
|
// weapon
|
|
s = "";
|
|
if( isTheDuplicate )
|
|
s += " (duplicate copy)";
|
|
if( weaponGone )
|
|
s += ", gone";
|
|
if( currentWeapon == idealWeapon )
|
|
common->Printf( " current/ideal weapon = %s = %d%s\n", GetCurrentWeaponString().c_str(), currentWeapon, s.c_str() );
|
|
else
|
|
{
|
|
common->Printf( " currentWeapon = %s = %d%s\n", GetCurrentWeaponString().c_str(), currentWeapon, s.c_str() );
|
|
common->Printf( " idealWeapon = %d, weaponSwitchTime (unused?) = %dms\n", idealWeapon, weaponSwitchTime - gameLocal.time );
|
|
}
|
|
common->Printf( " previousWeapon = %d\n", previousWeapon );
|
|
idWeapon* weap = weapon;
|
|
if( weap )
|
|
{
|
|
if( weap->weaponDef )
|
|
{
|
|
common->Printf( " weapon (%s) = %s = %d\n", weap->GetName(), weap->weaponDef->GetName(), weap->IdentifyWeapon() );
|
|
}
|
|
else
|
|
{
|
|
common->Printf( " weapon (%s): weaponDef = NULL, IdentifyWeapon = %d\n", weap->GetName(), weap->IdentifyWeapon() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
common->Printf( " weapon = NULL\n" );
|
|
}
|
|
// sight
|
|
common->Printf( " laser sight: active=%d, handle=%d\n", laserSightActive, laserSightHandle );
|
|
common->Printf( " crosshair: lastMode=%d, handle=%d\n", lastCrosshairMode, crosshairHandle );
|
|
// PDA
|
|
common->Printf( " PDA (was %d): fixed=%d (last=%d), ", PDAfixed, lastPdaFixed, wasPDA );
|
|
// Functions
|
|
common->Printf( " holdingWeapon=%d, floatingWeapon=%d, controllingWeapon=%d, tooFullToInteract=%d\n", holdingWeapon(), floatingWeapon(), controllingWeapon(), tooFullToInteract() );
|
|
common->Printf( " holdingFlashlight=%d, isOverMountedFlashlight=%d, holdingPDA=%d, holdingPhysics=%d, holdingItem=%d, holdingSomethingDroppable=%d\n", holdingFlashlight(), isOverMountedFlashlight(), holdingPDA(), holdingPhysics(), holdingItem(), holdingSomethingDroppable() );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::StealWeapon
|
|
steal the target player's current weapon
|
|
=================
|
|
*/
|
|
void idPlayer::StealWeapon( idPlayer *player ) {
|
|
assert( !gameLocal.isClient );
|
|
|
|
// make sure there's something to steal
|
|
int h = player->GetBestWeaponHandToSteal( this );
|
|
//idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon );
|
|
idWeapon* player_weapon = player->GetWeaponInHand( h );
|
|
if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
|
|
return;
|
|
}
|
|
// steal - we need to effectively force the other player to abandon his weapon
|
|
int newweap = player->hands[ h ].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->AmmoAvailable();
|
|
int inclip = player_weapon->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->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->hands[ h ].weaponGone = true;
|
|
|
|
// give weapon, setup the ammo count
|
|
int myhand = vr_weaponHand.GetInteger();
|
|
Give( "weapon", weapon_classname , -1);
|
|
ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
|
|
hands[ myhand ].idealWeapon = newweap;
|
|
inventory.ammo[ ammo_i ] += ammoavailable;
|
|
inventory.clip[ newweap ] = inclip;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::ActiveGui
|
|
===============
|
|
*/
|
|
idUserInterface *idPlayer::ActiveGui( void ) {
|
|
if ( objectiveSystemOpen ) {
|
|
return objectiveSystem;
|
|
}
|
|
|
|
return focusUI;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Weapon_Combat
|
|
===============
|
|
*/
|
|
void idPlayer::Weapon_Combat( void ) {
|
|
if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
|
|
commonVr->ForceChaperone(0, false);
|
|
return;
|
|
}
|
|
|
|
// Carl: check if we're reloading either weapon
|
|
bool reloading = false;
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
hands[h].weapon->RaiseWeapon();
|
|
if( hands[h].weapon->IsReloading() )
|
|
{
|
|
if( !AI_RELOAD )
|
|
{
|
|
reloading = true;
|
|
AI_RELOAD = true;
|
|
SetState( "ReloadWeapon" );
|
|
UpdateScript();
|
|
}
|
|
}
|
|
}
|
|
if( !reloading )
|
|
AI_RELOAD = false;
|
|
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
// Carl: If we're trying to change to the soul cube, but the soul cube is already flying towards the enemy, stop changing weapon
|
|
if( hands[h].idealWeapon == weapon_soulcube && soulCubeProjectile != NULL )
|
|
{
|
|
hands[h].idealWeapon = hands[h].currentWeapon;
|
|
}
|
|
// Carl: Otherwise, change to the chosen weapon before we fire
|
|
// Carl: TODO dual wielding, currently this only does the vr_weaponHand hand
|
|
if( hands[ h ].idealWeapon != hands[ h ].currentWeapon && hands[ h ].idealWeapon < MAX_WEAPONS )
|
|
{
|
|
// multiplayer stuff
|
|
if( weaponCatchup )
|
|
{
|
|
assert( gameLocal.isClient );
|
|
|
|
hands[h].currentWeapon = hands[h].idealWeapon;
|
|
weaponGone = false;
|
|
hands[h].animPrefix = spawnArgs.GetString( va( "def_weapon%d", hands[h].currentWeapon ) );
|
|
// Carl: Dual Wielding, we need to choose whether this is the original weapon or the duplicate
|
|
// Carl: if we're already holding the same weapon in the other hand, use the opposite copy
|
|
// Carl: otherwise use whichever copy has more ammo in it
|
|
if( hands[h].currentWeapon == hands[1 - h].currentWeapon )
|
|
hands[h].isTheDuplicate = !hands[1 - h].isTheDuplicate;
|
|
else
|
|
hands[h].isTheDuplicate = inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, true ) > inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, false );
|
|
//hands[h].weapon->GetWeaponDef( hands[h].animPrefix, inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, hands[h].isTheDuplicate ) );
|
|
hands[h].animPrefix.Strip( "weapon_" );
|
|
|
|
hands[h].weapon->NetCatchup();
|
|
const function_t* newstate = GetScriptFunction( "NetCatchup" );
|
|
if( newstate )
|
|
{
|
|
SetState( newstate );
|
|
UpdateScript();
|
|
}
|
|
weaponCatchup = false;
|
|
}
|
|
else
|
|
{
|
|
if( hands[ h ].weapon->IsReady() )
|
|
{
|
|
hands[ h ].weapon->PutAway();
|
|
}
|
|
|
|
if( hands[ h ].weapon->IsHolstered() )
|
|
{
|
|
assert( hands[h].idealWeapon >= 0 );
|
|
assert( hands[h].idealWeapon < MAX_WEAPONS );
|
|
|
|
if( hands[h].currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", hands[h].currentWeapon ) ) )
|
|
{
|
|
hands[h].previousWeapon = hands[h].currentWeapon;
|
|
}
|
|
hands[h].currentWeapon = hands[h].idealWeapon;
|
|
weaponGone = false;
|
|
hands[h].weaponGone = false;
|
|
hands[h].animPrefix = spawnArgs.GetString( va( "def_weapon%d", hands[h].currentWeapon ) );
|
|
// Carl: Dual Wielding, we need to choose whether this is the original weapon or the duplicate
|
|
// Carl: if we're already holding the same weapon in the other hand, use the opposite copy
|
|
// Carl: otherwise use whichever copy has more ammo in it
|
|
if( hands[h].currentWeapon == hands[1 - h].currentWeapon )
|
|
hands[h].isTheDuplicate = !hands[1 - h].isTheDuplicate;
|
|
else
|
|
hands[h].isTheDuplicate = inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, true ) > inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, false );
|
|
hands[h].weapon->GetWeaponDef( hands[h].animPrefix, inventory.GetClipAmmoForWeapon( hands[h].currentWeapon, hands[h].isTheDuplicate ) );
|
|
hands[h].animPrefix.Strip( "weapon_" );
|
|
|
|
hands[h].weapon->Raise();
|
|
if( hands[h].holdingFlashlight() )
|
|
PlayAnim( h ? ANIMCHANNEL_LEFTHAND : ANIMCHANNEL_RIGHTHAND, "flashlight_idle" );
|
|
else
|
|
PlayAnim( h ? ANIMCHANNEL_LEFTHAND : ANIMCHANNEL_RIGHTHAND, "idle" );
|
|
}
|
|
}
|
|
}
|
|
else // Carl: If we're already using the chosen weapon
|
|
{
|
|
weaponGone = false; // if you drop and re-get weap, you may miss the = false above
|
|
hands[ h ].weaponGone = false;
|
|
if( hands[ h ].weapon->IsHolstered() )
|
|
{
|
|
if( !hands[ h ].weapon->AmmoAvailable() )
|
|
{
|
|
// weapons can switch automatically if they have no more ammo
|
|
hands[ h ].NextBestWeapon();
|
|
}
|
|
else
|
|
{
|
|
risingWeaponHand = h;
|
|
hands[ risingWeaponHand ].weapon->Raise();
|
|
state = GetScriptFunction( "RaiseWeapon" );
|
|
if( state )
|
|
{
|
|
SetState( state );
|
|
}
|
|
if( !hands[h].holdingFlashlight() )
|
|
PlayAnim( h ? ANIMCHANNEL_LEFTHAND : ANIMCHANNEL_RIGHTHAND, "idle" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for attack
|
|
AI_WEAPON_FIRED = false;
|
|
if ( !influenceActive ) {
|
|
// Carl Dual wielding - check both hands for weapons being fired
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
bool pullingTrigger = hands[ h ].triggerDown;
|
|
// Check if we're actually turning our helmet/armour-mounted light on or off instead of firing
|
|
if( pullingTrigger && hands[ h ].isOverMountedFlashlight() && !hands[ h ].tooFullToInteract() && !hands[h].oldFlashlightTriggerDown )
|
|
{
|
|
pullingTrigger = false;
|
|
if( flashlight->lightOn )
|
|
FlashlightOff();
|
|
else
|
|
FlashlightOn();
|
|
hands[ h ].oldFlashlightTriggerDown = true;
|
|
}
|
|
// Fire weapon
|
|
if( (pullingTrigger && !hands[ h ].oldFlashlightTriggerDown ) || ( usercmd.buttons & BUTTON_ATTACK ) )
|
|
{
|
|
//common->Printf( "trigger down\n" );
|
|
if( hands[ h ].controllingWeapon() && !hands[h].weaponGone && !weaponGone )
|
|
{
|
|
FireWeapon( h, GetWeaponInHand( h ) );
|
|
if( !hands[h].oldTriggerDown )
|
|
PlayAnim( h ? ANIMCHANNEL_LEFTHAND : ANIMCHANNEL_RIGHTHAND, "fire1" );
|
|
}
|
|
}
|
|
else if( (hands[ h ].oldTriggerDown && !hands[ h ].oldFlashlightTriggerDown) || oldButtons & BUTTON_ATTACK )
|
|
{
|
|
//common->Printf( "old trigger down\n" );
|
|
AI_ATTACK_HELD = false;
|
|
GetWeaponInHand( h )->EndAttack();
|
|
PlayAnim( h ? ANIMCHANNEL_LEFTHAND : ANIMCHANNEL_RIGHTHAND, "idle" );
|
|
}
|
|
// remember the old state
|
|
if( hands[ h ].oldFlashlightTriggerDown && !hands[ h ].triggerDown )
|
|
{
|
|
hands[ h ].oldFlashlightTriggerDown = false;
|
|
}
|
|
hands[ h ].oldTriggerDown = hands[ h ].triggerDown;
|
|
}
|
|
}
|
|
|
|
// update our ammo clip in our inventory
|
|
// update our ammo clip in our inventory
|
|
for( int h = 0; h < 2; h++ ) {
|
|
if ((hands[h].currentWeapon >= 0) && (hands[h].currentWeapon < MAX_WEAPONS)) {
|
|
inventory.clip[ hands[h].currentWeapon ] = hands[h].weapon->AmmoInClip();
|
|
if ( hud && ( hands[h].currentWeapon == hands[h].idealWeapon ) ) {
|
|
UpdateHudAmmo(hud, h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInventory::GetClipAmmoForWeapon
|
|
===============
|
|
*/
|
|
int idInventory::GetClipAmmoForWeapon( const int weapon, const bool duplicate ) const
|
|
{
|
|
if( duplicate )
|
|
return clipDuplicate[weapon];
|
|
else
|
|
return clip[weapon];
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Weapon_NPC
|
|
===============
|
|
*/
|
|
void idPlayer::Weapon_NPC( void ) {
|
|
if( hands[0].idealWeapon != hands[0].currentWeapon || hands[ 1 ].idealWeapon != hands[ 1 ].currentWeapon )
|
|
{
|
|
Weapon_Combat();
|
|
}
|
|
StopFiring();
|
|
for ( int h=0; h<2; h++ )
|
|
hands[h].weapon->LowerWeapon();
|
|
|
|
int talkButtons = 0;
|
|
talkButtons |= BUTTON_ATTACK | BUTTON_USE;
|
|
bool wasDown = ( oldButtons & talkButtons ) != 0;
|
|
bool isDown = ( usercmd.buttons & talkButtons ) != 0;
|
|
if ( isDown && !wasDown )
|
|
{
|
|
buttonMask |= BUTTON_ATTACK;
|
|
focusCharacter->ListenTo( this );
|
|
}
|
|
else if ( wasDown && !isDown )
|
|
{
|
|
focusCharacter->TalkTo( this );
|
|
}
|
|
|
|
/*if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
|
|
buttonMask |= BUTTON_ATTACK;
|
|
focusCharacter->TalkTo( this );
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_WeaponAvailable
|
|
==================
|
|
*/
|
|
void idPlayer::Event_WeaponAvailable( const char* name )
|
|
{
|
|
idThread::ReturnInt( WeaponAvailable( name ) ? 1 : 0 );
|
|
}
|
|
|
|
bool idPlayer::WeaponAvailable( const char* name )
|
|
{
|
|
for( int i = 0; i < MAX_WEAPONS; i++ )
|
|
{
|
|
if( inventory.weapons & ( 1 << i ) )
|
|
{
|
|
const char* weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
|
|
if( !idStr::Cmp( weap, name ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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 ) {
|
|
|
|
if ( !objectiveSystemOpen ) {
|
|
if( hands[ 0 ].idealWeapon != hands[ 0 ].currentWeapon || hands[ 1 ].idealWeapon != hands[ 1 ].currentWeapon )
|
|
{
|
|
Weapon_Combat();
|
|
}
|
|
StopFiring();
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
hands[ h ].weapon->LowerWeapon();
|
|
hands[ h ].weapon->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
hands[ h ].weapon->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
}
|
|
}
|
|
|
|
// 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 ) {
|
|
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( !hands[ 0 ].weapon.GetEntity()->IsWorldModelReady() || !hands[ 1 ].weapon.GetEntity()->IsWorldModelReady() ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// always make sure the weapons are correctly setup before accessing them
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( !hands[h].weapon->IsLinked() )
|
|
{
|
|
if( hands[ h ].idealWeapon == -1 )
|
|
hands[ h ].idealWeapon = 0;
|
|
if( hands[ h ].idealWeapon != -1 )
|
|
{
|
|
animPrefix = spawnArgs.GetString( va( "def_weapon%d", hands[h].idealWeapon) );
|
|
// Carl: Dual Wielding, we need to choose whether this is the original weapon or the duplicate
|
|
// if we're already holding the same weapon in the other hand, use the opposite copy
|
|
// otherwise use whichever copy has more ammo in it
|
|
if( hands[1-h].weapon->IsLinked() && hands[h].idealWeapon == hands[1 - h].idealWeapon )
|
|
hands[h].isTheDuplicate = !hands[1 - h].isTheDuplicate;
|
|
else
|
|
hands[h].isTheDuplicate = inventory.GetClipAmmoForWeapon( hands[h].idealWeapon, true ) > inventory.GetClipAmmoForWeapon( hands[h].idealWeapon, false );
|
|
int ammoInClip = inventory.GetClipAmmoForWeapon( hands[ h ].idealWeapon, hands[h].isTheDuplicate );
|
|
|
|
hands[ h ].weapon->GetWeaponDef( animPrefix, inventory.clip[ hands[ h ].idealWeapon ] );
|
|
assert( hands[ h ].weapon->IsLinked() );
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
|
|
HideTip();
|
|
}
|
|
|
|
if ( g_dragEntity.GetBool() ) {
|
|
StopFiring();
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].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 ) {
|
|
if( !game->isVR || commonVr->handInGui == false )
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon->LowerWeapon(); // KOZ FIXME HIDE WEAPon
|
|
}
|
|
} else {
|
|
for (int h = 0; h < 2; h++)
|
|
hands[h].weapon->GetRenderEntity()->suppressShadowInViewID = 0;
|
|
}
|
|
|
|
if( game->isVR && commonVr->handInGui )
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
}
|
|
|
|
// update weapon state, particles, dlights, etc
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon->PresentWeapon( CanShowWeaponViewmodel(), h );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::SpectateFreeFly
|
|
===============
|
|
*/
|
|
void idPlayer::SpectateFreeFly( bool force ) {
|
|
idPlayer *player;
|
|
idVec3 newOrig;
|
|
idVec3 spawn_origin;
|
|
idAngles spawn_angles;
|
|
|
|
player = gameLocal.GetClientByNum( spectator );
|
|
if ( force || gameLocal.time > lastSpectateChange ) {
|
|
spectator = entityNumber;
|
|
if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
|
|
newOrig = player->GetPhysics()->GetOrigin();
|
|
if ( player->physicsObj.IsCrouching() ) {
|
|
newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
|
|
} else {
|
|
newOrig[ 2 ] += pm_normalviewheight.GetFloat();
|
|
}
|
|
newOrig[ 2 ] += SPECTATE_RAISE;
|
|
idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
|
|
idVec3 start = player->GetPhysics()->GetOrigin();
|
|
start[2] += pm_spectatebbox.GetFloat() * 0.5f;
|
|
trace_t t;
|
|
// assuming spectate bbox is inside stand or crouch box
|
|
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();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Koz
|
|
idPlayer::UpdateTeleportAim
|
|
|
|
equation for parabola : y = y0 + vy0 * t - .5 * g * t^2
|
|
x = x0 + vx0 * t
|
|
*/
|
|
|
|
void idPlayer::UpdateTeleportAim()// idVec3 beamOrigin, idMat3 beamAxis )// idVec3 p0, idVec3 v0, idVec3 a, float dist, int points, idVec3 hitLocation, idVec3 hitNormal, float timeToHit )
|
|
{
|
|
// teleport target is a .md5 model
|
|
// model has 3 components:
|
|
// the aiming beam comprised of a ribbon with 23 segments/24 ( joints teleportBeamJoint[ 0 - 23 ] )
|
|
// the telepad itself (big circle with the fx and cylinder - joint teleportPadJoint )
|
|
// the center aiming dot ( terminates the beam, shown whenever beam is on - joint teleportCenterPadJoint )
|
|
// the origin of the model should be set to the starting point of the aiming beam
|
|
// teleportBeamJoint[0] should also be set to the origin
|
|
// teleportBeamJoint[1 - 22] trace the arc
|
|
// teleportBeamJoint[23] and teleportCenterPadJoint should be set to the end position of the beam
|
|
// teleportPadJoint should be set to the position of the teleport target ( can be different from beam if aim assist is active )
|
|
|
|
const int slTime = 200; // 200ms, remove vr_teleportSlerpTime.GetFloat();
|
|
|
|
const float grav = 9.81f * 39.3701f;
|
|
|
|
static bool pleaseDuck = false; // Low Headroom Please Duck
|
|
|
|
float numPoints = vr_teleportMaxPoints.GetFloat();// 24;
|
|
float vel = vr_teleportVel.GetFloat();
|
|
float dist = vr_teleportDist.GetFloat();
|
|
|
|
trace_t traceResults;
|
|
idVec3 last = vec3_zero;
|
|
idVec3 next = vec3_zero;
|
|
idVec3 endPos = vec3_zero;
|
|
|
|
idVec3 up = idVec3( 0, 0, 1 );
|
|
|
|
idMat3 padAxis = mat3_identity;
|
|
float t = 0;
|
|
|
|
idMat3 forward = mat3_identity;
|
|
float beamAngle = 0.0f;
|
|
float vx, vz = 0.0f;
|
|
float z0 = 0.0f;
|
|
float tDisX = 0.0f;
|
|
float zDelt, xDelt = 0.0f;
|
|
|
|
idVec2 st, en, df = vec2_zero;
|
|
idVec3 jpos = vec3_zero;
|
|
|
|
static bool isShowing = false;
|
|
static bool wasShowing = false;
|
|
|
|
static idVec3 beamOrigin = vec3_zero;
|
|
static idMat3 beamAxis = mat3_identity;
|
|
|
|
bool showTeleport = ( vr_teleport.GetInteger() > 1 || ( !commonVr->VR_USE_MOTION_CONTROLS && vr_teleport.GetInteger() > 0 ) ) && commonVr->teleportButtonCount != 0;
|
|
static bool lastShowTeleport = false;
|
|
|
|
if ( !lastShowTeleport )
|
|
{
|
|
isShowing = false;
|
|
}
|
|
|
|
lastShowTeleport = showTeleport;
|
|
|
|
pleaseDuck = false;
|
|
|
|
if ( !showTeleport || !GetTeleportBeamOrigin( beamOrigin, beamAxis ) )
|
|
{
|
|
if (vr_teleport.GetInteger() != 1)
|
|
aimValidForTeleport = false;
|
|
teleportTarget->Hide();
|
|
return;
|
|
}
|
|
|
|
aimValidForTeleport = false;
|
|
|
|
forward = idAngles(0.0f, beamAxis.ToAngles().yaw, 0.0f).ToMat3();
|
|
teleportTarget->SetAxis( forward );
|
|
|
|
beamAngle = idMath::ClampFloat( -65.0f, 65.0f, beamAxis.ToAngles().pitch );
|
|
|
|
dist *= idMath::Cos( DEG2RAD( beamAngle ) ); // we want to be able to aim farther horizontally than vertically, so modify velocity and dist based on pitch.
|
|
vel *= idMath::Cos( DEG2RAD( beamAngle ) );
|
|
|
|
vx = vel * idMath::Cos( DEG2RAD( beamAngle ));
|
|
vz = vel * idMath::Sin( DEG2RAD( beamAngle ));
|
|
|
|
tDisX = dist / vel;
|
|
|
|
last = beamOrigin;
|
|
z0 = beamOrigin.z;
|
|
|
|
for ( int i = 0; i < numPoints; i++ )
|
|
{
|
|
t += tDisX;
|
|
|
|
zDelt = z0 - vz * t - 0.5 * grav * ( t * t );
|
|
xDelt = vx * tDisX;
|
|
|
|
next = last + forward[0] * xDelt;
|
|
|
|
if ( z0 - zDelt >= vr_teleportMaxDrop.GetFloat() )
|
|
{
|
|
zDelt = z0 - vr_teleportMaxDrop.GetFloat();
|
|
t = (idMath::Sqrt( (2 * grav * z0) - (2 * grav * zDelt) + (vz * vz) ) - vz) / grav;
|
|
xDelt = vx * t;
|
|
next = beamOrigin + forward[0] * xDelt;
|
|
i = numPoints;
|
|
|
|
}
|
|
|
|
next.z = zDelt;
|
|
teleportPoint = teleportAimPoint = next;
|
|
|
|
padAxis = forward;
|
|
|
|
if ( gameLocal.clip.TracePoint( traceResults, last, next, MASK_SHOT_RENDERMODEL, this ) )
|
|
{
|
|
|
|
const char * hitMat;
|
|
|
|
hitMat = traceResults.c.material->GetName();
|
|
float hitPitch = traceResults.c.normal.ToAngles().pitch;
|
|
|
|
// handrails really make aiming suck, so skip any non floor hits that consist of these materials
|
|
// really need to verify this doesn't break anything.
|
|
// Carl: It does break teleporting onto handrails, which I intended to be able to do.
|
|
|
|
|
|
if ( vr_teleportSkipHandrails.GetInteger() == 1 && hitPitch != -90 )
|
|
{
|
|
if ( idStr::FindText( hitMat, "base_trim" ) > -1 ||
|
|
idStr::FindText( hitMat, "swatch" ) > -1 ||
|
|
idStr::FindText( hitMat, "mchangar2" ) > -1 ||
|
|
idStr::FindText( hitMat, "mchangar3" ) > -1 )
|
|
{
|
|
|
|
common->Printf( "Beam hit rejected: material %s hitpitch %f\n", hitMat,hitPitch );
|
|
last = next;
|
|
endPos = last;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
//common->Printf( "Beam hit material = %s\n", traceResults.c.material->GetName() );
|
|
next = traceResults.c.point;
|
|
endPos = next;
|
|
|
|
|
|
//set the axis for the telepad to match the surface
|
|
static idAngles surfaceAngle = ang_zero;
|
|
|
|
static idQuat lastQ = idAngles( 0.0f, 0.0f, 90.0f ).ToQuat();
|
|
static idQuat nextQ = lastQ;
|
|
static idQuat lastSet = lastQ;
|
|
|
|
static idMat3 lastAxis = mat3_zero;
|
|
static idMat3 curAxis = mat3_zero;
|
|
|
|
static int slerpEnd = commonVr->Sys_Milliseconds() - 500;
|
|
static idVec3 lastHitNormal = vec3_zero;
|
|
|
|
idVec3 hitNormal = traceResults.c.normal;
|
|
|
|
static idAngles muzzleAngle = ang_zero;
|
|
static idAngles diffAngle = ang_zero;
|
|
static float rollDiff = 0.0f;
|
|
|
|
surfaceAngle = traceResults.c.normal.ToAngles().Normalize180();
|
|
muzzleAngle = beamAxis.ToAngles().Normalize180();
|
|
muzzleAngle.roll = 0;
|
|
|
|
surfaceAngle.pitch *= -1;
|
|
surfaceAngle.yaw += 180;
|
|
surfaceAngle.Normalize180();
|
|
|
|
diffAngle = idAngles( 0, 0, muzzleAngle.yaw - surfaceAngle.yaw ).Normalize180();
|
|
|
|
rollDiff = diffAngle.roll * 1 / (90 / surfaceAngle.pitch);
|
|
|
|
surfaceAngle.roll = muzzleAngle.roll - rollDiff;
|
|
surfaceAngle.Normalize180();
|
|
curAxis = surfaceAngle.ToMat3();
|
|
|
|
if ( hitNormal != lastHitNormal )
|
|
{
|
|
|
|
lastHitNormal = hitNormal;
|
|
|
|
if ( slerpEnd - commonVr->Sys_Milliseconds() <= 0 )
|
|
{
|
|
lastQ = lastAxis.ToQuat();
|
|
}
|
|
else
|
|
{
|
|
lastQ = lastSet;
|
|
}
|
|
|
|
slerpEnd = commonVr->Sys_Milliseconds() + slTime;
|
|
nextQ = curAxis.ToQuat();
|
|
|
|
}
|
|
|
|
if ( slerpEnd - commonVr->Sys_Milliseconds() <= 0 )
|
|
{
|
|
padAxis = curAxis;
|
|
}
|
|
else
|
|
{
|
|
float qt = (float)((float)(slTime + 1.0f) - (float)(slerpEnd - commonVr->Sys_Milliseconds())) / (float)slTime;
|
|
lastSet.Slerp( lastQ, nextQ, qt );
|
|
padAxis = lastSet.ToMat3();
|
|
}
|
|
|
|
lastAxis = curAxis;
|
|
|
|
bool aimLadder = false, aimActor = false, aimElevator = false;
|
|
aimLadder = traceResults.c.material && (traceResults.c.material->GetSurfaceFlags() & SURF_LADDER);
|
|
idEntity* aimEntity = gameLocal.GetTraceEntity(traceResults);
|
|
if (aimEntity)
|
|
{
|
|
if (aimEntity->IsType(idActor::Type))
|
|
aimActor = aimEntity->health > 0;
|
|
else if (aimEntity->IsType(idElevator::Type))
|
|
aimElevator = true;
|
|
else if (aimEntity->IsType(idStaticEntity::Type) || aimEntity->IsType(idLight::Type))
|
|
{
|
|
renderEntity_t *rend = aimEntity->GetRenderEntity();
|
|
if (rend)
|
|
{
|
|
idRenderModel *model = rend->hModel;
|
|
aimElevator = (model && idStr::Cmp(model->Name(), "models/mapobjects/elevators/elevator.lwo") == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
teleportPoint = teleportAimPoint = traceResults.c.point;
|
|
float beamLengthSquared = 0;
|
|
if (aimElevator)
|
|
{
|
|
teleportPoint = teleportAimPoint + idVec3(0, 0, 10);
|
|
beamLengthSquared = (teleportPoint - beamOrigin).LengthSqr();
|
|
}
|
|
if ((hitPitch >= -90.0f && hitPitch <= -45.0f && !aimActor) || aimLadder)
|
|
{
|
|
bool aimValid = (aimElevator && beamLengthSquared <= 300 * 300) || CanReachPosition(teleportAimPoint, teleportPoint);
|
|
// check if we are teleporting OUT of an elevator
|
|
if (!aimValid && !aimActor && fabs(teleportPoint.z - physicsObj.GetOrigin().z) <= 10 && (teleportPoint - beamOrigin).LengthSqr() <= 300 * 300)
|
|
{
|
|
// do a trace to see if we're in an elevator, and if so, set aimValid to true
|
|
trace_t result;
|
|
physicsObj.ClipTranslation(result, GetPhysics()->GetGravityNormal() * 10, NULL);
|
|
if (result.fraction < 1.0f)
|
|
{
|
|
aimEntity = gameLocal.GetTraceEntity(result);
|
|
if (aimEntity)
|
|
{
|
|
if (aimEntity->IsType(idElevator::Type))
|
|
aimValid = true;
|
|
else if (aimEntity->IsType(idStaticEntity::Type) || aimEntity->IsType(idLight::Type))
|
|
{
|
|
renderEntity_t *rend = aimEntity->GetRenderEntity();
|
|
if (rend)
|
|
{
|
|
idRenderModel *model = rend->hModel;
|
|
aimValid = (model && idStr::Cmp(model->Name(), "models/mapobjects/elevators/elevator.lwo") == 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( aimValid )
|
|
{
|
|
|
|
// pitch indicates a flat surface or <45 deg slope,
|
|
// check to see if a clip test passes at the location AFTER
|
|
// checking reachability.
|
|
// the clip test will prevent us from teleporting too close to walls.
|
|
|
|
static trace_t trace;
|
|
static idClipModel* clip;
|
|
static idMat3 clipAxis;
|
|
static idVec3 tracePt;
|
|
tracePt = teleportPoint;
|
|
|
|
clip = physicsObj.GetClipModel();
|
|
clipAxis = physicsObj.GetClipModel()->GetAxis();
|
|
|
|
gameLocal.clip.Translation(trace, tracePt, tracePt, clip, clipAxis, CONTENTS_SOLID, NULL);
|
|
// Carl: check if we're on stairs
|
|
if (trace.fraction < 1.0f)
|
|
{
|
|
tracePt.z += pm_stepsize.GetFloat();
|
|
gameLocal.clip.Translation(trace, tracePt, tracePt, clip, clipAxis, CONTENTS_SOLID, NULL);
|
|
}
|
|
|
|
if (trace.fraction < 1.0f)
|
|
{
|
|
aimValidForTeleport = false;
|
|
isShowing = false;
|
|
// Koz
|
|
//please duck sometimes shows incorrectly if a moveable object is in the way.
|
|
//add a clip check at the crouch height, and if it passes, show the please duck graphic.
|
|
//otherwise something is in the way.
|
|
|
|
if ( clip->GetBounds()[1][2] == pm_crouchheight.GetFloat() ) // player is already crouching and we failed the clip test
|
|
{
|
|
pleaseDuck = false;
|
|
}
|
|
else
|
|
{
|
|
|
|
// change bounds to crouch height and check again.
|
|
idBounds bounds = clip->GetBounds();
|
|
bounds[1][2] = pm_crouchheight.GetFloat();
|
|
if ( pm_usecylinder.GetBool() )
|
|
{
|
|
clip->LoadModel( idTraceModel( bounds, 8 ) );
|
|
}
|
|
else
|
|
{
|
|
clip->LoadModel( idTraceModel( bounds ) );
|
|
}
|
|
|
|
gameLocal.clip.Translation( trace, tracePt, tracePt, clip, clipAxis, CONTENTS_SOLID, NULL );
|
|
|
|
pleaseDuck = trace.fraction >= 1.0f ? true : false;
|
|
|
|
//reset bounds
|
|
bounds[1][2] = pm_normalheight.GetFloat();
|
|
if ( pm_usecylinder.GetBool() )
|
|
{
|
|
clip->LoadModel( idTraceModel( bounds, 8 ) );
|
|
}
|
|
else
|
|
{
|
|
clip->LoadModel( idTraceModel( bounds ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aimValidForTeleport = true;
|
|
if ( !isShowing )
|
|
{
|
|
slerpEnd = commonVr->Sys_Milliseconds() - 100;
|
|
padAxis = curAxis;
|
|
lastAxis = curAxis;
|
|
}
|
|
|
|
teleportTarget->GetRenderEntity()->weaponDepthHack = false;
|
|
isShowing = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const idMat3 correct = idAngles( -90.0f, 0.0f, 90.0f ).ToMat3();
|
|
padAxis *= forward.Inverse();
|
|
padAxis = correct * padAxis;
|
|
|
|
break;
|
|
}
|
|
|
|
last = next;
|
|
endPos = last;
|
|
}
|
|
|
|
teleportTarget->SetOrigin( beamOrigin );
|
|
|
|
// an approximation of a parabola has been scanned for surface hits and the endpoint has been calculated.
|
|
// update the beam model by setting the joint positions along the beam to trace the arc.
|
|
// joint 0 should be set to the origin, and the origin of the model is set to the origin of the beam in worldspace.
|
|
|
|
teleportTargetAnimator->SetJointPos( teleportBeamJoint[0], JOINTMOD_WORLD_OVERRIDE, vec3_zero ); // joint 0 always the origin.
|
|
|
|
st = beamOrigin.ToVec2();
|
|
en = endPos.ToVec2();
|
|
|
|
tDisX = ( (en - st).Length() / vx ) / 23.0f; // time for each segment of arc at velocity vx.
|
|
t = 0.0f;
|
|
|
|
next = beamOrigin;
|
|
|
|
idMat3 forwardInv = forward.Inverse();
|
|
|
|
for ( int i = 1; i < 23; i++ )
|
|
{
|
|
t += tDisX;
|
|
|
|
zDelt = z0 - vz * t - 0.5f * grav * (t * t);
|
|
xDelt = vx * tDisX;
|
|
|
|
next = next + forward[0] * xDelt;
|
|
next.z = zDelt;
|
|
|
|
jpos = ( next - beamOrigin ) * forwardInv;
|
|
teleportTargetAnimator->SetJointPos( teleportBeamJoint[i], JOINTMOD_WORLD_OVERRIDE, jpos );
|
|
}
|
|
|
|
jpos = ( endPos - beamOrigin ) * forwardInv;
|
|
teleportTargetAnimator->SetJointPos( teleportBeamJoint[23], JOINTMOD_WORLD_OVERRIDE, jpos );
|
|
//teleportCenterPadJoint
|
|
|
|
//set the center aiming point
|
|
jpos = (teleportAimPoint - beamOrigin) * forwardInv;
|
|
teleportTargetAnimator->SetJointPos( teleportCenterPadJoint, JOINTMOD_WORLD_OVERRIDE, jpos );
|
|
teleportTargetAnimator->SetJointAxis( teleportCenterPadJoint, JOINTMOD_WORLD_OVERRIDE, padAxis );
|
|
|
|
if ( vr_teleportShowAimAssist.GetInteger() )
|
|
{
|
|
// if we want to have the telepad reflect the aim assist, update the joint for the telepad
|
|
//otherwise it will use the same origin as the aiming point
|
|
jpos = (teleportPoint - beamOrigin) * forwardInv;
|
|
}
|
|
|
|
teleportTargetAnimator->SetJointPos( teleportPadJoint, JOINTMOD_WORLD_OVERRIDE, jpos );
|
|
teleportTargetAnimator->SetJointAxis( teleportPadJoint, JOINTMOD_WORLD_OVERRIDE, padAxis );
|
|
|
|
if ( !aimValidForTeleport )
|
|
{
|
|
// this will show the beam, but hide the teleport target
|
|
|
|
if ( pleaseDuck )
|
|
{
|
|
teleportTarget->GetRenderEntity()->shaderParms[0] = 1;
|
|
teleportTarget->GetRenderEntity()->shaderParms[1] = 1;
|
|
teleportTarget->Show();
|
|
teleportTarget->GetRenderEntity()->customSkin = skinTelepadCrouch;
|
|
isShowing = true;
|
|
|
|
if ( !vr_teleportHint.GetBool() )
|
|
{
|
|
ShowTip( "Duck! Low Headroom!", "If the teleport target turns red, there is limited headroom at the teleport destination. You must crouch before you can teleport to this location.", false );
|
|
vr_teleportHint.SetBool( true );
|
|
}
|
|
//gameRenderWorld->DrawText( "Low Headroom\nPlease Duck", teleportPoint + idVec3( 0, 0, 18 ), 0.2f, colorOrange, viewAngles.ToMat3() );
|
|
}
|
|
else
|
|
{
|
|
teleportTarget->GetRenderEntity()->customSkin = NULL;
|
|
teleportTarget->GetRenderEntity()->shaderParms[0] = 0;
|
|
teleportTarget->GetRenderEntity()->shaderParms[1] = 0;
|
|
teleportTarget->Show();
|
|
isShowing = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
teleportTarget->GetRenderEntity()->customSkin = NULL;
|
|
teleportDir = ( physicsObj.GetOrigin() - teleportPoint );
|
|
teleportDir.Normalize();
|
|
teleportTarget->GetRenderEntity()->shaderParms[0] = 255;
|
|
teleportTarget->GetRenderEntity()->shaderParms[1] = 1;
|
|
teleportTarget->Show();
|
|
isShowing = true;
|
|
}
|
|
|
|
teleportTarget->Present();
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::HandleSingleGuiCommand
|
|
===============
|
|
*/
|
|
bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
|
|
idToken token;
|
|
|
|
if ( !src->ReadToken( &token ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( token == ";" ) {
|
|
return false;
|
|
}
|
|
|
|
if ( token.Icmp( "addhealth" ) == 0 ) {
|
|
if ( entityGui && health < 100 ) {
|
|
int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
|
|
int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
|
|
_health -= amt;
|
|
entityGui->spawnArgs.SetInt( "gui_parm1", _health );
|
|
if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
|
|
entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
|
|
}
|
|
health += amt;
|
|
if ( health > 100 ) {
|
|
health = 100;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "ready" ) == 0 ) {
|
|
PerformImpulse( IMPULSE_17 );
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "updatepda" ) == 0 ) {
|
|
UpdatePDAInfo( true );
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "updatepda2" ) == 0 ) {
|
|
UpdatePDAInfo( false );
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "stoppdavideo" ) == 0 ) {
|
|
if ( objectiveSystem && objectiveSystemOpen && pdaVideoWave.Length() > 0 ) {
|
|
StopSound( SND_CHANNEL_PDA, false );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "close" ) == 0 ) {
|
|
if ( objectiveSystem && objectiveSystemOpen ) {
|
|
TogglePDA(1 - vr_weaponHand.GetInteger());
|
|
}
|
|
}
|
|
|
|
if ( token.Icmp( "playpdavideo" ) == 0 ) {
|
|
if ( objectiveSystem && objectiveSystemOpen && pdaVideo.Length() > 0 ) {
|
|
const idMaterial *mat = declManager->FindMaterial( pdaVideo );
|
|
if ( mat ) {
|
|
int c = mat->GetNumStages();
|
|
for ( int i = 0; i < c; i++ ) {
|
|
const shaderStage_t *stage = mat->GetStage(i);
|
|
if ( stage && stage->texture.cinematic ) {
|
|
stage->texture.cinematic->ResetTime( gameLocal.time );
|
|
}
|
|
}
|
|
if ( pdaVideoWave.Length() ) {
|
|
const idSoundShader *shader = declManager->FindSound( pdaVideoWave );
|
|
StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( token.Icmp( "playpdaaudio" ) == 0 ) {
|
|
if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
|
|
const idSoundShader *shader = declManager->FindSound( pdaAudio );
|
|
int ms;
|
|
StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, &ms );
|
|
StartAudioLog();
|
|
CancelEvents( &EV_Player_StopAudioLog );
|
|
PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( token.Icmp( "stoppdaaudio" ) == 0 ) {
|
|
if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
|
|
// idSoundShader *shader = declManager->FindSound( pdaAudio );
|
|
StopAudioLog();
|
|
StopSound( SND_CHANNEL_PDA, false );
|
|
}
|
|
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 ) {
|
|
other->Signal( SIG_TOUCH );
|
|
if ( !spectating ) {
|
|
if ( other->RespondsTo( EV_Touch ) ) {
|
|
other->ProcessEvent( &EV_Touch, this, &collision );
|
|
}
|
|
} else {
|
|
if ( other->RespondsTo( EV_SpectatorTouch ) ) {
|
|
other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idPlayer::UpdateLocation
|
|
|
|
Searches nearby locations
|
|
================
|
|
*/
|
|
void idPlayer::UpdateLocation( void ) {
|
|
if ( hud ) {
|
|
idLocationEntity *locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
|
|
if ( locationEntity ) {
|
|
hud->SetStateString( "location", locationEntity->GetLocation() );
|
|
} else {
|
|
hud->SetStateString( "location", common->GetLanguageDict()->GetString( "#str_02911" ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz idPlayer::UpdateNeckPose
|
|
In Vr, if viewing the player body, update the neck joint with the orientation of the HMD.
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateNeckPose()
|
|
{
|
|
static idAngles headAngles, lastView = ang_zero;
|
|
|
|
if ( !game->isVR ) return;
|
|
|
|
// if showing the player body, move the head/neck based on HMD
|
|
lastView = commonVr->lastHMDViewAxis.ToAngles();
|
|
headAngles.roll = lastView.pitch;
|
|
headAngles.pitch = commonVr->lastHMDYaw - commonVr->bodyYawOffset;
|
|
headAngles.yaw = lastView.roll;
|
|
headAngles.Normalize360();
|
|
animator.SetJointAxis( neckJoint, JOINTMOD_LOCAL, headAngles.ToMat3() );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdatePDASlot
|
|
==============
|
|
*/
|
|
void idPlayer::UpdatePDASlot()
|
|
{
|
|
if( vr_slotDisable.GetBool() )
|
|
{
|
|
return;
|
|
}
|
|
if( pdaRenderEntity.hModel ) // && inventory.pdas.Num()
|
|
{
|
|
//pdaRenderEntity.timeGroup = timeGroup;
|
|
|
|
pdaRenderEntity.entityNum = ENTITYNUM_NONE;
|
|
|
|
pdaRenderEntity.axis = pdaHolsterAxis * waistAxis;
|
|
idVec3 slotOrigin = slots[SLOT_PDA_HIP].origin;
|
|
if (vr_weaponHand.GetInteger())
|
|
slotOrigin.y *= -1.0f;
|
|
pdaRenderEntity.origin = waistOrigin + slotOrigin * waistAxis;
|
|
|
|
pdaRenderEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
pdaRenderEntity.weaponDepthHack = g_useWeaponDepthHack.GetBool();
|
|
|
|
if( pdaModelDefHandle == -1 )
|
|
{
|
|
pdaModelDefHandle = gameRenderWorld->AddEntityDef( &pdaRenderEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( pdaModelDefHandle, &pdaRenderEntity );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::ClearFocus
|
|
|
|
Clears the focus cursor
|
|
================
|
|
*/
|
|
void idPlayer::ClearFocus( void ) {
|
|
focusCharacter = NULL;
|
|
focusGUIent = NULL;
|
|
focusUI = NULL;
|
|
focusVehicle = NULL;
|
|
talkCursor = 0;
|
|
|
|
// Koz
|
|
commonVr->handInGui = false;
|
|
//weapon->GetRenderEntity()->allowSurfaceInViewID = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
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 ) {
|
|
idClipModel *clipModelList[ MAX_GENTITIES ];
|
|
idClipModel *clip;
|
|
int listedClipModels;
|
|
idEntity *oldFocus;
|
|
idEntity *ent;
|
|
idUserInterface *oldUI;
|
|
idAI *oldChar;
|
|
int oldTalkCursor;
|
|
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;
|
|
|
|
static idMat3 lastScanAxis = commonVr->lastViewAxis;
|
|
static idVec3 lastScanStart = commonVr->lastViewOrigin;
|
|
static idVec3 lastBodyPosition = physicsObj.GetOrigin();
|
|
static float lastBodyYaw = 0.0f;
|
|
static bool lowered = false;
|
|
static bool raised = false;
|
|
|
|
static idVec3 surfaceNormal = vec3_zero;
|
|
static idVec3 fingerPosLocal = vec3_zero;
|
|
static idMat3 fingerAxisLocal = mat3_identity;
|
|
static idVec3 fingerPosGlobal = vec3_zero;
|
|
static idVec3 scanStart = vec3_zero;
|
|
static idVec3 scanEnd = vec3_zero;
|
|
static idVec3 talkScanEnd = vec3_zero;
|
|
static jointHandle_t fingerJoint;
|
|
static bool touching = false;
|
|
|
|
static idMat3 weaponAxis;
|
|
static bool scanFromWeap;
|
|
static float scanRange;
|
|
static float scanRangeCorrected;
|
|
static float hmdAbsPitch;
|
|
|
|
idMat3 viewPitchAdj = idAngles( vr_guiFocusPitchAdj.GetFloat(), 0.0f, 0.0f ).ToMat3();
|
|
|
|
scanRange = 50.0f;
|
|
|
|
if ( gameLocal.inCinematic || commonVr->thirdPersonMovement ) {
|
|
return;
|
|
}
|
|
|
|
//check for PDA interaction.
|
|
//if the PDA is being interacted with, there is no need to check for other guis
|
|
//or scan for character names so bail.
|
|
if ( UpdateFocusPDA() )
|
|
{
|
|
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 || commonVr->teleportButtonCount != 0) {
|
|
ClearFocus();
|
|
raised = false;
|
|
lowered = false;
|
|
if ( commonVr->teleportButtonCount != 0 ) return;
|
|
}
|
|
|
|
// don't let spectators interact with GUIs
|
|
if ( spectating ) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
start = GetEyePosition();
|
|
end = start + viewAngles.ToForward() * 80.0f;
|
|
*/
|
|
start = GetEyePosition();
|
|
|
|
// Koz begin
|
|
if ( game->isVR ) // Koz fixme only when vr actually active.
|
|
{
|
|
// Koz in VR, if weapon equipped, use muzzle orientation to scan for accessible guis,
|
|
// otherwise use player center eye.
|
|
|
|
scanFromWeap = hands[vr_weaponHand.GetInteger()].weapon->GetMuzzlePositionWithHacks( start, weaponAxis );
|
|
if ( !scanFromWeap || vr_guiMode.GetInteger() == 1 || ( vr_guiMode.GetInteger() == 2 && commonVr->VR_USE_MOTION_CONTROLS ) ) // guiMode 2 = use guis as touch screen
|
|
{
|
|
//weapon has no muzzle ( fists, grenades, chainsaw) or we are using the guis as touchscreens so scan from center of view.
|
|
start = commonVr->lastViewOrigin;
|
|
weaponAxis = commonVr->lastViewAxis;
|
|
|
|
scanRange += 36.0f;
|
|
|
|
if ( vr_guiMode.GetInteger() == 2 )
|
|
{
|
|
|
|
weaponAxis = viewPitchAdj * weaponAxis; // add a little down pitch to help my neck.
|
|
|
|
scanRange = 36.0f; // get pretty close for touchscreens
|
|
// if the hand is already in the gui, or has completed the lower cycle after hitting a gui,
|
|
// scan from the same point in space until the player exceeds movement threshold
|
|
// this prevents getting kicked from a gui when just looking around
|
|
if ( commonVr->handInGui || raised )
|
|
{
|
|
static idMat3 yawAdj;
|
|
static idVec3 distMoved;
|
|
scanRange += 4;
|
|
yawAdj = idAngles( 0.0f, viewAngles.yaw - lastBodyYaw, 0.0f ).Normalize180().ToMat3();
|
|
weaponAxis = yawAdj * lastScanAxis;
|
|
distMoved = physicsObj.GetOrigin() - lastBodyPosition;
|
|
if ( distMoved.Length() < 12.0f ) distMoved = vec3_zero;
|
|
start = lastScanStart + distMoved;
|
|
|
|
}
|
|
else
|
|
{
|
|
lastScanStart = start;
|
|
lastScanAxis = weaponAxis;
|
|
lastBodyYaw = viewAngles.yaw;
|
|
lastBodyPosition = physicsObj.GetOrigin();
|
|
}
|
|
}
|
|
hmdAbsPitch = abs( commonVr->lastHMDViewAxis.ToAngles().Normalize180().pitch + vr_guiFocusPitchAdj.GetFloat() );
|
|
if ( hmdAbsPitch > 60.0f ) hmdAbsPitch = 60.0f;
|
|
scanRangeCorrected = scanRange / cos( DEG2RAD( hmdAbsPitch ) );
|
|
scanRange = scanRangeCorrected;
|
|
}
|
|
else
|
|
{
|
|
// Koz - if weapon has been lowered (in gui), raise pointer to compensate.
|
|
start.z -= hands[ vr_weaponHand.GetInteger() ].weapon->hideOffset;
|
|
}
|
|
|
|
end = start + weaponAxis[0] * scanRange;// Koz originial value was 80.0f - allowed access to gui from too great a distance (IMO), reduced to 50.0f Koz fixme - make cvar?
|
|
}
|
|
else
|
|
{
|
|
end = start + firstPersonViewAxis[0] * 80.0f;
|
|
}
|
|
// Koz end
|
|
if ( game->isVR && commonVr->VR_USE_MOTION_CONTROLS && vr_guiMode.GetInteger() == 2 )
|
|
{
|
|
talkScanEnd = start + weaponAxis[0] * (scanRange + 40);
|
|
}
|
|
else
|
|
{
|
|
talkScanEnd = end;
|
|
}
|
|
|
|
|
|
/*
|
|
//Use unadjusted weapon angles to control GUIs
|
|
idMat3 weaponViewAxis;
|
|
idAngles weaponViewAngles;
|
|
CalculateViewWeaponPos(true, start, weaponViewAxis, weaponViewAngles);
|
|
end = start + (weaponViewAngles.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 );
|
|
|
|
if ( vr_debugGui.GetBool() )
|
|
{
|
|
gameRenderWorld->DebugLine( colorRed, start, end, 20, true );
|
|
//common->Printf( "Handin gui %d raised %d lowered %d hideoffset %f\n", commonVr->handInGui, raised, lowered, hands[ vr_weaponHand.GetInteger() ].weapon->hideOffset );
|
|
}
|
|
|
|
// 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() ) {
|
|
if ( (!ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[0] || !ent->GetRenderEntity()->gui[0]->IsInteractive()) && (!game->IsPDAOpen() /* && !commonVr->PDAclipModelSet */) ) { // Koz don't bail if the PDA is open and clipmodel is set.
|
|
continue;
|
|
}
|
|
|
|
if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
|
|
// don't allow guis on pickup items focus
|
|
continue;
|
|
}
|
|
|
|
int fingerHand = vr_weaponHand.GetInteger();
|
|
|
|
// Koz : if the weapon is reloading, don't let the hand enter a gui, or the weapon anims
|
|
// will still be driving the hand and it looks stupid.
|
|
|
|
if ( hands[ fingerHand ].weapon->IsReloading() ) continue;
|
|
|
|
// Koz : the shotgun reload script cycles through WP_RELOAD and WP_READY for each shell,
|
|
// so we cant just check the reload state or else the hand will enter
|
|
// the gui in between two shells loading. Make sure when using the shotgun
|
|
// that the idle animation is playing before entering a gui.
|
|
// fixme : this sucks - find a better way to check this.
|
|
if( hands[ fingerHand ].currentWeapon == weapon_shotgun )
|
|
{
|
|
if( idStr::Cmp( hands[ fingerHand ].weapon->GetAnimator()->CurrentAnim( ANIMCHANNEL_ALL )->AnimName(), "idle" ) != 0 ) continue;
|
|
}
|
|
|
|
|
|
ent->GetAnimator();
|
|
pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), ent->GetAnimator(), 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 );
|
|
}
|
|
}
|
|
|
|
if ( !game->isVR || !( game->isVR && ( vr_guiMode.GetInteger() == 2 && commonVr->VR_USE_MOTION_CONTROLS) ) )
|
|
{
|
|
// handle event normally
|
|
// 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;
|
|
}
|
|
else
|
|
{
|
|
// the game is vr, player is using motion controls, gui mode is set to use touchscreens
|
|
// and the view has found a gui to interact with.
|
|
// wait for lower weapon to drop the hand to hidedistance and hide the weapon model
|
|
// then raise the empty hand with pointy finger back to original position
|
|
int fingerHand = vr_weaponHand.GetInteger();
|
|
idWeapon* fingerWeapon = hands[ fingerHand ].weapon;
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME * 3 ;
|
|
if ( !lowered )
|
|
{
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME * 3;
|
|
if ( fingerWeapon->hideOffset != fingerWeapon->hideDistance ) break;
|
|
lowered = true;
|
|
}
|
|
|
|
commonVr->handInGui = true;
|
|
|
|
if ( !raised )
|
|
{
|
|
fingerWeapon->hideStart = fingerWeapon->hideDistance;
|
|
fingerWeapon->hideEnd = 0.0f;
|
|
if ( gameLocal.time - fingerWeapon->hideStartTime < fingerWeapon->hideTime )
|
|
{
|
|
fingerWeapon->hideStartTime = gameLocal.time - ( fingerWeapon->hideTime - (gameLocal.time - fingerWeapon->hideStartTime));
|
|
}
|
|
else
|
|
{
|
|
fingerWeapon->hideStartTime = gameLocal.time;
|
|
}
|
|
|
|
raised = true;
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME * 3;
|
|
break;
|
|
}
|
|
|
|
if ( raised == true && fingerWeapon->hideOffset != 0.0f )
|
|
{
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME;
|
|
break;
|
|
}
|
|
|
|
|
|
// so now get current position of the pointy finger tip joint and
|
|
// see if we have touched the gui
|
|
|
|
gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
|
|
|
|
if ( fingerHand == HAND_RIGHT )
|
|
{
|
|
fingerJoint = animator.GetJointHandle( "RindexTip" );
|
|
}
|
|
else
|
|
{
|
|
fingerJoint = animator.GetJointHandle( "LindexTip" );
|
|
}
|
|
|
|
surfaceNormal = -trace.c.normal;
|
|
|
|
animator.GetJointTransform( fingerJoint, gameLocal.time, fingerPosLocal, fingerAxisLocal );
|
|
fingerPosGlobal = fingerPosLocal * GetRenderEntity()->axis + GetRenderEntity()->origin;
|
|
|
|
static int fingerForwDist = 1.0f;
|
|
static int fingerBackwDist = 12.0f;
|
|
|
|
scanStart = fingerPosGlobal - fingerBackwDist * surfaceNormal;
|
|
scanEnd = fingerPosGlobal + fingerForwDist * surfaceNormal;
|
|
|
|
//gameRenderWorld->DebugLine( colorRed, scanStart, scanEnd, 20 );
|
|
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME;
|
|
|
|
pt = gameRenderWorld->GuiTrace( focusGUIent->GetModelDefHandle(), focusGUIent->GetAnimator(), scanStart, scanEnd );
|
|
|
|
if ( pt.fraction >= 1.0f ) // no hit if > = 1.0f
|
|
{
|
|
//send mouse button up
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
command = focusUI->HandleEvent( &ev, gameLocal.time );
|
|
HandleGuiCommands( focusGUIent, command );
|
|
touching = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//we have a hit
|
|
if ( touching )
|
|
{
|
|
if ( pt.fraction >= 0.94f )// || pt.fraction < 0.875f ) // not touching any more was 0.965 and 0.0875
|
|
{
|
|
//common->Printf( "Sending mouse off 1 fraction = %f\n",trace.fraction );
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
command = focusUI->HandleEvent( &ev, gameLocal.time );
|
|
HandleGuiCommands( focusGUIent, command );
|
|
touching = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( pt.x != -1 )
|
|
{
|
|
// 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 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else // if ( !touching )
|
|
{
|
|
//common->Printf( "Fraction = %f\n", pt.fraction );
|
|
if ( pt.fraction < 0.94f )
|
|
{
|
|
//common->Printf( "Setting touching true\n" );
|
|
touching = true;
|
|
if ( pt.x != -1 )
|
|
{
|
|
// 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 );
|
|
|
|
//send mouse button down
|
|
ev = sys->GenerateMouseButtonEvent( 1, true );
|
|
command = focusUI->HandleEvent( &ev, gameLocal.time );
|
|
HandleGuiCommands( focusGUIent, command );
|
|
|
|
//Rumble the controller to let player know they scored a touch.
|
|
hands[fingerHand].SetControllerShake( 0.1f, 12, 0.8f, 12 );
|
|
|
|
focusTime = gameLocal.time + FOCUS_GUI_TIME;
|
|
break;
|
|
|
|
}
|
|
}
|
|
/*else
|
|
{
|
|
|
|
//send mouse button up
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
command = focusUI->HandleEvent( &ev, gameLocal.time );
|
|
HandleGuiCommands( focusGUIent, command );
|
|
touching = false;
|
|
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 ) {
|
|
lowered = false;
|
|
raised = false;
|
|
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::UpdateFocusPDA
|
|
|
|
Searches nearby entities for interactive guis, possibly making one of them
|
|
the focus and sending it a mouse move event
|
|
================
|
|
*/
|
|
bool idPlayer::UpdateFocusPDA()
|
|
{
|
|
static bool touching = false;
|
|
jointHandle_t fingerJoint[2] = { animator.GetJointHandle( "RindexTip" ), animator.GetJointHandle( "LindexTip" ) };
|
|
idVec3 fingerPosLocal = vec3_zero;
|
|
idMat3 fingerAxisLocal = mat3_identity;
|
|
idVec3 fingerPosGlobal = vec3_zero;
|
|
idVec3 scanStart = vec3_zero;
|
|
idVec3 scanEnd = vec3_zero;
|
|
int pdaScrX = renderSystem->GetScreenWidth();// vr_pdaScreenX.GetInteger();
|
|
int pdaScrY = pdaScrX * 0.75; // vr_pdaScreenY.GetInteger();
|
|
const int fingerForwDist = 1.0f;
|
|
const int fingerBackwDist = 12.0f;
|
|
guiPoint_t pt;
|
|
sysEvent_t ev;
|
|
|
|
if ( !game->isVR || !( game->IsPDAOpen() || commonVr->VR_GAME_PAUSED || hands[0].currentWeapon == weapon_pda || hands[1].currentWeapon == weapon_pda ) )
|
|
{
|
|
touching = false;
|
|
return false;
|
|
}
|
|
|
|
int pdahand;
|
|
if( hands[ 0 ].holdingPDA() )
|
|
pdahand = 0;
|
|
else if( hands[ 1 ].holdingPDA() )
|
|
pdahand = 1;
|
|
else
|
|
pdahand = 1 - vr_weaponHand.GetInteger();
|
|
int fingerHand = 1 - pdahand;
|
|
|
|
commonVr->scanningPDA = true; // let the swf event handler know its ok to take mouse input, even if the mouse cursor is not in the window.
|
|
|
|
|
|
// game is VR and PDA active
|
|
if ( vr_guiMode.GetInteger() == 2 && commonVr->VR_USE_MOTION_CONTROLS )
|
|
{
|
|
// the game is vr, player is using motion controls, gui mode is set to use touchscreens
|
|
// and the pda is open
|
|
// wait for lower weapon to drop the hand to hidedistance and hide the weapon model
|
|
// then raise the empty hand with pointy finger back to original position
|
|
|
|
if ( !touching )
|
|
{
|
|
// send a cursor event to get it off the screen since it is hidden.
|
|
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
|
|
SendPDAEvent( &ev );
|
|
}
|
|
|
|
commonVr->handInGui = true;
|
|
focusTime = gameLocal.time + FOCUS_TIME;
|
|
|
|
// get current position of the pointy finger tip joint and
|
|
// see if we have touched the gui
|
|
idWeapon* pdaWeapon = GetPDAWeapon();
|
|
|
|
animator.GetJointTransform( fingerJoint[ fingerHand ], gameLocal.time, fingerPosLocal, fingerAxisLocal );
|
|
fingerPosGlobal = fingerPosLocal * GetRenderEntity()->axis + GetRenderEntity()->origin;
|
|
|
|
|
|
scanStart = fingerPosGlobal - PDAaxis[0] * fingerBackwDist;// PDAaxis.Inverse();// surfaceNormal;
|
|
scanEnd = fingerPosGlobal + PDAaxis[0] * fingerForwDist;// *surfaceNormal;
|
|
|
|
|
|
pt = gameRenderWorld->GuiTrace( pdaWeapon->GetModelDefHandle(), pdaWeapon->GetAnimator(), scanStart, scanEnd );
|
|
pt.y = 1.0f - pt.y;
|
|
|
|
/*
|
|
//debug finger
|
|
if ( pt.fraction >= 1.0f )
|
|
{
|
|
gameRenderWorld->DebugLine( colorRed, scanStart, scanEnd, 20 );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->DebugLine( colorGreen, scanStart, scanEnd, 20 );
|
|
}
|
|
*/
|
|
|
|
/*if ( common->Dialog().IsDialogActive() || game->Shell_IsActive() ) // commonVr->VR_GAME_PAUSED )
|
|
{
|
|
pt.y -= .12f;
|
|
}
|
|
else
|
|
{
|
|
pt.y += 0.12f; // add offset for screensafe borders
|
|
}*/
|
|
|
|
if ( pt.fraction >= 1.0f ) // no hit if > = 1.0f
|
|
{
|
|
//send mouse button up
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
SendPDAEvent( &ev );
|
|
|
|
touching = false;
|
|
}
|
|
else
|
|
{
|
|
//we have a hit
|
|
if ( touching )
|
|
{
|
|
if ( pt.fraction >= 0.94f ) // no longer touching
|
|
{
|
|
|
|
//common->Printf( "Sending mouse off 1 fraction = %f\n",trace.fraction );
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
SendPDAEvent( &ev );
|
|
|
|
touching = false;
|
|
}
|
|
else
|
|
{
|
|
if ( pt.x != -1 )
|
|
{
|
|
// clamp the mouse to the corner
|
|
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
|
|
SendPDAEvent( &ev );
|
|
|
|
// move to an absolute position
|
|
ev = sys->GenerateMouseMoveEvent( pt.x * pdaScrX, pt.y * pdaScrY );//( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
|
|
SendPDAEvent( &ev );
|
|
|
|
}
|
|
}
|
|
}
|
|
else // if ( !touching )
|
|
{
|
|
//common->Printf( "Fraction = %f\n", pt.fraction );
|
|
if ( pt.fraction < 0.94f )
|
|
{
|
|
//common->Printf( "Setting touching true\n" );
|
|
touching = true;
|
|
if ( pt.x != -1 )
|
|
{
|
|
// clamp the mouse to the corner
|
|
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
|
|
SendPDAEvent( &ev );
|
|
|
|
// move to an absolute position
|
|
ev = sys->GenerateMouseMoveEvent( pt.x * pdaScrX, pt.y * pdaScrY );
|
|
SendPDAEvent( &ev );
|
|
|
|
//send mouse button down and back up again
|
|
ev = sys->GenerateMouseButtonEvent( 1, true );
|
|
SendPDAEvent( &ev );
|
|
|
|
//Rumble the controller to let player know they scored a touch.
|
|
|
|
// Carl: Should the PDA vibrate/haptic feedback too?
|
|
hands[fingerHand].SetControllerShake( 0.1f, 12, 0.8f, 12 );
|
|
hands[pdahand].SetControllerShake( 0.1f, 12, 0.8f, 12 );
|
|
}
|
|
commonVr->scanningPDA = false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
//send mouse button up
|
|
ev = sys->GenerateMouseButtonEvent( 1, false );
|
|
SendPDAEvent( &ev );
|
|
//Sys_AddMouseButtonEvent(1, false);
|
|
touching = false;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
commonVr->scanningPDA = false;
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------
|
|
//not using motion controls, scan from view
|
|
|
|
scanStart = commonVr->lastViewOrigin;
|
|
scanEnd = scanStart + commonVr->lastViewAxis[0] * 60.0f; // not sure why the PDA would be farther than 60 inches away. Thats one LOOONG arm.
|
|
|
|
//gameRenderWorld->DebugLine( colorYellow, scanStart, scanEnd, 10 );
|
|
|
|
idWeapon* pdaWeapon = GetPDAWeapon();
|
|
pt = gameRenderWorld->GuiTrace( pdaWeapon->GetModelDefHandle(), pdaWeapon->GetAnimator(), scanStart, scanEnd );
|
|
|
|
if ( pt.x != -1 )
|
|
{
|
|
|
|
pt.y = 1 - pt.y; // texture was copied from framebuffer and is upside down so invert y
|
|
if ( !commonVr->VR_GAME_PAUSED ) pt.y += 0.12f; // add offset for screensafe borders if using PDA
|
|
|
|
focusTime = gameLocal.time + FOCUS_TIME;
|
|
|
|
// move to an absolute position
|
|
|
|
ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
|
|
SendPDAEvent( &ev );
|
|
|
|
ev = sys->GenerateMouseMoveEvent( pdaScrX * pt.x, pdaScrY * pt.y );
|
|
SendPDAEvent( &ev );
|
|
|
|
commonVr->scanningPDA = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
commonVr->scanningPDA = false;
|
|
|
|
return false; // view didn't hit pda
|
|
}
|
|
|
|
void idPlayer::SendPDAEvent( const sysEvent_t* sev )
|
|
{
|
|
objectiveSystem->HandleEvent(sev, gameLocal.time );
|
|
//HandleGuiEvents( sev );
|
|
/*if ( common->Dialog().IsDialogActive() || game->Shell_IsActive() ) //commonVr->VR_GAME_PAUSED )
|
|
{
|
|
if ( common->Dialog().IsDialogActive() )
|
|
{
|
|
common->Dialog().HandleDialogEvent( sev );
|
|
}
|
|
else
|
|
{
|
|
game->Shell_HandleGuiEvent( sev );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HandleGuiEvents( sev );
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::HandleGuiEvents
|
|
==============
|
|
*/
|
|
bool idPlayer::HandleGuiEvents( const sysEvent_t* ev )
|
|
{
|
|
|
|
bool handled = false;
|
|
if(hud)
|
|
{
|
|
hud->HandleEvent(ev, gameLocal.time );
|
|
}
|
|
/*if( hudManager != NULL && hudManager->IsActive() )
|
|
{
|
|
handled = hudManager->HandleGuiEvent( ev );
|
|
}
|
|
/*
|
|
if ( pdaMenu != NULL && pdaMenu->IsActive() )
|
|
{
|
|
handled = pdaMenu->HandleGuiEvent( ev );
|
|
}*/
|
|
|
|
return handled;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateHolsterSlot
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateHolsterSlot()
|
|
{
|
|
if( vr_slotDisable.GetBool() )
|
|
{
|
|
FreeHolsterSlot();
|
|
holsteredWeapon = weapon_fists;
|
|
return;
|
|
}
|
|
if( holsterRenderEntity.hModel )
|
|
{
|
|
//holsterRenderEntity.timeGroup = timeGroup;
|
|
|
|
holsterRenderEntity.entityNum = ENTITYNUM_NONE;
|
|
|
|
holsterRenderEntity.axis = holsterAxis * waistAxis;
|
|
idVec3 slotOrigin = slots[SLOT_WEAPON_HIP].origin + idVec3(-5, 0, 0);
|
|
if (vr_weaponHand.GetInteger())
|
|
slotOrigin.y *= -1.0f;
|
|
holsterRenderEntity.origin = waistOrigin + slotOrigin * waistAxis;
|
|
|
|
holsterRenderEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
holsterRenderEntity.weaponDepthHack = g_useWeaponDepthHack.GetBool();
|
|
|
|
if( holsterModelDefHandle == -1 )
|
|
{
|
|
holsterModelDefHandle = gameRenderWorld->AddEntityDef( &holsterRenderEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( holsterModelDefHandle, &holsterRenderEntity );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Koz
|
|
idPlayer::UpdateHeadingBeam
|
|
*/
|
|
void idPlayer::UpdateHeadingBeam()
|
|
{
|
|
if ( vr_headingBeamMode.IsModified() )
|
|
{
|
|
int mode = vr_headingBeamMode.GetInteger();
|
|
vr_headingBeamMode.ClearModified();
|
|
|
|
|
|
switch ( mode )
|
|
{
|
|
|
|
case 0:
|
|
headingBeamActive = false;
|
|
break;
|
|
|
|
case 1:
|
|
headingBeamEntity.customSkin = skinHeadingSolid;
|
|
headingBeamActive = true;
|
|
break;
|
|
|
|
case 2:
|
|
headingBeamEntity.customSkin = skinHeadingArrows;
|
|
headingBeamActive = true;
|
|
break;
|
|
|
|
case 3:
|
|
headingBeamEntity.customSkin = skinHeadingArrowsScroll;
|
|
headingBeamActive = true;
|
|
break;
|
|
|
|
default:
|
|
headingBeamEntity.customSkin = skinHeadingArrowsScroll;
|
|
headingBeamActive = true;
|
|
|
|
}
|
|
}
|
|
|
|
if ( !headingBeamActive )
|
|
{
|
|
// hide it
|
|
headingBeamEntity.allowSurfaceInViewID = -1;
|
|
|
|
}
|
|
else
|
|
{
|
|
idVec3 beamOrigin = GetEyePosition();
|
|
idMat3 beamAxis = idAngles( 0.0f, viewAngles.yaw, 0.0f ).ToMat3();
|
|
|
|
beamOrigin.z -= (pm_normalviewheight.GetFloat());
|
|
|
|
headingBeamEntity.axis = beamAxis;
|
|
headingBeamEntity.origin = beamOrigin;
|
|
headingBeamEntity.bounds.Zero();
|
|
|
|
headingBeamEntity.allowSurfaceInViewID = entityNumber + 1 ;
|
|
|
|
}
|
|
|
|
// make sure the entitydefs are updated
|
|
/*
|
|
if ( headingBeamHandle == -1 )
|
|
{
|
|
headingBeamHandle = gameRenderWorld->AddEntityDef( &headingBeamEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( headingBeamHandle, &headingBeamEntity );
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::CrashLand
|
|
|
|
Check for hard landings that generate sound events
|
|
=================
|
|
*/
|
|
void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
|
|
idVec3 origin, velocity;
|
|
idVec3 gravityVector, gravityNormal;
|
|
float delta;
|
|
float 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 ) {
|
|
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 * USERCMD_MSEC ) & 255;
|
|
bobFoot = ( bobCycle & 128 ) >> 7;
|
|
bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
|
|
}*/
|
|
|
|
//Disable bobbing in VR
|
|
bobCycle = 0;
|
|
bobFoot = 0;
|
|
bobfracsin = 0;
|
|
|
|
// 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::UpdateFlashLight
|
|
===============
|
|
*/
|
|
void idPlayer::UpdateFlashlight()
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].idealWeapon == weapon_flashlight )
|
|
{
|
|
// force classic flashlight to go away
|
|
hands[h].NextWeapon();
|
|
}
|
|
}
|
|
|
|
if( !flashlight.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( !flashlight->GetOwner() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Don't update the flashlight if dead in MP.
|
|
// Otherwise you can see a floating flashlight worldmodel near player's skeletons.
|
|
if( gameLocal.isMultiplayer )
|
|
{
|
|
if( health < 0 )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Flashlight has an infinite battery in multiplayer and in RoE XBox mode when mounted on your pistol
|
|
if( !gameLocal.isMultiplayer && commonVr->currentFlashlightMode != FLASHLIGHT_PISTOL )
|
|
{
|
|
if( flashlight->lightOn )
|
|
{
|
|
if( flashlight_batteryDrainTimeMS.GetInteger() > 0 )
|
|
{
|
|
flashlightBattery -= ( gameLocal.time - gameLocal.previousTime );
|
|
if( flashlightBattery < 0 )
|
|
{
|
|
FlashlightOff();
|
|
flashlightBattery = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( flashlightBattery < flashlight_batteryDrainTimeMS.GetInteger() )
|
|
{
|
|
flashlightBattery += ( gameLocal.time - gameLocal.previousTime ) * Max( 1, ( flashlight_batteryDrainTimeMS.GetInteger() / flashlight_batteryChargeTimeMS.GetInteger() ) );
|
|
if( flashlightBattery > flashlight_batteryDrainTimeMS.GetInteger() )
|
|
{
|
|
flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( hud )
|
|
{
|
|
//hud->UpdateFlashlight( this );
|
|
}
|
|
|
|
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( !flashlight->IsWorldModelReady() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// always make sure the weapon is correctly setup before accessing it
|
|
if( !flashlight->IsLinked() )
|
|
{
|
|
flashlight->GetWeaponDef( "weapon_flashlight_new", 0 );
|
|
flashlight->SetIsPlayerFlashlight( true );
|
|
|
|
// adjust position / orientation of flashlight
|
|
idAnimatedEntity* worldModel = flashlight->GetWorldModel();
|
|
worldModel->BindToJoint( this, "Chest", true );
|
|
// Don't interpolate the flashlight world model in mp, let it bind like normal.
|
|
//worldModel->SetUseClientInterpolation( false );
|
|
|
|
assert( flashlight->IsLinked() );
|
|
}
|
|
|
|
// this positions the third person flashlight model! (as seen in the mirror)
|
|
idAnimatedEntity* worldModel = flashlight->GetWorldModel();
|
|
static const idVec3 fl_pos = idVec3( 3.0f, 9.0f, 2.0f );
|
|
worldModel->GetPhysics()->SetOrigin( fl_pos );
|
|
static float fl_pitch = 0.0f;
|
|
static float fl_yaw = 0.0f;
|
|
static float fl_roll = 0.0f;
|
|
static idAngles ang = ang_zero;
|
|
ang.Set( fl_pitch, fl_yaw, fl_roll );
|
|
worldModel->GetPhysics()->SetAxis( ang.ToMat3() );
|
|
|
|
if( flashlight->lightOn )
|
|
{
|
|
if( ( flashlightBattery < flashlight_batteryChargeTimeMS.GetInteger() / 2 ) && ( gameLocal.random.RandomFloat() < flashlight_batteryFlickerPercent.GetFloat() ) )
|
|
{
|
|
flashlight->RemoveMuzzleFlashlight();
|
|
}
|
|
else
|
|
{
|
|
flashlight->MuzzleFlashLight();
|
|
}
|
|
}
|
|
|
|
flashlight->PresentWeapon( true, 1 - vr_weaponHand.GetInteger() );
|
|
|
|
if( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || gameLocal.inCinematic || spectating || fl.hidden || commonVr->currentFlashlightPosition == FLASHLIGHT_INVENTORY )
|
|
{
|
|
worldModel->Hide();
|
|
}
|
|
else
|
|
{
|
|
worldModel->Show();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::SetViewAngles
|
|
================
|
|
*/
|
|
void idPlayer::SetViewAngles( const idAngles &angles ) {
|
|
UpdateDeltaViewAngles( angles );
|
|
viewAngles = angles;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::UpdateViewAngles
|
|
================
|
|
*/
|
|
void idPlayer::UpdateViewAngles( void ) {
|
|
int i;
|
|
idAngles delta;
|
|
|
|
if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2) ) {
|
|
// no view changes at all, but we still want to update the deltas or else when
|
|
// we get out of this mode, our view will snap to a kind of random angle
|
|
UpdateDeltaViewAngles( viewAngles );
|
|
|
|
// Koz fixme - this was in tmeks fork, verify what we are doing here is still appropriate.
|
|
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] );
|
|
}
|
|
}
|
|
// Koz end
|
|
|
|
return;
|
|
}
|
|
|
|
// if dead
|
|
if ( health <= 0 ) {
|
|
if ( pm_thirdPersonDeath.GetBool() ) {
|
|
viewAngles.roll = 0.0f;
|
|
viewAngles.pitch = 30.0f;
|
|
} else {
|
|
viewAngles.roll = 40.0f;
|
|
viewAngles.pitch = -15.0f;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// circularly clamp the angles with deltas
|
|
for ( i = 1; 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::AdjustHeartRate
|
|
|
|
Player heartrate works as follows
|
|
|
|
DEF_HEARTRATE is resting heartrate
|
|
|
|
Taking damage when health is above 75 adjusts heart rate by 1 beat per second
|
|
Taking damage when health is below 75 adjusts heart rate by 5 beats per second
|
|
Maximum heartrate from damage is MAX_HEARTRATE
|
|
|
|
Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
|
|
|
|
Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
|
|
|
|
All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
|
|
Once it starts falling it always tries to get to DEF_HEARTRATE
|
|
|
|
The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling
|
|
immediately to zero
|
|
|
|
Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is
|
|
scaled linearly based on the actual rate
|
|
|
|
Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
|
|
it is audible or -10db and scales to 8db on the last few beats
|
|
==============
|
|
*/
|
|
void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
|
|
|
|
if ( heartInfo.GetEndValue() == target ) {
|
|
return;
|
|
}
|
|
|
|
if ( AI_DEAD && !force ) {
|
|
return;
|
|
}
|
|
|
|
lastHeartAdjust = gameLocal.time;
|
|
|
|
heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GetBaseHeartRate
|
|
==============
|
|
*/
|
|
int idPlayer::GetBaseHeartRate( void ) {
|
|
int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
|
|
int rate = idMath::FtoiFast( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
|
|
int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
|
|
rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
|
|
return rate;
|
|
}
|
|
|
|
int idPlayer::GetBestWeaponHand()
|
|
{
|
|
if( !weaponEnabled || hands[1 - vr_weaponHand.GetInteger()].idealWeapon == weapon_fists )
|
|
{
|
|
return vr_weaponHand.GetInteger();
|
|
}
|
|
if( hands[ vr_weaponHand.GetInteger() ].idealWeapon == weapon_fists )
|
|
{
|
|
return 1 - vr_weaponHand.GetInteger();
|
|
}
|
|
|
|
int w = MAX_WEAPONS;
|
|
while( w > 0 )
|
|
{
|
|
w--;
|
|
if( w == weapon_flashlight )
|
|
{
|
|
continue;
|
|
}
|
|
const char* weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
common->Printf( "Checking Best Hand = %s\n", spawnArgs.GetString( va( "def_weapon%d", w ) ));
|
|
//if( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) )
|
|
if( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo(weap) ) )
|
|
{
|
|
continue;
|
|
}
|
|
if( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Some weapons will report having ammo but the clip is empty and
|
|
//will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
|
|
//We need to skip these weapons because they cannot be used
|
|
//GB Not sure any of these guns are in D3
|
|
/*
|
|
if( inventory.HasEmptyClipCannotRefill( weap, this, false ) && inventory.HasEmptyClipCannotRefill( weap, this, true ) )
|
|
{
|
|
continue;
|
|
}*/
|
|
|
|
if( hands[ vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return vr_weaponHand.GetInteger();
|
|
if( hands[ 1- vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return 1 - vr_weaponHand.GetInteger();
|
|
}
|
|
w = MAX_WEAPONS;
|
|
while( w > 0 )
|
|
{
|
|
w--;
|
|
if( w == weapon_flashlight )
|
|
{
|
|
continue;
|
|
}
|
|
const char* weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
//if( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) )
|
|
if( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Some weapons will report having ammo but the clip is empty and
|
|
//will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
|
|
//We need to skip these weapons because they cannot be used
|
|
/*if( inventory.HasEmptyClipCannotRefill( weap, this, false ) && inventory.HasEmptyClipCannotRefill( weap, this, true ) )
|
|
{
|
|
continue;
|
|
}*/
|
|
|
|
if( hands[ vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return vr_weaponHand.GetInteger();
|
|
if( hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return 1 - vr_weaponHand.GetInteger();
|
|
}
|
|
w = MAX_WEAPONS;
|
|
while( w > 0 )
|
|
{
|
|
w--;
|
|
if( w == weapon_flashlight )
|
|
{
|
|
continue;
|
|
}
|
|
const char* weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
|
|
if( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( hands[ vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return vr_weaponHand.GetInteger();
|
|
if( hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon == w )
|
|
return 1 - vr_weaponHand.GetInteger();
|
|
}
|
|
|
|
return vr_weaponHand.GetInteger();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::GetCurrentHarvestWeapon
|
|
Carl: Dual wielding, get the specific weapon used to harvest souls (Soul Cube or Artifact)
|
|
Returns the required one if you're holding it, or the other one, or the main weapon
|
|
=================
|
|
*/
|
|
idStr idPlayer::GetCurrentHarvestWeapon( idStr requiredWeapons )
|
|
{
|
|
// Carl: TODO dual wielding
|
|
if( hands[ 0 ].weapon && ( hands[ 0 ].weapon->IdentifyWeapon() == WEAPON_SOULCUBE || hands[ 0 ].weapon->IdentifyWeapon() == WEAPON_ARTIFACT ) )
|
|
return hands[ 0 ].GetCurrentWeaponString();
|
|
if( hands[ 1 ].weapon && ( hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_SOULCUBE || hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_ARTIFACT ) )
|
|
return hands[ 1 ].GetCurrentWeaponString();
|
|
return GetCurrentWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetBestWeaponHandToSteal
|
|
Carl: Dual wielding, when the code needs just one weapon, guess which one is the "main" one
|
|
===============
|
|
*/
|
|
int idPlayer::GetBestWeaponHandToSteal( idPlayer* thief )
|
|
{
|
|
// Carl: TODO dual wielding
|
|
return GetBestWeaponHand();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetCurrentHeartRate
|
|
==============
|
|
*/
|
|
void idPlayer::SetCurrentHeartRate( void ) {
|
|
|
|
int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
|
|
|
|
if ( PowerUpActive( ADRENALINE )) {
|
|
heartRate = 135;
|
|
} else {
|
|
heartRate = idMath::FtoiFast( heartInfo.GetCurrentValue( gameLocal.time ) );
|
|
int currentRate = GetBaseHeartRate();
|
|
if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
|
|
AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
|
|
}
|
|
}
|
|
|
|
int bps = idMath::FtoiFast( 60.0f / heartRate * 1000.0f );
|
|
if ( gameLocal.time - lastHeartBeat > bps ) {
|
|
int dmgVol = DMG_VOLUME;
|
|
int deathVol = DEATH_VOLUME;
|
|
int zeroVol = ZERO_VOLUME;
|
|
float pct = 0.0;
|
|
if ( heartRate > BASE_HEARTRATE && health > 0 ) {
|
|
pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
|
|
pct *= ((float)dmgVol - (float)zeroVol);
|
|
} else if ( health <= 0 ) {
|
|
pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
|
|
if ( pct > 1.0f ) {
|
|
pct = 1.0f;
|
|
} else if (pct < 0.0f) {
|
|
pct = 0.0f;
|
|
}
|
|
pct *= ((float)deathVol - (float)zeroVol);
|
|
}
|
|
|
|
pct += (float)zeroVol;
|
|
|
|
if ( pct != zeroVol ) {
|
|
StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
|
|
// modify just this channel to a custom volume
|
|
soundShaderParms_t parms;
|
|
memset( &parms, 0, sizeof( parms ) );
|
|
parms.volume = pct;
|
|
refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
|
|
}
|
|
|
|
lastHeartBeat = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetFlashHandPose() // Call set flashlight hand pose script function
|
|
Updates the pose of the player model flashlight hand
|
|
======
|
|
*/
|
|
void idPlayer::SetFlashHandPose() // Call set flashlight hand pose script function
|
|
{
|
|
|
|
const function_t* func;
|
|
|
|
func = scriptObject.GetFunction( "SetFlashHandPose" ); // Set flashlight hand pose
|
|
if ( func )
|
|
{
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
// Koz debug common->Printf( "Calling SetFlashHandPose\n" ); // Set flashlight hand pose
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
}
|
|
else
|
|
{
|
|
common->Warning( "Can't find function 'SetFlashHandPose' in object '%s'", scriptObject.GetTypeName() ); // Set flashlight hand pose
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz idPlayer::SetHandIKPos
|
|
Set the position for the hand based on weapon origin
|
|
==============
|
|
Carl: TODO Dual wielding
|
|
*/
|
|
void idPlayer::SetHandIKPos( int hand, idVec3 handOrigin, idMat3 handAxis, idQuat rotation, bool isFlashlight )
|
|
{
|
|
// this is for arm IK when viewing player body
|
|
// the position for the player hand joint is modified
|
|
// to reflect the position of the viewmodel.
|
|
// armIK / reach_ik then performs crude IK on arm using new positon.
|
|
|
|
jointHandle_t weaponHandAttachJoint = INVALID_JOINT; // this is the joint on the WEAPON the hand should meet
|
|
jointHandle_t handWeaponAttachJoint = INVALID_JOINT; // the is the joint on the HAND the weapon should meet
|
|
|
|
static idVec3 weaponHandAttachJointPositionLocal = vec3_zero;
|
|
static idMat3 weaponHandAttachJointAxisLocal = mat3_identity;
|
|
//idVec3 weaponHandAttachJointPositionGlobal = vec3_zero;
|
|
//idMat3 weaponHandAttachJointAxisGlobal = mat3_identity;
|
|
|
|
idVec3 weaponHandAttachJointDefaultPositionLocal = vec3_zero;
|
|
|
|
//idVec3 handWeaponAttachJointPositionLocal = vec3_zero;
|
|
//idMat3 handWeaponAttachJointAxisLocal = mat3_identity;
|
|
//idVec3 handWeaponAttachJointPositionGlobal = vec3_zero;
|
|
//idMat3 handWeaponAttachJointAxisGlobal = mat3_identity;
|
|
|
|
idVec3 weaponAttachDelta = vec3_zero;
|
|
|
|
idVec3 handAttacherPositionLocal = vec3_zero;
|
|
idVec3 handAttacherPositionGlobal = vec3_zero;
|
|
idMat3 handmat = mat3_identity;
|
|
//idVec3 handDelta = vec3_zero;
|
|
|
|
//idMat3 rot180 = idAngles( 0.0f, 180.0f, 0.0f ).ToMat3();
|
|
|
|
|
|
commonVr->currentHandWorldPosition[hand] = handOrigin;
|
|
|
|
hands[hand].handOrigin = handOrigin;
|
|
hands[hand].handAxis = handAxis;
|
|
|
|
weapon_t currentWeaponEnum = hands[hand].weapon->IdentifyWeapon();
|
|
idEntityPtr<idWeapon> curEntity;
|
|
int activeWeapon;
|
|
if( isFlashlight && commonVr->currentFlashlightPosition == FLASHLIGHT_HAND && flashlight.IsValid() )
|
|
{
|
|
curEntity = flashlight;
|
|
activeWeapon = weapon_flashlight;
|
|
}
|
|
else
|
|
{
|
|
curEntity = hands[hand].weapon;
|
|
activeWeapon = hands[hand].currentWeapon;
|
|
}
|
|
|
|
handWeaponAttachJoint = ik_handAttacher[hand]; // joint on the hand the weapon attaches to
|
|
weaponHandAttachJoint = curEntity->weaponHandAttacher[hand]; // joint on the weapon the hand should attach to
|
|
weaponHandAttachJointDefaultPositionLocal = curEntity->weaponHandDefaultPos[hand];// the default position of the attacher on the weapon. used to calc movement deltas from anims
|
|
|
|
|
|
//get the local and global orientations for the hand and weapon attacher joints
|
|
|
|
//weapon hand attacher - the joint on the weapon the hand should align to
|
|
curEntity->GetAnimator()->GetJointTransform( weaponHandAttachJoint, gameLocal.time, weaponHandAttachJointPositionLocal, weaponHandAttachJointAxisLocal );
|
|
|
|
/*
|
|
// for debugging
|
|
weaponHandAttachJointPositionGlobal = weaponHandAttachJointPositionLocal * curEntity->GetRenderEntity()->axis + curEntity->GetRenderEntity()->origin;
|
|
weaponHandAttachJointAxisGlobal = weaponHandAttachJointAxisLocal * curEntity->GetRenderEntity()->axis;
|
|
DebugCross( weaponHandAttachJointPositionGlobal, weaponHandAttachJointAxisGlobal, colorBlue );
|
|
*/
|
|
|
|
// calculate the delta between the weaponAttach joint default position and the animated position for this frame
|
|
// this is in model space
|
|
weaponAttachDelta = weaponHandAttachJointPositionLocal - weaponHandAttachJointDefaultPositionLocal;
|
|
|
|
if ( activeWeapon == weapon_pda && hand == vr_weaponHand.GetInteger() )
|
|
{
|
|
//the PDA is actually being held in the off hand,
|
|
//so don't let the weapon animation adjust the hand position.
|
|
weaponHandAttachJointAxisLocal = mat3_identity;
|
|
weaponAttachDelta = vec3_zero;
|
|
}
|
|
|
|
//handOrigin and handAxis are the points in world space where the attacher joint on the hand model should be moved to.
|
|
//this location is updated by weaponAttachDelta so the weapon animation can drive the hand.
|
|
|
|
handOrigin += handWeaponAttacherToDefaultOffset[hand][activeWeapon] * handAxis;
|
|
|
|
handAttacherPositionGlobal = handOrigin + ( weaponAttachDelta * handAxis );
|
|
|
|
// for debugging
|
|
//DebugCross( handAttacherPositionGlobal, handAxis, colorRed );
|
|
|
|
handAttacherPositionLocal = handAttacherPositionGlobal - renderEntity.origin;
|
|
handAttacherPositionLocal *= renderEntity.axis.Inverse();
|
|
|
|
handmat = weaponHandAttachJointAxisLocal * rotation.ToMat3();
|
|
|
|
handAttacherPositionLocal -= handWeaponAttachertoWristJointOffset[hand][activeWeapon] * handmat;
|
|
|
|
/*
|
|
idVec3 debug1 = handAttacherPositionLocal * renderEntity.axis;
|
|
debug1 += renderEntity.origin;
|
|
DebugCross( debug1, handmat * renderEntity.axis, colorGreen );
|
|
*/
|
|
|
|
handmat = ik_handCorrectAxis[hand][activeWeapon] * handmat;
|
|
|
|
GetAnimator()->SetJointPos( armIK.handJoints[hand], JOINTMOD_WORLD_OVERRIDE, handAttacherPositionLocal );
|
|
GetAnimator()->SetJointAxis( armIK.handJoints[hand], JOINTMOD_WORLD_OVERRIDE, handmat );
|
|
|
|
commonVr->handRoll[hand] = rotation.ToAngles().roll;
|
|
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateAir
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateAir( void ) {
|
|
if ( health <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
// see if the player is connected to the info_vacuum
|
|
bool newAirless = false;
|
|
|
|
if ( gameLocal.vacuumAreaNum != -1 ) {
|
|
int num = GetNumPVSAreas();
|
|
if ( num > 0 ) {
|
|
int areaNum;
|
|
|
|
// if the player box spans multiple areas, get the area from the origin point instead,
|
|
// otherwise a rotating player box may poke into an outside area
|
|
if ( num == 1 ) {
|
|
const int *pvsAreas = GetPVSAreas();
|
|
areaNum = pvsAreas[0];
|
|
} else {
|
|
areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
|
|
}
|
|
newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
|
|
}
|
|
}
|
|
|
|
if ( newAirless ) {
|
|
if ( !airless ) {
|
|
StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
|
|
StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "noAir" );
|
|
}
|
|
}
|
|
airTics--;
|
|
if ( airTics < 0 ) {
|
|
airTics = 0;
|
|
// check for damage
|
|
const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
|
|
int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
|
|
if ( gameLocal.time > lastAirDamage + dmgTiming ) {
|
|
Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
|
|
lastAirDamage = gameLocal.time;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if ( airless ) {
|
|
StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
|
|
StopSound( SND_CHANNEL_BODY2, false );
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "Air" );
|
|
}
|
|
}
|
|
airTics+=2; // regain twice as fast as lose
|
|
if ( airTics > pm_airTics.GetInteger() ) {
|
|
airTics = pm_airTics.GetInteger();
|
|
}
|
|
}
|
|
|
|
airless = newAirless;
|
|
|
|
if ( hud ) {
|
|
hud->SetStateInt( "player_air", 100 * airTics / pm_airTics.GetInteger() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::AddGuiPDAData
|
|
==============
|
|
*/
|
|
int idPlayer::AddGuiPDAData( const declType_t dataType, const char *listName, const idDeclPDA *src, idUserInterface *gui ) {
|
|
int c, i;
|
|
idStr work;
|
|
if ( dataType == DECL_EMAIL ) {
|
|
c = src->GetNumEmails();
|
|
for ( i = 0; i < c; i++ ) {
|
|
const idDeclEmail *email = src->GetEmailByIndex( i );
|
|
if ( email == NULL ) {
|
|
work = va( "-\tEmail %d not found\t-", i );
|
|
} else {
|
|
work = email->GetFrom();
|
|
work += "\t";
|
|
work += email->GetSubject();
|
|
work += "\t";
|
|
work += email->GetDate();
|
|
}
|
|
gui->SetStateString( va( "%s_item_%i", listName, i ), work );
|
|
}
|
|
return c;
|
|
} else if ( dataType == DECL_AUDIO ) {
|
|
c = src->GetNumAudios();
|
|
for ( i = 0; i < c; i++ ) {
|
|
const idDeclAudio *audio = src->GetAudioByIndex( i );
|
|
if ( audio == NULL ) {
|
|
work = va( "Audio Log %d not found", i );
|
|
} else {
|
|
work = audio->GetAudioName();
|
|
}
|
|
gui->SetStateString( va( "%s_item_%i", listName, i ), work );
|
|
}
|
|
return c;
|
|
} else if ( dataType == DECL_VIDEO ) {
|
|
c = inventory.videos.Num();
|
|
for ( i = 0; i < c; i++ ) {
|
|
const idDeclVideo *video = GetVideo( i );
|
|
if ( video == NULL ) {
|
|
work = va( "Video CD %s not found", inventory.videos[i].c_str() );
|
|
} else {
|
|
work = video->GetVideoName();
|
|
}
|
|
gui->SetStateString( va( "%s_item_%i", listName, i ), work );
|
|
}
|
|
return c;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GetPDA
|
|
==============
|
|
*/
|
|
const idDeclPDA *idPlayer::GetPDA( void ) const {
|
|
if ( inventory.pdas.Num() ) {
|
|
return static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[ 0 ] ) );
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
idWeapon * idPlayer::GetPDAWeapon() const
|
|
{
|
|
if( hands[ 1 - vr_weaponHand.GetInteger() ].weapon && hands[ 1 - vr_weaponHand.GetInteger() ].weapon->IdentifyWeapon() == WEAPON_PDA )
|
|
return hands[ 1 - vr_weaponHand.GetInteger() ].weapon;
|
|
else
|
|
return hands[ vr_weaponHand.GetInteger() ].weapon;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GetVideo
|
|
==============
|
|
*/
|
|
const idDeclVideo *idPlayer::GetVideo( int index ) {
|
|
if ( index >= 0 && index < inventory.videos.Num() ) {
|
|
return static_cast< const idDeclVideo* >( declManager->FindType( DECL_VIDEO, inventory.videos[index], false ) );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdatePDAInfo
|
|
==============
|
|
*/
|
|
void idPlayer::UpdatePDAInfo( bool updatePDASel ) {
|
|
int j, sel;
|
|
|
|
if ( objectiveSystem == NULL ) {
|
|
return;
|
|
}
|
|
|
|
assert( hud );
|
|
|
|
int currentPDA = objectiveSystem->State().GetInt( "listPDA_sel_0", "0" );
|
|
if ( currentPDA == -1 ) {
|
|
currentPDA = 0;
|
|
}
|
|
|
|
if ( updatePDASel ) {
|
|
objectiveSystem->SetStateInt( "listPDAVideo_sel_0", 0 );
|
|
objectiveSystem->SetStateInt( "listPDAEmail_sel_0", 0 );
|
|
objectiveSystem->SetStateInt( "listPDAAudio_sel_0", 0 );
|
|
}
|
|
|
|
if ( currentPDA > 0 ) {
|
|
currentPDA = inventory.pdas.Num() - currentPDA;
|
|
}
|
|
|
|
// Mark in the bit array that this pda has been read
|
|
if ( currentPDA < 128 ) {
|
|
inventory.pdasViewed[currentPDA >> 5] |= 1 << (currentPDA & 31);
|
|
}
|
|
|
|
pdaAudio = "";
|
|
pdaVideo = "";
|
|
pdaVideoWave = "";
|
|
idStr name, data, preview, info, wave;
|
|
for ( j = 0; j < MAX_PDAS; j++ ) {
|
|
objectiveSystem->SetStateString( va( "listPDA_item_%i", j ), "" );
|
|
}
|
|
for ( j = 0; j < MAX_PDA_ITEMS; j++ ) {
|
|
objectiveSystem->SetStateString( va( "listPDAVideo_item_%i", j ), "" );
|
|
objectiveSystem->SetStateString( va( "listPDAAudio_item_%i", j ), "" );
|
|
objectiveSystem->SetStateString( va( "listPDAEmail_item_%i", j ), "" );
|
|
objectiveSystem->SetStateString( va( "listPDASecurity_item_%i", j ), "" );
|
|
}
|
|
for ( j = 0; j < inventory.pdas.Num(); j++ ) {
|
|
|
|
const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[j], false ) );
|
|
|
|
if ( pda == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
int index = inventory.pdas.Num() - j;
|
|
if ( j == 0 ) {
|
|
// Special case for the first PDA
|
|
index = 0;
|
|
}
|
|
|
|
if ( j != currentPDA && j < 128 && inventory.pdasViewed[j >> 5] & (1 << (j & 31)) ) {
|
|
// This pda has been read already, mark in gray
|
|
objectiveSystem->SetStateString( va( "listPDA_item_%i", index), va(S_COLOR_GRAY "%s", pda->GetPdaName()) );
|
|
} else {
|
|
// This pda has not been read yet
|
|
objectiveSystem->SetStateString( va( "listPDA_item_%i", index), pda->GetPdaName() );
|
|
}
|
|
|
|
const char *security = pda->GetSecurity();
|
|
if ( j == currentPDA || (currentPDA == 0 && security && *security ) ) {
|
|
if ( *security == 0 ) {
|
|
security = common->GetLanguageDict()->GetString( "#str_00066" );
|
|
}
|
|
objectiveSystem->SetStateString( "PDASecurityClearance", security );
|
|
}
|
|
|
|
if ( j == currentPDA ) {
|
|
|
|
objectiveSystem->SetStateString( "pda_icon", pda->GetIcon() );
|
|
objectiveSystem->SetStateString( "pda_id", pda->GetID() );
|
|
objectiveSystem->SetStateString( "pda_title", pda->GetTitle() );
|
|
|
|
if ( j == 0 ) {
|
|
// Selected, personal pda
|
|
// Add videos
|
|
if ( updatePDASel || !inventory.pdaOpened ) {
|
|
objectiveSystem->HandleNamedEvent( "playerPDAActive" );
|
|
objectiveSystem->SetStateString( "pda_personal", "1" );
|
|
inventory.pdaOpened = true;
|
|
}
|
|
objectiveSystem->SetStateString( "pda_location", hud->State().GetString("location") );
|
|
objectiveSystem->SetStateString( "pda_name", cvarSystem->GetCVarString( "ui_name") );
|
|
AddGuiPDAData( DECL_VIDEO, "listPDAVideo", pda, objectiveSystem );
|
|
sel = objectiveSystem->State().GetInt( "listPDAVideo_sel_0", "0" );
|
|
const idDeclVideo *vid = NULL;
|
|
if ( sel >= 0 && sel < inventory.videos.Num() ) {
|
|
vid = static_cast< const idDeclVideo * >( declManager->FindType( DECL_VIDEO, inventory.videos[ sel ], false ) );
|
|
}
|
|
if ( vid ) {
|
|
pdaVideo = vid->GetRoq();
|
|
pdaVideoWave = vid->GetWave();
|
|
objectiveSystem->SetStateString( "PDAVideoTitle", vid->GetVideoName() );
|
|
objectiveSystem->SetStateString( "PDAVideoVid", vid->GetRoq() );
|
|
objectiveSystem->SetStateString( "PDAVideoIcon", vid->GetPreview() );
|
|
objectiveSystem->SetStateString( "PDAVideoInfo", vid->GetInfo() );
|
|
} else {
|
|
//FIXME: need to precache these in the player def
|
|
objectiveSystem->SetStateString( "PDAVideoVid", "sound/vo/video/welcome.tga" );
|
|
objectiveSystem->SetStateString( "PDAVideoIcon", "sound/vo/video/welcome.tga" );
|
|
objectiveSystem->SetStateString( "PDAVideoTitle", "" );
|
|
objectiveSystem->SetStateString( "PDAVideoInfo", "" );
|
|
}
|
|
} else {
|
|
// Selected, non-personal pda
|
|
// Add audio logs
|
|
if ( updatePDASel ) {
|
|
objectiveSystem->HandleNamedEvent( "playerPDANotActive" );
|
|
objectiveSystem->SetStateString( "pda_personal", "0" );
|
|
inventory.pdaOpened = true;
|
|
}
|
|
objectiveSystem->SetStateString( "pda_location", pda->GetPost() );
|
|
objectiveSystem->SetStateString( "pda_name", pda->GetFullName() );
|
|
int audioCount = AddGuiPDAData( DECL_AUDIO, "listPDAAudio", pda, objectiveSystem );
|
|
objectiveSystem->SetStateInt( "audioLogCount", audioCount );
|
|
sel = objectiveSystem->State().GetInt( "listPDAAudio_sel_0", "0" );
|
|
const idDeclAudio *aud = NULL;
|
|
if ( sel >= 0 ) {
|
|
aud = pda->GetAudioByIndex( sel );
|
|
}
|
|
if ( aud ) {
|
|
pdaAudio = aud->GetWave();
|
|
objectiveSystem->SetStateString( "PDAAudioTitle", aud->GetAudioName() );
|
|
objectiveSystem->SetStateString( "PDAAudioIcon", aud->GetPreview() );
|
|
objectiveSystem->SetStateString( "PDAAudioInfo", aud->GetInfo() );
|
|
} else {
|
|
objectiveSystem->SetStateString( "PDAAudioIcon", "sound/vo/video/welcome.tga" );
|
|
objectiveSystem->SetStateString( "PDAAutioTitle", "" );
|
|
objectiveSystem->SetStateString( "PDAAudioInfo", "" );
|
|
}
|
|
}
|
|
// add emails
|
|
name = "";
|
|
data = "";
|
|
int numEmails = pda->GetNumEmails();
|
|
if ( numEmails > 0 ) {
|
|
AddGuiPDAData( DECL_EMAIL, "listPDAEmail", pda, objectiveSystem );
|
|
sel = objectiveSystem->State().GetInt( "listPDAEmail_sel_0", "-1" );
|
|
if ( sel >= 0 && sel < numEmails ) {
|
|
const idDeclEmail *email = pda->GetEmailByIndex( sel );
|
|
name = email->GetSubject();
|
|
data = email->GetBody();
|
|
}
|
|
}
|
|
objectiveSystem->SetStateString( "PDAEmailTitle", name );
|
|
objectiveSystem->SetStateString( "PDAEmailText", data );
|
|
}
|
|
}
|
|
if ( objectiveSystem->State().GetInt( "listPDA_sel_0", "-1" ) == -1 ) {
|
|
objectiveSystem->SetStateInt( "listPDA_sel_0", 0 );
|
|
}
|
|
objectiveSystem->StateChanged( gameLocal.time );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::TogglePDA
|
|
==============
|
|
*/
|
|
void idPlayer::TogglePDA( int hand ) {
|
|
|
|
// Koz begin : reset PDA controls
|
|
commonVr->forceLeftStick = true;
|
|
|
|
if ( objectiveSystem == NULL ) {
|
|
return;
|
|
}
|
|
|
|
if ( inventory.pdas.Num() == 0 ) {
|
|
objectiveSystem->HandleNamedEvent( "showPDATip" );
|
|
//ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
|
|
return;
|
|
}
|
|
|
|
assert( hud );
|
|
|
|
SetupPDASlot( objectiveSystemOpen );
|
|
SetupHolsterSlot( 1 - hand, objectiveSystemOpen );
|
|
|
|
if ( !objectiveSystemOpen ) {
|
|
int j, c = inventory.items.Num();
|
|
objectiveSystem->SetStateInt( "inv_count", c );
|
|
for ( j = 0; j < MAX_INVENTORY_ITEMS; j++ ) {
|
|
objectiveSystem->SetStateString( va( "inv_name_%i", j ), "" );
|
|
objectiveSystem->SetStateString( va( "inv_icon_%i", j ), "" );
|
|
objectiveSystem->SetStateString( va( "inv_text_%i", j ), "" );
|
|
}
|
|
for ( j = 0; j < c; j++ ) {
|
|
idDict *item = inventory.items[j];
|
|
if ( !item->GetBool( "inv_pda" ) ) {
|
|
const char *iname = item->GetString( "inv_name" );
|
|
const char *iicon = item->GetString( "inv_icon" );
|
|
const char *itext = item->GetString( "inv_text" );
|
|
objectiveSystem->SetStateString( va( "inv_name_%i", j ), iname );
|
|
objectiveSystem->SetStateString( va( "inv_icon_%i", j ), iicon );
|
|
objectiveSystem->SetStateString( va( "inv_text_%i", j ), itext );
|
|
const idKeyValue *kv = item->MatchPrefix( "inv_id", NULL );
|
|
if ( kv ) {
|
|
objectiveSystem->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( j = 0; j < MAX_WEAPONS; j++ ) {
|
|
const char *weapnum = va( "def_weapon%d", j );
|
|
const char *hudWeap = va( "weapon%d", j );
|
|
int weapstate = 0;
|
|
if ( inventory.weapons & ( 1 << j ) ) {
|
|
const char *weap = spawnArgs.GetString( weapnum );
|
|
if ( weap && *weap ) {
|
|
weapstate++;
|
|
}
|
|
}
|
|
objectiveSystem->SetStateInt( hudWeap, weapstate );
|
|
}
|
|
|
|
objectiveSystem->SetStateInt( "listPDA_sel_0", inventory.selPDA );
|
|
objectiveSystem->SetStateInt( "listPDAVideo_sel_0", inventory.selVideo );
|
|
objectiveSystem->SetStateInt( "listPDAAudio_sel_0", inventory.selAudio );
|
|
objectiveSystem->SetStateInt( "listPDAEmail_sel_0", inventory.selEMail );
|
|
UpdatePDAInfo( false );
|
|
UpdateObjectiveInfo();
|
|
objectiveSystem->Activate( true, gameLocal.time );
|
|
hud->HandleNamedEvent( "pdaPickupHide" );
|
|
hud->HandleNamedEvent( "videoPickupHide" );
|
|
} else {
|
|
inventory.selPDA = objectiveSystem->State().GetInt( "listPDA_sel_0" );
|
|
inventory.selVideo = objectiveSystem->State().GetInt( "listPDAVideo_sel_0" );
|
|
inventory.selAudio = objectiveSystem->State().GetInt( "listPDAAudio_sel_0" );
|
|
inventory.selEMail = objectiveSystem->State().GetInt( "listPDAEmail_sel_0" );
|
|
objectiveSystem->Activate( false, gameLocal.time );
|
|
}
|
|
//objectiveSystemOpen ^= 1;
|
|
objectiveSystemOpen = !objectiveSystemOpen;
|
|
}
|
|
|
|
// Carl: Context sensitive VR trigger buttons, and dual wielding
|
|
// 0 = right hand, 1 = left hand; true if pressed, false if released; returns true if handled as trigger pull
|
|
// WARNING! Called from the input thread?
|
|
bool idPlayer::TriggerClickWorld( int hand, bool pressed )
|
|
{
|
|
bool b;
|
|
if( !pressed )
|
|
{
|
|
b = hands[ hand ].triggerDown;
|
|
hands[ hand ].triggerDown = false;
|
|
//common->Printf( " Releasing Trigger\n" );
|
|
return b;
|
|
}
|
|
// if we're holding or floating a weapon, then the trigger button activates the trigger of that weapon
|
|
b = hands[ hand ].controllingWeapon();
|
|
// if we're over a flashlight then the trigger button activates the button of that flashlight
|
|
b = b || ( hands[ hand ].isOverMountedFlashlight() && !hands[ hand ].tooFullToInteract() );
|
|
hands[ hand ].triggerDown = b;
|
|
//common->Printf( " Pressing Trigger = %d\n", b );
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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 != NULL ) || ( IsHidden() == spectating ) );
|
|
|
|
if ( spectating == spectate ) {
|
|
return;
|
|
}
|
|
|
|
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
|
|
ClearPowerUps();
|
|
spectator = this->entityNumber;
|
|
Init();
|
|
StopRagdoll();
|
|
SetPhysics( &physicsObj );
|
|
physicsObj.DisableClip();
|
|
Hide();
|
|
Event_DisableWeapon();
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "aim_clear" );
|
|
MPAimFadeTime = 0;
|
|
}
|
|
} else {
|
|
// put everything back together again
|
|
// put everything back together again
|
|
hands[ 0 ].currentWeapon = -1; // to make sure the def will be loaded if necessary
|
|
hands[ 1 ].currentWeapon = -1;
|
|
Show();
|
|
Event_EnableWeapon();
|
|
SetEyeHeight( pm_normalviewheight.GetFloat() );
|
|
}
|
|
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 );
|
|
}
|
|
|
|
if ( game->isVR )
|
|
{
|
|
commonVr->bodyClip = newClip;
|
|
|
|
static idClipModel* newClip2;
|
|
|
|
bounds[0].Set( -vr_headbbox.GetFloat() * 0.5f, -vr_headbbox.GetFloat() * 0.5f, 0 );
|
|
bounds[1].Set( vr_headbbox.GetFloat() * 0.5f, vr_headbbox.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
|
|
|
|
if ( pm_usecylinder.GetBool() )
|
|
{
|
|
newClip2 = new idClipModel( idTraceModel( bounds, 8 ) );
|
|
newClip2->Translate( physicsObj.PlayerGetOrigin() );
|
|
//physicsObj.SetClipModel( newClip2, 1.0f );
|
|
}
|
|
else
|
|
{
|
|
newClip2 = new idClipModel( idTraceModel( bounds ) );
|
|
newClip2->Translate( physicsObj.PlayerGetOrigin() );
|
|
//physicsObj.SetClipModel( newClip2, 1.0f );
|
|
}
|
|
|
|
commonVr->headClip = newClip2;
|
|
|
|
}
|
|
}
|
|
|
|
void idPlayer::SetControllerShake( float magnitude, int duration, const idVec3 &direction )
|
|
{
|
|
idVec3 dir = direction;
|
|
dir.Normalize();
|
|
idVec3 left = hands[HAND_LEFT].handOrigin - hands[HAND_RIGHT].handOrigin;
|
|
float side = left * dir * 0.5 + 0.5;
|
|
|
|
// push magnitude up so the middle doesn't feel as weak
|
|
float invSide = 1.0 - side;
|
|
float rightSide = 1.0 - side*side;
|
|
float leftSide = 1.0 - invSide*invSide;
|
|
|
|
float leftMag = magnitude * leftSide;
|
|
float rightMag = magnitude * rightSide;
|
|
|
|
hands[HAND_LEFT].SetControllerShake( leftMag, duration, leftMag, duration );
|
|
hands[HAND_RIGHT].SetControllerShake( rightMag, duration, rightMag, duration );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
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 ) {
|
|
|
|
//bool isIntroMap = ( idStr::FindText( gameLocal.GetMapFileName(), "mars_city1" ) >= 0 );
|
|
bool isIntroMap = false;
|
|
|
|
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 );
|
|
}
|
|
|
|
if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 ) {
|
|
SelectWeapon( impulse, false );
|
|
return;
|
|
}
|
|
|
|
|
|
switch( impulse ) {
|
|
case IMPULSE_13: {
|
|
Reload();
|
|
break;
|
|
}
|
|
case IMPULSE_14: {
|
|
NextWeapon();
|
|
break;
|
|
}
|
|
case IMPULSE_15: {
|
|
PrevWeapon();
|
|
break;
|
|
}
|
|
case IMPULSE_16:
|
|
{
|
|
if( flashlight.IsValid() )
|
|
{
|
|
if( flashlight->lightOn )
|
|
{
|
|
FlashlightOff();
|
|
}
|
|
else if( !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
FlashlightOn();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IMPULSE_17: {
|
|
if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
|
|
gameLocal.mpGame.ToggleReady();
|
|
}
|
|
break;
|
|
}
|
|
case IMPULSE_18: {
|
|
centerView.Init(gameLocal.time, 200, viewAngles.pitch, 0);
|
|
break;
|
|
}
|
|
case IMPULSE_19: {
|
|
// when we're not in single player, IMPULSE_19 is used for showScores
|
|
// otherwise it opens the pda
|
|
if ( !gameLocal.isMultiplayer ) {
|
|
if ( objectiveSystemOpen ) {
|
|
TogglePDA( 1 - vr_weaponHand.GetInteger() );
|
|
} else if ( weapon_pda >= 0 && inventory.pdas.Num()) {
|
|
commonVr->pdaToggleTime = commonVr->Sys_Milliseconds();
|
|
SetupPDASlot( false );
|
|
SetupHolsterSlot( vr_weaponHand.GetInteger(), false );
|
|
hands[ 1 - vr_weaponHand.GetInteger() ].SelectWeapon(weapon_pda, true, false);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
// Carl specific fists weapon
|
|
case IMPULSE_26:
|
|
{
|
|
if ( !isIntroMap )
|
|
SelectWeapon( weapon_fists, false, true );
|
|
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;
|
|
}
|
|
// Koz Begin
|
|
// Koz Begin
|
|
case IMPULSE_32: // HMD/Body orienataion reset. Make fisrtperson axis match view direction.
|
|
{
|
|
OrientHMDBody();
|
|
break;
|
|
}
|
|
|
|
case IMPULSE_33: // toggle lasersight on/off.
|
|
{
|
|
ToggleLaserSight();
|
|
break;
|
|
}
|
|
|
|
case IMPULSE_34: // comfort turn right
|
|
{
|
|
// Koz fixme
|
|
// this performs comfort turns for key input,
|
|
// really need to move this to usercmdgen
|
|
/*
|
|
static idAngles angles;
|
|
angles.Set( 0.0f, vr_comfortDelta.GetFloat(), 0.0f );
|
|
SetDeltaViewAngles( deltaViewAngles - angles );
|
|
break;
|
|
*/
|
|
}
|
|
|
|
case IMPULSE_35: // comfort turn left
|
|
{
|
|
// Koz fixme
|
|
// this performs comfort turns for key input,
|
|
// really need to move this to usercmdgen
|
|
/*
|
|
static idAngles angles;
|
|
angles.Set( 0.0f, vr_comfortDelta.GetFloat(), 0.0f );
|
|
SetDeltaViewAngles( deltaViewAngles + angles );
|
|
break;
|
|
*/
|
|
}
|
|
|
|
case IMPULSE_36: // Toggle Hud
|
|
{
|
|
ToggleHud();
|
|
break;
|
|
}
|
|
|
|
case IMPULSE_37: // toggle heading beam
|
|
{
|
|
ToggleHeadingBeam();
|
|
break;
|
|
}
|
|
|
|
case IMPULSE_39:// next flashlight mode
|
|
{
|
|
commonVr->NextFlashlightMode();
|
|
break;
|
|
}
|
|
case IMPULSE_40: {
|
|
UseVehicle();
|
|
break;
|
|
}
|
|
// Koz end
|
|
// Carl:
|
|
case IMPULSE_PAUSE:
|
|
{
|
|
if ( gameLocal.inCinematic)
|
|
{
|
|
|
|
}
|
|
else if ( commonVr->PDAforced && !commonVr->PDAforcetoggle )
|
|
{
|
|
// If we're in the menu, just exit
|
|
PerformImpulse( 40 );
|
|
}
|
|
else
|
|
{
|
|
g_stopTime.SetBool( !g_stopTime.GetBool() );
|
|
}
|
|
break;
|
|
}
|
|
case IMPULSE_RESUME:
|
|
{
|
|
if ( gameLocal.inCinematic)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
g_stopTime.SetBool( false );
|
|
if ( objectiveSystemOpen || ( commonVr->PDAforced && !commonVr->PDAforcetoggle ))
|
|
{
|
|
// If we're in the menu, just exit
|
|
PerformImpulse( 40 );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Carl: Teleport
|
|
=====================
|
|
idPlayer::PointReachableAreaNum
|
|
=====================
|
|
*/
|
|
int idPlayer::PointReachableAreaNum(const idVec3& pos, const float boundsScale) const
|
|
{
|
|
int areaNum;
|
|
idVec3 size;
|
|
idBounds bounds;
|
|
|
|
if (!aas)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
size = aas->GetSettings()->boundingBoxes[0][1] * boundsScale;
|
|
bounds[0] = -size;
|
|
size.z = 32.0f;
|
|
bounds[1] = size;
|
|
|
|
areaNum = aas->PointReachableAreaNum(pos, bounds, AREA_REACHABLE_WALK);
|
|
|
|
return areaNum;
|
|
}
|
|
|
|
bool idPlayer::HandleESC( void ) {
|
|
if ( gameLocal.inCinematic ) {
|
|
return SkipCinematic();
|
|
}
|
|
|
|
if ( objectiveSystemOpen ) {
|
|
TogglePDA( 1 - vr_weaponHand.GetInteger() );
|
|
return true;
|
|
}
|
|
|
|
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 ( health <= 0 ) {
|
|
if ( ( gameLocal.time > minRespawnTime ) && ( 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() && !PowerUpActive( ADRENALINE ) ) {
|
|
stamina -= MS2SEC( USERCMD_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();
|
|
}
|
|
if ( game->isVR )
|
|
{
|
|
speed = ( pm_walkspeed.GetFloat() + vr_walkSpeedAdjust.GetFloat() ) * (1.0f - bobFrac) + pm_runspeed.GetFloat() * bobFrac;
|
|
}
|
|
else
|
|
{
|
|
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( USERCMD_MSEC );
|
|
if ( stamina > pm_stamina.GetFloat() ) {
|
|
stamina = pm_stamina.GetFloat();
|
|
}
|
|
if ( game->isVR )
|
|
{
|
|
speed = pm_walkspeed.GetFloat() + vr_walkSpeedAdjust.GetFloat();
|
|
}
|
|
else
|
|
{
|
|
speed = pm_walkspeed.GetFloat();
|
|
}
|
|
bobFrac = 0.0f;
|
|
}
|
|
|
|
speed *= PowerUpModifier(SPEED);
|
|
|
|
if ( influenceActive == INFLUENCE_LEVEL3 ) {
|
|
speed *= 0.33f;
|
|
}
|
|
|
|
physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::AdjustBodyAngles
|
|
==============
|
|
*/
|
|
void idPlayer::AdjustBodyAngles( void ) {
|
|
idMat3 lookAxis;
|
|
idMat3 legsAxis;
|
|
bool blend;
|
|
float diff;
|
|
float frac;
|
|
float upBlend;
|
|
float forwardBlend;
|
|
float downBlend;
|
|
|
|
if ( health < 0 ) {
|
|
return;
|
|
}
|
|
|
|
blend = true;
|
|
|
|
if ( !physicsObj.HasGroundContacts() ) {
|
|
idealLegsYaw = 0.0f;
|
|
legsForward = true;
|
|
} else if ( usercmd.forwardmove < 0 ) {
|
|
idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
|
|
legsForward = false;
|
|
} else if ( usercmd.forwardmove > 0 ) {
|
|
idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
|
|
legsForward = true;
|
|
} else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
|
|
if ( !legsForward ) {
|
|
idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
|
|
} else {
|
|
idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
|
|
}
|
|
} else if ( usercmd.rightmove != 0 ) {
|
|
idealLegsYaw = 0.0f;
|
|
legsForward = true;
|
|
} else {
|
|
legsForward = true;
|
|
diff = idMath::Fabs( idealLegsYaw - legsYaw );
|
|
idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
|
|
if ( diff < 0.1f ) {
|
|
legsYaw = idealLegsYaw;
|
|
blend = false;
|
|
}
|
|
}
|
|
|
|
if ( !physicsObj.IsCrouching() ) {
|
|
legsForward = true;
|
|
}
|
|
|
|
oldViewYaw = viewAngles.yaw;
|
|
|
|
AI_TURN_LEFT = false;
|
|
AI_TURN_RIGHT = false;
|
|
if ( idealLegsYaw < -45.0f ) {
|
|
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 = viewAngles.pitch / 90.0f;
|
|
//mmdanggg2: stop the model from bending down and getting in the way!!
|
|
if ( frac > 0.0f ) {
|
|
downBlend = 0.0f; // frac;
|
|
forwardBlend = 1.0f;// -frac;
|
|
upBlend = 0.0f;
|
|
}
|
|
else {
|
|
downBlend = 0.0f;
|
|
forwardBlend = 1.0f;// +frac;
|
|
upBlend = 0.0f; // -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::InitPlayerBones
|
|
Koz - moved bone inits here, called during player restore as well.
|
|
==============
|
|
*/
|
|
void idPlayer::InitPlayerBones()
|
|
{
|
|
const char* value;
|
|
|
|
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() );
|
|
}
|
|
|
|
// Koz begin
|
|
value = spawnArgs.GetString( "bone_neck", "" );
|
|
neckJoint = animator.GetJointHandle( value );
|
|
if ( neckJoint == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint '%s' not found for 'bone_neck' on '%s'", value, name.c_str() );
|
|
}
|
|
|
|
value = spawnArgs.GetString( "bone_chest_pivot", "" );
|
|
chestPivotJoint = animator.GetJointHandle( value );
|
|
if ( chestPivotJoint == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint '%s' not found for 'bone_chest_pivot' on '%s'", value, name.c_str() );
|
|
}
|
|
|
|
// we need to load the starting joint orientations for the hands so we can compute correct offsets later
|
|
value = spawnArgs.GetString( "ik_hand1", "" ); // right hand
|
|
ik_hand[0] = animator.GetJointHandle( value );
|
|
if ( ik_hand[0] == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint '%s' not found for 'ik_hand1' on '%s'", value, name.c_str() );
|
|
}
|
|
|
|
value = spawnArgs.GetString( "ik_hand2", "" );// left hand
|
|
ik_hand[1] = animator.GetJointHandle( value );
|
|
if ( ik_hand[1] == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint '%s' not found for 'ik_hand2' on '%s'", value, name.c_str() );
|
|
}
|
|
|
|
ik_handAttacher[0] = animator.GetJointHandle( "RhandWeap" );
|
|
if ( ik_handAttacher[0] == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint RhandWeap not found for player anim default\n" );
|
|
}
|
|
|
|
ik_handAttacher[1] = animator.GetJointHandle( "LhandWeap" );
|
|
|
|
if ( ik_handAttacher[1] == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint LhandWeap not found for player anim default\n" );
|
|
}
|
|
|
|
idStr animPre = "default";// this is the anim that has the default/normal hand and weapon attacher orientations (relationsh
|
|
|
|
int animNo = animator.GetAnim( animPre.c_str() );
|
|
if ( animNo == 0 )
|
|
{
|
|
gameLocal.Error( "Player default animation not found\n" );
|
|
}
|
|
|
|
int numJoints = animator.NumJoints();
|
|
|
|
idJointMat* joints = (idJointMat*)_alloca16( numJoints * sizeof( joints[0] ) );
|
|
|
|
// create the idle default pose ( in this case set to default which should tranlsate to pistol_idle )
|
|
gameEdit->ANIM_CreateAnimFrame( animator.ModelHandle(), animator.GetAnim( animNo )->MD5Anim( 0 ), numJoints, joints, 1, animator.ModelDef()->GetVisualOffset() + modelOffset, animator.RemoveOrigin() );
|
|
|
|
|
|
|
|
static idVec3 defaultWeapAttachOff[2]; // the default distance between the weapon attacher and the hand joint;
|
|
defaultWeapAttachOff[0] = joints[ik_handAttacher[0]].ToVec3() - joints[ik_hand[0]].ToVec3(); // default
|
|
defaultWeapAttachOff[1] = joints[ik_handAttacher[1]].ToVec3() - joints[ik_hand[1]].ToVec3();
|
|
|
|
jointHandle_t j1;
|
|
value = spawnArgs.GetString( "ik_elbow1", "" );// right
|
|
j1 = animator.GetJointHandle( value );
|
|
if ( j1 == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint ik_elbow1 not found for player anim default\n" );
|
|
}
|
|
ik_elbowCorrectAxis[0] = joints[j1].ToMat3();
|
|
|
|
value = spawnArgs.GetString( "ik_elbow2", "" );// left
|
|
j1 = animator.GetJointHandle( value );
|
|
if ( j1 == INVALID_JOINT )
|
|
{
|
|
gameLocal.Error( "Joint ik_elbow2 not found for player anim default\n" );
|
|
}
|
|
ik_elbowCorrectAxis[1] = joints[j1].ToMat3();
|
|
|
|
chestPivotCorrectAxis = joints[chestPivotJoint].ToMat3();
|
|
chestPivotDefaultPos = joints[chestPivotJoint].ToVec3();
|
|
commonVr->chestDefaultDefined = true;
|
|
|
|
|
|
|
|
common->Printf( "Animpre hand 0 default offset = %s\n", defaultWeapAttachOff[0].ToString() );
|
|
common->Printf( "Animpre hand 1 default offset = %s\n", defaultWeapAttachOff[1].ToString() );
|
|
|
|
// now calc the weapon attacher offsets
|
|
for ( int hand = 0; hand < 2; hand++ )
|
|
{
|
|
for ( int weap = 0; weap < 32; weap++ ) // should be max weapons
|
|
{
|
|
|
|
idStr animPre = spawnArgs.GetString( va( "def_weapon%d", weap ) );
|
|
animPre.Strip( "weapon_" );
|
|
animPre += "_idle";
|
|
|
|
int animNo = animator.GetAnim( animPre.c_str() );
|
|
int numJoints = animator.NumJoints();
|
|
|
|
if ( animNo == 0 ) continue;
|
|
|
|
// common->Printf( "Animpre = %s animNo = %d\n", animPre.c_str(), animNo );
|
|
|
|
// create the idle pose for this weapon
|
|
gameEdit->ANIM_CreateAnimFrame( animator.ModelHandle(), animator.GetAnim( animNo )->MD5Anim( 0 ), numJoints, joints, 1, animator.ModelDef()->GetVisualOffset() + modelOffset, animator.RemoveOrigin() );
|
|
|
|
ik_handCorrectAxis[hand][weap] = joints[ik_hand[hand]].ToMat3();
|
|
// common->Printf( "Hand %d weap %d anim %s attacher pos %s default pos %s\n", hand, weap, animPre.c_str(), joints[ik_handAttacher[hand]].ToVec3().ToString(), defaultWeapAttachOff[hand].ToString() );
|
|
|
|
//this is the translation between the hand joint ( the wrist ) and the attacher joint. The attacher joint is
|
|
//the location in space where the motion control is locating the weapon / hand, but IK is using the 'wrist' to
|
|
//drive animation, so use this offset to derive the wrist position from the attacher joint orientation
|
|
handWeaponAttachertoWristJointOffset[hand][weap] = joints[ik_handAttacher[hand]].ToVec3() - joints[ik_hand[hand]].ToVec3();
|
|
|
|
// the is the delta if the attacher joint was moved from the position in the default animation to aid with alignment in
|
|
// different weapon animations. To keep the hand in a consistant location when weapon is changed,
|
|
// the weapon and hand positions will need to be adjusted by this amount when presented
|
|
handWeaponAttacherToDefaultOffset[hand][weap] = handWeaponAttachertoWristJointOffset[hand][weap] - defaultWeapAttachOff[hand];
|
|
|
|
// common->Printf( "Hand %d weap %d anim %s attacher offset = %s\n", hand, weap, animPre.c_str(), handWeaponAttacherToDefaultOffset[hand][weap].ToString() );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::IsSoundChannelPlaying
|
|
===============
|
|
*/
|
|
bool idPlayer::IsSoundChannelPlaying( const s_channelType channel )
|
|
{
|
|
if( GetSoundEmitter() != NULL )
|
|
{
|
|
//return GetSoundEmitter()->CurrentlyPlaying( channel );
|
|
return GetSoundEmitter()->CurrentlyPlaying();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::InitTeleportTarget
|
|
==============
|
|
*/
|
|
void idPlayer::InitTeleportTarget()
|
|
{
|
|
idVec3 origin;
|
|
int targetAnim;
|
|
idStr jointName;
|
|
|
|
skinTelepadCrouch = declManager->FindSkin( "skins/vr/padcrouch" );
|
|
|
|
commonVr->teleportButtonCount = 0;
|
|
|
|
common->Printf( "Initializing teleport target\n" );
|
|
origin = GetPhysics()->GetOrigin() + (origin + modelOffset) * GetPhysics()->GetAxis();
|
|
|
|
teleportTarget = (idAnimatedEntity*)gameLocal.FindEntity( "vrTeleportTarget" );
|
|
if ( !teleportTarget )
|
|
{
|
|
teleportTarget = (idAnimatedEntity*)gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL );
|
|
teleportTarget->SetName( "vrTeleportTarget" );
|
|
}
|
|
teleportTarget->SetModel( "telepad1" );
|
|
teleportTarget->SetOrigin( origin );
|
|
teleportTarget->SetAxis( GetPhysics()->GetAxis() );
|
|
|
|
idAnimatedEntity *duplicate;
|
|
if (duplicate = (idAnimatedEntity*)gameLocal.FindEntity("vrTeleportTarget2"))
|
|
{
|
|
common->Warning("Loading game which had a duplicate vrTeleportTarget.");
|
|
duplicate->PostEventMS(&EV_Remove, 0);
|
|
}
|
|
|
|
teleportTargetAnimator = teleportTarget->GetAnimator();
|
|
targetAnim = teleportTargetAnimator->GetAnim( "idle" );
|
|
//common->Printf( "Teleport target idle anim # = %d\n", targetAnim );
|
|
teleportTargetAnimator->PlayAnim( ANIMCHANNEL_ALL, targetAnim, gameLocal.time, 0 );
|
|
|
|
teleportPadJoint = teleportTargetAnimator->GetJointHandle( "pad" );
|
|
|
|
if ( teleportPadJoint == INVALID_JOINT )
|
|
{
|
|
common->Printf( "Unable to find joint teleportPadJoint \n" );
|
|
}
|
|
|
|
teleportCenterPadJoint = teleportTargetAnimator->GetJointHandle( "centerpad" );
|
|
|
|
if ( teleportCenterPadJoint == INVALID_JOINT )
|
|
{
|
|
common->Printf( "Unable to find joint teleportCenterPadJoint \n" );
|
|
}
|
|
|
|
for ( int i = 0; i < 24; i++ )
|
|
{
|
|
jointName = va( "padbeam%d", i + 1 );
|
|
teleportBeamJoint[i] = teleportTargetAnimator->GetJointHandle( jointName.c_str() );
|
|
if ( teleportBeamJoint[i] == INVALID_JOINT )
|
|
{
|
|
common->Printf( "Unable to find teleportBeamJoint %s\n", jointName.c_str() );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Carl
|
|
=====================
|
|
idPlayer::SetAAS
|
|
=====================
|
|
*/
|
|
void idPlayer::SetAAS( bool forceAAS48 )
|
|
{
|
|
idStr use_aas;
|
|
|
|
spawnArgs.GetString("use_aas", NULL, use_aas);
|
|
gameLocal.Printf("Player AAS: use_aas = %s\n", use_aas.c_str());
|
|
aas = gameLocal.GetAAS(use_aas);
|
|
// Carl: use our own custom generated AAS specifically for player movement (teleporting).
|
|
if (!aas && !forceAAS48 )
|
|
aas = gameLocal.GetAAS("aas_player");
|
|
// Every map has aas48, used for zombies and imps. It's close enough to player.
|
|
if (!aas || forceAAS48 )
|
|
aas = gameLocal.GetAAS("aas48");
|
|
if (aas)
|
|
{
|
|
const idAASSettings* settings = aas->GetSettings();
|
|
if (settings)
|
|
{
|
|
/*
|
|
if (!ValidForBounds(settings, physicsObj.GetBounds()))
|
|
{
|
|
gameLocal.Error("%s cannot use use_aas %s\n", name.c_str(), use_aas.c_str());
|
|
}
|
|
*/
|
|
float height = settings->maxStepHeight;
|
|
gameLocal.Printf("Player AAS = %s: AAS step height = %f, player step height = %f\n", settings->fileExtension.c_str(), height, pm_stepsize.GetFloat());
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
aas = NULL;
|
|
}
|
|
}
|
|
gameLocal.Printf("WARNING: Player %s has no AAS file\n", name.c_str());
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetAASLocation
|
|
==============
|
|
*/
|
|
void idPlayer::SetAASLocation( void ) {
|
|
int i;
|
|
int areaNum;
|
|
idVec3 size;
|
|
idBounds bounds;
|
|
idAAS *aas;
|
|
idVec3 origin;
|
|
|
|
if ( !GetFloorPos( 64.0f, origin ) ) {
|
|
return;
|
|
}
|
|
|
|
for( i = 0; i < aasLocation.Num(); i++ ) {
|
|
aas = gameLocal.GetAAS( i );
|
|
if ( !aas ) {
|
|
continue;
|
|
}
|
|
|
|
size = aas->GetSettings()->boundingBoxes[0][1];
|
|
bounds[0] = -size;
|
|
size.z = 32.0f;
|
|
bounds[1] = size;
|
|
|
|
areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
|
|
if ( areaNum ) {
|
|
aasLocation[ i ].pos = origin;
|
|
aasLocation[ i ].areaNum = areaNum;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GetAASLocation
|
|
==============
|
|
*/
|
|
void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
|
|
int i;
|
|
|
|
if ( aas != NULL ) {
|
|
for( i = 0; i < aasLocation.Num(); i++ ) {
|
|
if ( aas == gameLocal.GetAAS( i ) ) {
|
|
areaNum = aasLocation[ i ].areaNum;
|
|
pos = aasLocation[ i ].pos;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
areaNum = 0;
|
|
pos = physicsObj.GetOrigin();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::Move
|
|
==============
|
|
*/
|
|
void idPlayer::Move( void ) {
|
|
float newEyeOffset;
|
|
idVec3 oldOrigin;
|
|
idVec3 oldVelocity;
|
|
idVec3 pushVelocity;
|
|
|
|
static bool testLean = false;
|
|
static idVec3 leanOrigin = vec3_zero;
|
|
|
|
// 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() );
|
|
|
|
{
|
|
idVec3 org;
|
|
idMat3 axis;
|
|
if ( !game->isVR )
|
|
{
|
|
GetViewPos( org, axis ); // Koz default movement
|
|
physicsObj.SetPlayerInput( usercmd, axis[0] );
|
|
}
|
|
else
|
|
{
|
|
if ( !physicsObj.OnLadder() ) // Koz fixme, dont move if on a ladder or player will fall/stick
|
|
{
|
|
|
|
idVec3 bodyOrigin = vec3_zero;
|
|
idVec3 movedBodyOrigin = vec3_zero;
|
|
idVec3 movedRemainder = vec3_zero;
|
|
idMat3 bodyAxis;
|
|
idMat3 origPhysAxis;
|
|
|
|
GetViewPos( bodyOrigin, bodyAxis );
|
|
bodyOrigin = physicsObj.GetOrigin();
|
|
origPhysAxis = physicsObj.GetAxis();
|
|
|
|
idVec3 newBodyOrigin;
|
|
|
|
idAngles bodyAng = bodyAxis.ToAngles();
|
|
idMat3 bodyAx = idAngles( bodyAng.pitch, bodyAng.yaw - commonVr->bodyYawOffset, bodyAng.roll ).Normalize180().ToMat3();
|
|
|
|
newBodyOrigin = bodyOrigin + bodyAx[0] * commonVr->remainingMoveHmdBodyPositionDelta.x + bodyAx[1] * commonVr->remainingMoveHmdBodyPositionDelta.y;
|
|
commonVr->remainingMoveHmdBodyPositionDelta.x = commonVr->remainingMoveHmdBodyPositionDelta.y = 0;
|
|
|
|
commonVr->motionMoveDelta = newBodyOrigin - bodyOrigin;
|
|
commonVr->motionMoveVelocity = commonVr->motionMoveDelta / ((1000 / commonVr->hmdHz) * 0.001f);
|
|
|
|
if ( !commonVr->isLeaning )
|
|
{
|
|
movedBodyOrigin = physicsObj.MotionMove( commonVr->motionMoveVelocity );
|
|
physicsObj.SetAxis( origPhysAxis ); // make sure motion move doesnt change the axis
|
|
|
|
movedRemainder = (newBodyOrigin - movedBodyOrigin);
|
|
|
|
if ( movedRemainder.Length() > commonVr->motionMoveDelta.Length() * 0.25f )
|
|
{
|
|
commonVr->isLeaning = true;
|
|
testLean = false;
|
|
leanOrigin = movedBodyOrigin;
|
|
commonVr->leanOffset = movedRemainder;
|
|
}
|
|
else
|
|
{
|
|
// if the pda is fixed in space, we need to keep track of how much we have moved the player body
|
|
// so we can keep the PDA in the same position relative to the player while accounting for external movement ( on a lift / eleveator etc )
|
|
if ( !hands[0].PDAfixed && !hands[1].PDAfixed )
|
|
{
|
|
commonVr->fixedPDAMoveDelta = vec3_zero;
|
|
}
|
|
else
|
|
{
|
|
commonVr->fixedPDAMoveDelta += (movedBodyOrigin - bodyOrigin);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
|
|
{
|
|
// player body blocked by object. let the head move some by accruing all ( body ) movement here.
|
|
// check to see if player body can move to the new location without clipping anything
|
|
// if it can, move it and clear leanoffsets, otherwise limit the distance
|
|
idVec3 testOrigin = vec3_zero;
|
|
|
|
commonVr->leanOffset += commonVr->motionMoveDelta;
|
|
|
|
if ( commonVr->leanOffset.LengthSqr() > 36.0f * 36.0f ) // dont move head more than 36 inches // Koz fixme should me measure distance from waist?
|
|
{
|
|
commonVr->leanOffset.Normalize();
|
|
commonVr->leanOffset *= 36.0f;
|
|
}
|
|
|
|
if ( commonVr->leanBlank )
|
|
{
|
|
if ( commonVr->leanOffset.LengthSqr() > commonVr->leanBlankOffsetLengthSqr )
|
|
{
|
|
commonVr->leanOffset = commonVr->leanBlankOffset;
|
|
}
|
|
}
|
|
|
|
testOrigin = bodyOrigin + commonVr->leanOffset;
|
|
|
|
if ( commonVr->leanOffset.LengthSqr() > 4.0f || bodyOrigin != leanOrigin ) testLean = true; // dont check to cancel lean until player body has moved, or head has moved at least two inches.
|
|
|
|
if ( testLean )
|
|
{
|
|
// clip against the player clipmodel
|
|
trace_t trace;
|
|
idMat3 clipAxis;
|
|
|
|
idClipModel* clip;
|
|
clip = physicsObj.GetClipModel();
|
|
clipAxis = physicsObj.GetClipModel()->GetAxis();
|
|
|
|
|
|
gameLocal.clip.Translation( trace, testOrigin, testOrigin, clip, clipAxis, MASK_SHOT_RENDERMODEL /* CONTENTS_SOLID */, this );
|
|
if ( trace.fraction < 1.0f )
|
|
{
|
|
|
|
// do ik stuff here
|
|
// trying to do this now in player walkIk
|
|
|
|
}
|
|
else
|
|
{
|
|
// not leaning, clear the offsets and move the player origin
|
|
physicsObj.SetOrigin( testOrigin );
|
|
commonVr->isLeaning = false;
|
|
//common->Printf("Setting Leaning FALSE %d\n", Sys_Milliseconds());
|
|
commonVr->leanOffset = vec3_zero;
|
|
//animator.ClearJoint( chestPivotJoint );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GetViewPos( org, axis ); // Koz default movement
|
|
physicsObj.SetPlayerInput( usercmd, axis[0] );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// FIXME: physics gets disabled somehow
|
|
BecomeActive( TH_PHYSICS );
|
|
// Carl: check if we're experiencing artificial locomotion
|
|
idVec3 before = physicsObj.GetOrigin();
|
|
RunPhysics();
|
|
idVec3 after = physicsObj.GetOrigin();
|
|
|
|
// 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() ) {
|
|
// Koz begin
|
|
if ( game->isVR )
|
|
{
|
|
if ( vr_crouchMode.GetInteger() != 0 || (usercmd.buttons & BUTTON_CROUCH) )
|
|
{
|
|
newEyeOffset = 34; //Carl: When showing our body, our body doesn't crouch enough, so move eyes as high as possible (any higher and the top of our head wouldn't fit)
|
|
if ( vr_crouchMode.GetInteger() != 0 && commonVr->poseHmdHeadPositionDelta.z < -vr_crouchTriggerDist.GetFloat() )
|
|
{
|
|
// crouch was initiated by the trigger, adjust eyeOffset by trigger val so view isnt too low.
|
|
newEyeOffset += vr_crouchTriggerDist.GetFloat();
|
|
}
|
|
}
|
|
else
|
|
newEyeOffset = pm_normalviewheight.GetFloat();
|
|
}
|
|
else
|
|
{
|
|
newEyeOffset = pm_crouchviewheight.GetFloat();
|
|
}
|
|
// Koz end
|
|
} else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
|
|
newEyeOffset = 0.0f;
|
|
} else {
|
|
newEyeOffset = pm_normalviewheight.GetFloat();
|
|
}
|
|
float oldEyeOffset = EyeHeight();
|
|
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 );
|
|
|
|
// Carl: Motion sickness detection
|
|
float distance = ((after - before) - commonVr->motionMoveDelta).LengthSqr();
|
|
static float oldHeadHeightDiff = 0;
|
|
float crouchDistance = EyeHeight() + commonVr->headHeightDiff - oldEyeOffset - oldHeadHeightDiff;
|
|
oldHeadHeightDiff = commonVr->headHeightDiff;
|
|
distance += crouchDistance * crouchDistance + viewBob.LengthSqr();
|
|
blink = (distance > 0.005f);
|
|
|
|
|
|
CrashLand( oldOrigin, oldVelocity );
|
|
|
|
// Handling vr_comfortMode
|
|
if (vr_motionSickness.IsModified() && !warpMove && !warpAim)
|
|
{
|
|
timescale.SetFloat(1);
|
|
vr_motionSickness.ClearModified();
|
|
}
|
|
const int comfortMode = vr_motionSickness.GetInteger();
|
|
//" 0 off | 2 = tunnel | 5 = tunnel + chaperone | 6 slow mo | 7 slow mo + chaperone | 8 tunnel + slow mo | 9 = tunnel + slow mo + chaperone
|
|
if ( comfortMode < 2 || game->InCinematic() )
|
|
{
|
|
this->playerView.EnableVrComfortVision( false );
|
|
commonVr->thirdPersonMovement = false;
|
|
return;
|
|
}
|
|
|
|
float speed = physicsObj.GetLinearVelocity().LengthFast();
|
|
if ( comfortMode == 10 && speed == 0 && usercmd.forwardmove == 0 && usercmd.rightmove == 0 )
|
|
commonVr->thirdPersonMovement = false;
|
|
|
|
if (( comfortMode == 2 ) || ( comfortMode == 5 ) || ( comfortMode == 8 ) || ( comfortMode == 9 ))
|
|
{
|
|
if (speed == 0 && !blink)
|
|
{
|
|
this->playerView.EnableVrComfortVision(false);
|
|
}
|
|
else
|
|
{
|
|
this->playerView.EnableVrComfortVision(true);
|
|
}
|
|
}
|
|
else
|
|
this->playerView.EnableVrComfortVision(false);
|
|
|
|
if ((( comfortMode == 6 ) || ( comfortMode == 7 ) || ( comfortMode == 8 ) || ( comfortMode == 9 )) && !warpAim && !warpMove )
|
|
{
|
|
float speedFactor = (( pm_runspeed.GetFloat() - speed ) / pm_runspeed.GetFloat());
|
|
if ( speedFactor < 0 )
|
|
{
|
|
speedFactor = 0;
|
|
}
|
|
timescale.SetFloat( 0.5f + 0.5f * speedFactor );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::Move_Interpolated
|
|
==============
|
|
*/
|
|
void idPlayer::Move_Interpolated( float fraction )
|
|
{
|
|
|
|
float newEyeOffset = 0.0f;
|
|
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 if( mountedObject )
|
|
{
|
|
physicsObj.SetContents( 0 );
|
|
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() );
|
|
|
|
{
|
|
idVec3 org;
|
|
idMat3 axis;
|
|
GetViewPos( org, axis );
|
|
|
|
physicsObj.SetPlayerInput( usercmd, axis[0] );
|
|
}
|
|
|
|
// FIXME: physics gets disabled somehow
|
|
BecomeActive( TH_PHYSICS );
|
|
//GB Dont think this function is ever used (MoveInterpolated)
|
|
//InterpolatePhysics( fraction );
|
|
|
|
// 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() )
|
|
{
|
|
// Koz begin
|
|
// dont change the eyeoffset if using full motion crouch.
|
|
if ( game->isVR )
|
|
{
|
|
if ( vr_crouchMode.GetInteger() != 0 || (usercmd.buttons & BUTTON_CROUCH) )
|
|
{
|
|
newEyeOffset = 34; //Carl: When showing our body, our body doesn't crouch enough, so move eyes as high as possible (any higher and the top of our head wouldn't fit)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newEyeOffset = pm_crouchviewheight.GetFloat();
|
|
}
|
|
// Koz end
|
|
}
|
|
else if( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) )
|
|
{
|
|
newEyeOffset = 0.0f;
|
|
}
|
|
// Koz begin
|
|
else if ( game->isVR )
|
|
{
|
|
newEyeOffset = pm_normalviewheight.GetFloat();
|
|
//Carl: Our body is too tall, so move our eyes higher so they don't clip the body
|
|
}
|
|
// Koz end
|
|
else
|
|
{
|
|
newEyeOffset = pm_normalviewheight.GetFloat();
|
|
}
|
|
|
|
if( EyeHeight() != newEyeOffset ) // Koz fixme - do we want a slow or instant crouch in VR?
|
|
{
|
|
if( spectating )
|
|
{
|
|
SetEyeHeight( newEyeOffset );
|
|
}
|
|
else
|
|
{
|
|
// smooth out duck height changes
|
|
SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
|
|
}
|
|
}
|
|
|
|
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( void ) {
|
|
idPlayer *aimed;
|
|
|
|
if ( !hud ) {
|
|
return;
|
|
}
|
|
|
|
if ( entityNumber != gameLocal.localClientNum ) {
|
|
return;
|
|
}
|
|
|
|
if ( game->isVR )
|
|
{
|
|
UpdateVrHud();
|
|
}
|
|
|
|
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 = 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::SetupFlashlightSlot
|
|
==============
|
|
*/
|
|
void idPlayer::SetupFlashlightHolster()
|
|
{
|
|
memset( &flashlightRenderEntity, 0, sizeof( flashlightRenderEntity ) );
|
|
flashlightRenderEntity.hModel = renderModelManager->FindModel( "models/items/flashlight/flashlight2_world.lwo" );
|
|
if( flashlightRenderEntity.hModel )
|
|
{
|
|
flashlightRenderEntity.hModel->Reset();
|
|
flashlightRenderEntity.bounds = flashlightRenderEntity.hModel->Bounds( &flashlightRenderEntity );
|
|
}
|
|
flashlightRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
|
flashlightRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
|
|
flashlightRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
|
|
flashlightRenderEntity.shaderParms[ SHADERPARM_ALPHA ] = 0.75f;
|
|
flashlightRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
|
|
flashlightRenderEntity.shaderParms[5] = 0.0f;
|
|
flashlightRenderEntity.shaderParms[6] = 0.0f;
|
|
flashlightRenderEntity.shaderParms[7] = 0.0f;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateFlashlightHolster
|
|
==============
|
|
*/
|
|
void idPlayer::UpdateFlashlightHolster()
|
|
{
|
|
/*
|
|
* bool hasFlashlight = !( ( weapon_flashlight < 0 ) || ( inventory.weapons & ( 1 << weapon_flashlight ) ) == 0 );
|
|
if( hasFlashlight &&
|
|
GetCurrentWeaponId() != weapon_flashlight &&
|
|
flashlightRenderEntity.hModel &&
|
|
pVRClientInfo != nullptr)
|
|
{
|
|
idVec3 holsterOffset( -pVRClientInfo->flashlightHolsterOffset[2],
|
|
-pVRClientInfo->flashlightHolsterOffset[0],
|
|
pVRClientInfo->flashlightHolsterOffset[1]);
|
|
|
|
idAngles a(0, viewAngles.yaw - pVRClientInfo->hmdorientation[YAW], 0);
|
|
holsterOffset *= a.ToMat3();
|
|
holsterOffset *= vr_worldscale.GetFloat();
|
|
flashlightRenderEntity.origin = firstPersonViewOrigin + holsterOffset;
|
|
|
|
flashlightRenderEntity.entityNum = ENTITYNUM_NONE;
|
|
|
|
flashlightRenderEntity.axis = idAngles(-60, 90, 0).ToMat3() * firstPersonViewAxis * 0.8f;
|
|
|
|
flashlightRenderEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
flashlightRenderEntity.weaponDepthHack = true;
|
|
|
|
if( flashlightModelDefHandle == -1 )
|
|
{
|
|
flashlightModelDefHandle = gameRenderWorld->AddEntityDef( &flashlightRenderEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( flashlightModelDefHandle, &flashlightRenderEntity );
|
|
}
|
|
}
|
|
else if (!hasFlashlight || // User hasn't got flashlight yet
|
|
GetCurrentWeaponId() == weapon_flashlight)
|
|
{
|
|
if( flashlightModelDefHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeEntityDef( flashlightModelDefHandle );
|
|
flashlightModelDefHandle = -1;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::Think
|
|
|
|
Called every tic for each player
|
|
==============
|
|
*/
|
|
void idPlayer::Think( void ) {
|
|
renderEntity_t *headRenderEnt;
|
|
|
|
UpdatePlayerIcons();
|
|
|
|
UpdateSkinSetup();
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (pVRClientInfo != nullptr)
|
|
{
|
|
if (inventory.weapons > 1) {
|
|
pVRClientInfo->weaponid = GetCurrentWeaponId();
|
|
}
|
|
else {
|
|
pVRClientInfo->weaponid = -1;
|
|
}
|
|
cvarSystem->SetCVarBool("vr_weapon_stabilised", pVRClientInfo->weapon_stabilised);
|
|
pVRClientInfo->velocitytriggered = GetCurrentWeaponId() == WEAPON_FLASHLIGHT;
|
|
pVRClientInfo->pistol = GetCurrentWeaponId() == WEAPON_PISTOL;
|
|
}
|
|
|
|
// clear the ik before we do anything else so the skeleton doesn't get updated twice
|
|
walkIK.ClearJointMods();
|
|
|
|
// Koz
|
|
armIK.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( 1 - vr_weaponHand.GetInteger() );
|
|
}
|
|
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 );
|
|
}
|
|
|
|
/* No Zoom in VR
|
|
// zooming
|
|
if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
|
|
if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon ) {
|
|
zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon->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 weapons before running the physics
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].weapon )
|
|
hands[h].weapon->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
|
|
}
|
|
|
|
EvaluateControls();
|
|
|
|
if ( !af.IsActive() ) {
|
|
AdjustBodyAngles();
|
|
CopyJointsFromBodyToHead();
|
|
}
|
|
|
|
Move();
|
|
SetWeaponHandPose();
|
|
SetFlashHandPose(); // Call set flashlight hand pose script function
|
|
|
|
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(); // Koz moved to just before update flashlight.
|
|
|
|
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();
|
|
}
|
|
|
|
// Koz
|
|
UpdateNeckPose();
|
|
|
|
UpdateFocus(); // Koz move here update GUIs, Items, and character interactions.
|
|
|
|
UpdateFlashlight();
|
|
|
|
UpdateAir();
|
|
|
|
UpdateHud();
|
|
|
|
UpdatePowerUps();
|
|
|
|
//UpdateFlashlightHolster();
|
|
|
|
//Dr Beef version - maybe minimise
|
|
//UpdateLaserSight();
|
|
UpdateVrHud();
|
|
|
|
UpdateDeathSkin( false );
|
|
|
|
if ( gameLocal.isMultiplayer ) {
|
|
DrawPlayerIcons();
|
|
}
|
|
|
|
static int lastFlashlightMode = commonVr->GetCurrentFlashlightMode();
|
|
static bool lastFists = false;
|
|
static bool lastHandInGui = false;
|
|
|
|
// Koz turn body on or off in vr, update hand poses/skins if body or weapon hand changes.
|
|
if ( vr_weaponHand.IsModified() )
|
|
{
|
|
UpdatePlayerSkinsPoses();
|
|
vr_weaponHand.ClearModified();
|
|
|
|
}
|
|
if ( vr_flashlightMode.IsModified() || lastFlashlightMode != commonVr->GetCurrentFlashlightMode() )
|
|
{
|
|
if ( vr_flashlightMode.IsModified() )
|
|
{
|
|
commonVr->currentFlashlightMode = vr_flashlightMode.GetInteger();
|
|
vr_flashlightMode.ClearModified();
|
|
}
|
|
|
|
lastFlashlightMode = commonVr->GetCurrentFlashlightMode();
|
|
UpdatePlayerSkinsPoses();
|
|
}
|
|
|
|
if ( head ) {
|
|
headRenderEnt = head->GetRenderEntity();
|
|
} else {
|
|
headRenderEnt = NULL;
|
|
}
|
|
|
|
if ( headRenderEnt ) {
|
|
if ( influenceSkin ) {
|
|
headRenderEnt->customSkin = influenceSkin;
|
|
} else {
|
|
headRenderEnt->customSkin = NULL;
|
|
}
|
|
|
|
// Koz show the head if the player is using third person movement mode && the model has moved more than 8 inches.
|
|
if ( commonVr->thirdPersonMovement && commonVr->thirdPersonDelta > 45.0f )
|
|
{
|
|
headRenderEnt->suppressSurfaceInViewID = 0; // show head
|
|
headRenderEnt->allowSurfaceInViewID = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( vr_playerBodyMode.GetInteger() != 0 ) // not showing the body
|
|
{
|
|
headRenderEnt->allowSurfaceInViewID = -1; // hide the head, even in mirror views.
|
|
}
|
|
else
|
|
{
|
|
//headRenderEnt->suppressSurfaceInViewID = entityNumber + 1;
|
|
headRenderEnt->allowSurfaceInViewID = 0; // show the head.
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
|
|
renderEntity.suppressShadowInViewID = 0;
|
|
if ( headRenderEnt ) {
|
|
headRenderEnt->suppressShadowInViewID = 0;
|
|
}
|
|
} else {
|
|
renderEntity.suppressShadowInViewID = entityNumber+1;
|
|
if( headRenderEnt )
|
|
{
|
|
// Koz begin
|
|
if ( game->isVR && vr_playerBodyMode.GetInteger() == 0 ) // show the head shadow
|
|
{
|
|
headRenderEnt->suppressShadowInViewID = 0;
|
|
}
|
|
else
|
|
{
|
|
headRenderEnt->suppressShadowInViewID = entityNumber + 1;
|
|
}
|
|
// Koz end
|
|
}
|
|
}
|
|
// 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() ) {
|
|
if ( armIK.IsInitialized() ) armIK.Evaluate();
|
|
|
|
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 );
|
|
}
|
|
|
|
// stereo rendering laser sight that replaces the crosshair
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
UpdateLaserSight( h );
|
|
}
|
|
UpdateTeleportAim();
|
|
|
|
if ( vr_teleportMode.GetInteger() != 0 )
|
|
{
|
|
if (warpMove ) {
|
|
if ( gameLocal.time > warpTime )
|
|
{
|
|
extern idCVar timescale;
|
|
warpTime = 0;
|
|
noclip = false;
|
|
warpMove = false;
|
|
warpVel = vec3_origin;
|
|
timescale.SetFloat( 1.0f );
|
|
//playerView.EnableBFGVision(false);
|
|
Teleport( warpDest, viewAngles, NULL ); //Carl: get the destination exact
|
|
}
|
|
physicsObj.SetLinearVelocity( warpVel );
|
|
}
|
|
|
|
if ( jetMove ) {
|
|
|
|
if ( gameLocal.time > jetMoveTime )
|
|
{
|
|
jetMoveTime = 0;
|
|
jetMove = false;
|
|
jetMoveVel = vec3_origin;
|
|
jetMoveCoolDownTime = gameLocal.time + 30;
|
|
}
|
|
|
|
physicsObj.SetLinearVelocity( jetMoveVel );
|
|
|
|
}
|
|
}
|
|
|
|
if ( game->isVR ) UpdateHeadingBeam(); // Koz
|
|
UpdatePDASlot();
|
|
UpdateHolsterSlot();
|
|
|
|
if( vr_slotDebug.GetBool() )
|
|
{
|
|
for( int i = 0; i < SLOT_COUNT; i++ )
|
|
{
|
|
idVec3 slotOrigin = slots[i].origin;
|
|
if ( vr_weaponHand.GetInteger() && i != SLOT_FLASHLIGHT_SHOULDER )
|
|
slotOrigin.y *= -1;
|
|
idVec3 origin = waistOrigin + slotOrigin * waistAxis;
|
|
idSphere tempSphere( origin, sqrtf(slots[i].radiusSq) );
|
|
gameRenderWorld->DebugSphere( colorWhite, tempSphere, 18, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::ToggleHud Koz toggle hud
|
|
==============
|
|
*/
|
|
void idPlayer::ToggleHud()
|
|
{
|
|
hudActive = !hudActive;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::ToggleLaserSight Koz toggle the lasersight
|
|
==============
|
|
*/
|
|
void idPlayer::ToggleLaserSight()
|
|
{
|
|
if( !hands[ 0 ].laserSightActive || !hands[ 1 ].laserSightActive )
|
|
{
|
|
hands[ 0 ].laserSightActive = true;
|
|
hands[ 1 ].laserSightActive = true;
|
|
}
|
|
else
|
|
{
|
|
hands[ 0 ].laserSightActive = false;
|
|
hands[ 1 ].laserSightActive = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::ToggleHeadingBeam Koz toggle heading beam
|
|
==============
|
|
*/
|
|
void idPlayer::ToggleHeadingBeam()
|
|
{
|
|
if ( vr_headingBeamMode.GetInteger() != 0 )
|
|
{
|
|
headingBeamActive = !headingBeamActive;
|
|
}
|
|
}
|
|
|
|
// Carl: Context sensitive VR thumb clicks, and dual wielding
|
|
// 0 = right hand, 1 = left hand; true if pressed, false if released; returns true if handled as thumb click
|
|
// WARNING! Called from the input thread?
|
|
bool idPlayer::ThumbClickWorld( int hand, bool pressed )
|
|
{
|
|
bool b;
|
|
if( !pressed )
|
|
{
|
|
b = hands[ hand ].thumbDown;
|
|
hands[ hand ].thumbDown = false;
|
|
return b;
|
|
}
|
|
// if we're holding or over a flashlight, then the thumb button activates the button of that flashlight
|
|
b = hands[ hand ].holdingFlashlight();
|
|
b = b || ( hands[ hand ].isOverMountedFlashlight() && !hands[ hand ].tooFullToInteract() );
|
|
hands[ hand ].thumbDown = b;
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::RouteGuiMouse
|
|
=================
|
|
*/
|
|
void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
|
|
sysEvent_t ev;
|
|
|
|
if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
|
|
ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
|
|
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 ( 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 ) {
|
|
float delay;
|
|
|
|
assert( !gameLocal.isClient );
|
|
|
|
// stop taking knockback once dead
|
|
fl.noknockback = true;
|
|
if ( health < -999 ) {
|
|
health = -999;
|
|
}
|
|
|
|
if ( AI_DEAD ) {
|
|
AI_PAIN = true;
|
|
return;
|
|
}
|
|
|
|
heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
|
|
AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
|
|
|
|
if ( !g_testDeath.GetBool() ) {
|
|
playerView.Fade( colorBlack, 12000 );
|
|
}
|
|
|
|
AI_DEAD = true;
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
|
|
SetWaitState( "" );
|
|
|
|
animator.ClearAllJoints();
|
|
|
|
if ( StartRagdoll() ) {
|
|
pm_modelView.SetInteger( 0 );
|
|
minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
|
|
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
|
|
} else {
|
|
// don't allow respawn until the death anim is done
|
|
// g_forcerespawn may force spawning at some later time
|
|
delay = spawnArgs.GetFloat( "respawn_delay" );
|
|
minRespawnTime = gameLocal.time + SEC2MS( delay );
|
|
maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
|
|
}
|
|
|
|
physicsObj.SetMovementType( PM_DEAD );
|
|
StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
|
|
StopSound( SND_CHANNEL_BODY2, false );
|
|
|
|
fl.takedamage = true; // can still be gibbed
|
|
|
|
// get rid of weapons
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon->OwnerDied();
|
|
|
|
// drop the weapons as items
|
|
DropWeapons( true );
|
|
|
|
if ( !g_testDeath.GetBool() ) {
|
|
LookAtKiller( inflictor, attacker );
|
|
}
|
|
|
|
if ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) {
|
|
idPlayer *killer = NULL;
|
|
// no gibbing in MP. Event_Gib will early out in MP
|
|
if ( attacker->IsType( idPlayer::Type ) ) {
|
|
killer = static_cast<idPlayer*>(attacker);
|
|
if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
|
|
gibDeath = true;
|
|
gibsDir = dir;
|
|
gibsLaunched = false;
|
|
}
|
|
}
|
|
gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
|
|
} else {
|
|
physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
|
|
}
|
|
|
|
ClearPowerUps();
|
|
|
|
UpdateVisuals();
|
|
|
|
isChatting = false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
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::GetAnim
|
|
Carl: Virtual method idActor::GetAnim() needs to work differently on a dual wielding player
|
|
=====================
|
|
*/
|
|
int idPlayer::GetAnim( int channel, const char* animname )
|
|
{
|
|
int anim;
|
|
const char* temp;
|
|
idAnimator* animatorPtr;
|
|
|
|
if( channel == ANIMCHANNEL_HEAD )
|
|
{
|
|
if( !head )
|
|
{
|
|
return 0;
|
|
}
|
|
animatorPtr = head->GetAnimator();
|
|
}
|
|
else
|
|
{
|
|
animatorPtr = &animator;
|
|
}
|
|
|
|
if( channel == ANIMCHANNEL_LEFTHAND && hands[HAND_LEFT].animPrefix.Length() )
|
|
{
|
|
temp = va( "%s_%s", hands[HAND_LEFT].animPrefix.c_str(), animname );
|
|
anim = animatorPtr->GetAnim( temp );
|
|
if( anim )
|
|
return anim;
|
|
}
|
|
else if( channel == ANIMCHANNEL_RIGHTHAND && hands[HAND_RIGHT].animPrefix.Length() )
|
|
{
|
|
temp = va( "%s_%s", hands[HAND_RIGHT].animPrefix.c_str(), animname );
|
|
anim = animatorPtr->GetAnim( temp );
|
|
if( anim )
|
|
return anim;
|
|
}
|
|
if( animPrefix.Length() )
|
|
{
|
|
temp = va( "%s_%s", animPrefix.c_str(), animname );
|
|
anim = animatorPtr->GetAnim( temp );
|
|
if( anim )
|
|
return anim;
|
|
}
|
|
|
|
anim = animatorPtr->GetAnim( animname );
|
|
|
|
return anim;
|
|
}
|
|
|
|
/*
|
|
================
|
|
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 );
|
|
damage *= PowerUpModifier( BERSERK );
|
|
if( damage && ( victim != this ) && ( victim->IsType( idActor::Type ) || victim->IsType( idDamagable::Type ) ) )
|
|
{
|
|
SetLastHitTime( gameLocal.time );
|
|
}
|
|
/*
|
|
if (victim == this)
|
|
{
|
|
common->Vibrate(250, 0, damage / 50);
|
|
common->Vibrate(250, 1, damage / 50);
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 );
|
|
damage = GetDamageForLocation( damage, location );
|
|
|
|
idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
|
|
if ( !gameLocal.isMultiplayer ) {
|
|
if ( inflictor != gameLocal.world ) {
|
|
switch ( g_skill.GetInteger() ) {
|
|
case 0:
|
|
damage *= 0.80f;
|
|
if ( damage < 1 ) {
|
|
damage = 1;
|
|
}
|
|
break;
|
|
case 2:
|
|
damage *= 1.70f;
|
|
break;
|
|
case 3:
|
|
damage *= 3.5f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
damage *= damageScale;
|
|
|
|
// always give half damage if hurting self
|
|
if ( attacker == this ) {
|
|
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" );
|
|
}
|
|
}
|
|
|
|
// 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 );
|
|
|
|
// save some from armor
|
|
if ( !damageDef->GetBool( "noArmor" ) ) {
|
|
float armor_protection;
|
|
|
|
armor_protection = ( gameLocal.isMultiplayer ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
|
|
|
|
armorSave = ceil( damage * armor_protection );
|
|
if ( armorSave >= inventory.armor ) {
|
|
armorSave = inventory.armor;
|
|
}
|
|
|
|
if ( !damage ) {
|
|
armorSave = 0;
|
|
} else if ( armorSave >= damage ) {
|
|
armorSave = damage - 1;
|
|
damage = 1;
|
|
} else {
|
|
damage -= armorSave;
|
|
}
|
|
} else {
|
|
armorSave = 0;
|
|
}
|
|
|
|
// check for team damage
|
|
if ( gameLocal.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;
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPlayer::ControllerShakeFromDamage
|
|
============
|
|
*/
|
|
void idPlayer::ControllerShakeFromDamage( int damage )
|
|
{
|
|
// If the player is local. SHAkkkkkkeeee!
|
|
if( IsLocallyControlled() )
|
|
{
|
|
|
|
int maxMagScale = pm_controllerShake_damageMaxMag.GetFloat();
|
|
int maxDurScale = pm_controllerShake_damageMaxDur.GetFloat();
|
|
|
|
// determine rumble
|
|
// >= 100 damage - will be 300 Mag
|
|
float highMag = ( Max( damage, 100 ) / 100.0f ) * maxMagScale;
|
|
int highDuration = idMath::Ftoi( ( Max( damage, 100 ) / 100.0f ) * maxDurScale );
|
|
float lowMag = highMag * 0.75f;
|
|
int lowDuration = idMath::Ftoi( highDuration );
|
|
|
|
// Multiplayer damage from an unknown source to an unknown location. So shake both hands.
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[h].SetControllerShake( highMag, highDuration, lowMag, lowDuration );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
============
|
|
idPlayer::ControllerShakeFromDamage
|
|
============
|
|
*/
|
|
void idPlayer::ControllerShakeFromDamage( int damage, const idVec3 &dir )
|
|
{
|
|
// If the player is local. SHAkkkkkkeeee!
|
|
if( IsLocallyControlled() )
|
|
{
|
|
int maxMagScale = pm_controllerShake_damageMaxMag.GetFloat();
|
|
int maxDurScale = pm_controllerShake_damageMaxDur.GetFloat();
|
|
|
|
// determine rumble
|
|
// >= 100 damage - will be 300 Mag
|
|
float highMag = ( Max( damage, 100 ) / 100.0f ) * maxMagScale;
|
|
int highDuration = idMath::Ftoi( ( Max( damage, 100 ) / 100.0f ) * maxDurScale );
|
|
|
|
if( commonVr->hasHMD )
|
|
{
|
|
SetControllerShake( highMag, highDuration, dir );
|
|
}
|
|
else
|
|
{
|
|
float lowMag = highMag * 0.75f;
|
|
int lowDuration = idMath::Ftoi( highDuration );
|
|
hands[HAND_LEFT].SetControllerShake( highMag, highDuration, lowMag, lowDuration );
|
|
hands[HAND_RIGHT].SetControllerShake( highMag, highDuration, lowMag, lowDuration );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
============
|
|
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;
|
|
}
|
|
|
|
if ( attacker->IsType( idAI::Type ) ) {
|
|
if ( PowerUpActive( BERSERK ) ) {
|
|
return;
|
|
}
|
|
// don't take damage from monsters during influences
|
|
if ( influenceActive != 0 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
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
|
|
if ( vr_knockback.GetBool() )
|
|
{
|
|
// determine knockback
|
|
damageDef->dict.GetInt("knockback", "20", knockback);
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
// 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 ) {
|
|
|
|
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( IsLocallyControlled() )
|
|
{
|
|
ControllerShakeFromDamage( damage, dir );
|
|
}
|
|
}
|
|
|
|
if ( damage < 1 ) {
|
|
damage = 1;
|
|
}
|
|
|
|
health -= damage;
|
|
|
|
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 ) {
|
|
idVec3 org;
|
|
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon )
|
|
hands[ h ].weapon->LowerWeapon();
|
|
}
|
|
|
|
SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
|
|
if ( !gameLocal.isMultiplayer && GetFloorPos( 16.0f, org ) ) {
|
|
SetOrigin( org );
|
|
}
|
|
|
|
// clear the ik heights so model doesn't appear in the wrong place
|
|
walkIK.EnableAll();
|
|
|
|
GetPhysics()->SetLinearVelocity( vec3_origin );
|
|
|
|
SetViewAngles( angles );
|
|
|
|
legsYaw = 0.0f;
|
|
idealLegsYaw = 0.0f;
|
|
oldViewYaw = viewAngles.yaw;
|
|
|
|
if ( gameLocal.isMultiplayer ) {
|
|
playerView.Flash( colorWhite, 140 );
|
|
}
|
|
|
|
UpdateVisuals();
|
|
|
|
teleportEntity = destination;
|
|
|
|
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 ) {
|
|
privateCameraView = camView;
|
|
if ( camView ) {
|
|
StopFiring();
|
|
Hide();
|
|
} else {
|
|
if ( !spectating ) {
|
|
Show();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::SetQuickSlot
|
|
===============
|
|
*/
|
|
void idPlayer::SetQuickSlot( int index, int val )
|
|
{
|
|
if( index >= NUM_QUICK_SLOTS || index < 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
quickSlot[ index ] = val;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
idPlayer::DefaultFov
|
|
|
|
Returns the base FOV
|
|
====================
|
|
*/
|
|
float idPlayer::DefaultFov( void ) const {
|
|
float fov;
|
|
|
|
fov = renderSystem->GetFOV();
|
|
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;
|
|
}
|
|
|
|
// Koz, no zoom in VR.
|
|
/*if ( zoomFov.IsDone( gameLocal.time ) ) {
|
|
fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon ? weapon->GetZoomFov() : DefaultFov();
|
|
} else {
|
|
fov = zoomFov.GetCurrentValue( gameLocal.time );
|
|
}*/
|
|
|
|
fov = DefaultFov();
|
|
|
|
// bound normal viewsize
|
|
if ( fov < 1 ) {
|
|
fov = 1;
|
|
} else if ( fov > 179 ) {
|
|
fov = 179;
|
|
}
|
|
|
|
return fov;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GunTurningOffset
|
|
|
|
generate a rotational offset for the gun based on the view angle
|
|
history in loggedViewAngles
|
|
==============
|
|
*/
|
|
idAngles idPlayer::GunTurningOffset( void ) {
|
|
idAngles a;
|
|
|
|
a.Zero();
|
|
|
|
if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
|
|
return a;
|
|
}
|
|
|
|
idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
|
|
|
|
idAngles av, base;
|
|
int weaponAngleOffsetAverages;
|
|
float weaponAngleOffsetScale, weaponAngleOffsetMax;
|
|
|
|
//weapon->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
|
|
// Carl: todo dual wielding
|
|
hands[vr_weaponHand.GetInteger()].weapon->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
|
|
|
|
av = current;
|
|
|
|
// calcualte this so the wrap arounds work properly
|
|
for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
|
|
idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
|
|
|
|
idAngles delta = a2 - current;
|
|
|
|
if ( delta[1] > 180 ) {
|
|
delta[1] -= 360;
|
|
} else if ( delta[1] < -180 ) {
|
|
delta[1] += 360;
|
|
}
|
|
|
|
av += delta * ( 1.0f / weaponAngleOffsetAverages );
|
|
}
|
|
|
|
a = ( av - current ) * weaponAngleOffsetScale;
|
|
|
|
for ( int i = 0 ; i < 3 ; i++ ) {
|
|
if ( a[i] < -weaponAngleOffsetMax ) {
|
|
a[i] = -weaponAngleOffsetMax;
|
|
} else if ( a[i] > weaponAngleOffsetMax ) {
|
|
a[i] = weaponAngleOffsetMax;
|
|
}
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::GunAcceleratingOffset
|
|
|
|
generate a positional offset for the gun based on the movement
|
|
history in loggedAccelerations
|
|
==============
|
|
*/
|
|
idVec3 idPlayer::GunAcceleratingOffset( void ) {
|
|
idVec3 ofs;
|
|
|
|
float weaponOffsetTime, weaponOffsetScale;
|
|
|
|
ofs.Zero();
|
|
|
|
//weapon->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
|
|
// Carl: todo dual wielding
|
|
hands[vr_weaponHand.GetInteger()].weapon->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
|
|
|
|
int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
|
|
if ( stop < 0 ) {
|
|
stop = 0;
|
|
}
|
|
for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
|
|
loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
|
|
|
|
float f;
|
|
float t = gameLocal.time - acc->time;
|
|
if ( t >= weaponOffsetTime ) {
|
|
break; // remainder are too old to care about
|
|
}
|
|
|
|
f = t / weaponOffsetTime;
|
|
f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
|
|
ofs += f * weaponOffsetScale * acc->dir;
|
|
}
|
|
|
|
return ofs;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdateLaserSight
|
|
==============
|
|
*/
|
|
idCVar g_laserSightWidth( "g_laserSightWidth", "0.5", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam width" ); // Koz default was 2, IMO too big in VR.
|
|
idCVar g_laserSightLength( "g_laserSightLength", "1000", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam length" ); // Koz default was 250, but was to short in VR. Length will be clipped if object is hit, this is max length for the hit trace.
|
|
void idPlayer::UpdateLaserSight( int hand )
|
|
{
|
|
idVec3 muzzleOrigin;
|
|
idMat3 muzzleAxis;
|
|
|
|
idVec3 end, start;
|
|
trace_t traceResults;
|
|
|
|
float beamLength = g_laserSightLength.GetFloat(); // max length to run trace.
|
|
|
|
int sightMode = vr_weaponSight.GetInteger();
|
|
|
|
bool hideSight = false;
|
|
|
|
bool traceHit = false;
|
|
idWeapon* weapon = hands[ hand ].weapon;
|
|
|
|
// In Multiplayer, weapon might not have been spawned yet.
|
|
if( weapon == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Carl: teleport
|
|
static bool oldTeleport = false;
|
|
bool showTeleport = vr_teleport.GetInteger() == 1 && commonVr->VR_USE_MOTION_CONTROLS; // only show the teleport gun cursor if we're teleporting using the gun aim mode
|
|
showTeleport = showTeleport && !AI_DEAD && !gameLocal.inCinematic && !game->IsPDAOpen();
|
|
|
|
// check if lasersight should be hidden
|
|
if ( !hands[hand].laserSightActive || // Koz allow user to toggle lasersight.
|
|
sightMode == -1 ||
|
|
!weapon->ShowCrosshair() ||
|
|
AI_DEAD ||
|
|
weapon->IsHidden() ||
|
|
weapon->hideOffset != 0 || // Koz - turn off lasersight If gun is lowered ( in gui ).
|
|
commonVr->handInGui || // turn off lasersight if hand is in gui.
|
|
gameLocal.inCinematic ||
|
|
game->IsPDAOpen() || // Koz - turn off laser sight if using pda.
|
|
showTeleport || !weapon->GetMuzzlePositionWithHacks(muzzleOrigin, muzzleAxis)) // no lasersight for fists,grenades,soulcube etc
|
|
|
|
{
|
|
hideSight = !showTeleport;
|
|
}
|
|
|
|
if ( hideSight == true || ( sightMode != 0 && sightMode < 4 ) )
|
|
{
|
|
hands[hand].laserSightRenderEntity.allowSurfaceInViewID = -1;
|
|
if( hands[hand].laserSightHandle == -1 )
|
|
{
|
|
hands[hand].laserSightHandle = gameRenderWorld->AddEntityDef( &hands[hand].laserSightRenderEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( hands[hand].laserSightHandle, &hands[hand].laserSightRenderEntity );
|
|
}
|
|
}
|
|
|
|
if ( !showTeleport && ( hideSight == true || sightMode == 0 ) )
|
|
{
|
|
hands[hand].crosshairEntity.allowSurfaceInViewID = -1;
|
|
if ( hands[hand].crosshairHandle == -1 )
|
|
{
|
|
hands[hand].crosshairHandle = gameRenderWorld->AddEntityDef( &hands[hand].crosshairEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( hands[hand].crosshairHandle, &hands[hand].crosshairEntity );
|
|
}
|
|
}
|
|
|
|
|
|
if ( hideSight && !showTeleport ) return;
|
|
|
|
if ( showTeleport )
|
|
{
|
|
GetHandOrHeadPositionWithHacks(vr_teleport.GetInteger(), muzzleOrigin, muzzleAxis);
|
|
}
|
|
|
|
// calculate the beam origin and length.
|
|
start = muzzleOrigin - muzzleAxis[0] * 2.0f;
|
|
|
|
if ( vr_laserSightUseOffset.GetBool() ) start += weapon->laserSightOffset * muzzleAxis;
|
|
|
|
end = start + muzzleAxis[0] * beamLength;
|
|
|
|
// Koz begin : Keep the lasersight from clipping through everything.
|
|
|
|
traceHit = gameLocal.clip.TracePoint( traceResults, start, end, MASK_SHOT_RENDERMODEL, this );
|
|
if ( traceHit )
|
|
{
|
|
beamLength *= traceResults.fraction;
|
|
}
|
|
|
|
|
|
if ( (vr_weaponSight.GetInteger() == 0 || vr_weaponSight.GetInteger() > 3 ) && !showTeleport && !hideSight ) // using the lasersight
|
|
|
|
{
|
|
// only show in the player's view
|
|
// Koz - changed show lasersight shows up in all views/reflections in VR
|
|
hands[hand].laserSightRenderEntity.allowSurfaceInViewID = 0;// entityNumber + 1;
|
|
hands[hand].laserSightRenderEntity.axis.Identity();
|
|
hands[hand].laserSightRenderEntity.origin = start;
|
|
|
|
|
|
// program the beam model
|
|
idVec3& target = *reinterpret_cast<idVec3*>( &hands[hand].laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
|
|
target = start + muzzleAxis[0] * beamLength;
|
|
|
|
hands[hand].laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_WIDTH] = g_laserSightWidth.GetFloat();
|
|
|
|
if ( hands[hand].laserSightHandle == -1 )
|
|
{
|
|
hands[hand].laserSightHandle = gameRenderWorld->AddEntityDef( &hands[hand].laserSightRenderEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( hands[hand].laserSightHandle, &hands[hand].laserSightRenderEntity );
|
|
}
|
|
}
|
|
|
|
if ( vr_teleport.GetInteger() == 1 && commonVr->VR_USE_MOTION_CONTROLS && vr_weaponSight.GetInteger() == 0 )
|
|
vr_weaponSight.SetInteger( 1 );
|
|
|
|
sightMode = vr_weaponSight.GetInteger();
|
|
vr_weaponSight.ClearModified();
|
|
|
|
if ( !showTeleport && ( sightMode < 1 || hideSight )) return;
|
|
|
|
// update the crosshair model
|
|
// set the crosshair skin
|
|
|
|
switch ( sightMode )
|
|
{
|
|
case 4:
|
|
case 1:
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairDot;
|
|
break;
|
|
|
|
case 5:
|
|
case 2:
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairCircleDot;
|
|
break;
|
|
|
|
case 6:
|
|
case 3:
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairCross;
|
|
break;
|
|
|
|
default:
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairDot;
|
|
|
|
}
|
|
|
|
if ( showTeleport || sightMode > 0 ) hands[hand].crosshairEntity.allowSurfaceInViewID = entityNumber + 1;
|
|
hands[hand].crosshairEntity.axis.Identity();
|
|
|
|
static float muzscale = 0.0f ;
|
|
|
|
muzscale = 1 + beamLength / 100;
|
|
hands[hand].crosshairEntity.axis = muzzleAxis * muzscale;
|
|
|
|
bool aimLadder = false, aimActor = false, aimElevator = false;
|
|
|
|
static idAngles surfaceAngle = ang_zero;
|
|
|
|
if ( traceHit )
|
|
{
|
|
muzscale = 1 + beamLength / 100;
|
|
|
|
if ( showTeleport || vr_weaponSightToSurface.GetBool() )
|
|
{
|
|
aimLadder = traceResults.c.material && ( traceResults.c.material->GetSurfaceFlags() & SURF_LADDER );
|
|
idEntity* aimEntity = gameLocal.GetTraceEntity(traceResults);
|
|
if (aimEntity)
|
|
{
|
|
if (aimEntity->IsType(idActor::Type))
|
|
aimActor = aimEntity->health > 0;
|
|
else if (aimEntity->IsType(idElevator::Type))
|
|
aimElevator = true;
|
|
else if (aimEntity->IsType(idStaticEntity::Type) || aimEntity->IsType(idLight::Type))
|
|
{
|
|
renderEntity_t *rend = aimEntity->GetRenderEntity();
|
|
if (rend)
|
|
{
|
|
idRenderModel *model = rend->hModel;
|
|
aimElevator = (model && idStr::Cmp(model->Name(), "models/mapobjects/elevators/elevator.lwo") == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// fake it till you make it. there must be a better way. Too bad my brain is broken.
|
|
|
|
static idAngles muzzleAngle = ang_zero;
|
|
static idAngles diffAngle = ang_zero;
|
|
static float rollDiff = 0.0f;
|
|
|
|
surfaceAngle = traceResults.c.normal.ToAngles().Normalize180();
|
|
muzzleAngle = muzzleAxis.ToAngles().Normalize180();
|
|
|
|
surfaceAngle.pitch *= -1;
|
|
surfaceAngle.yaw += 180;
|
|
surfaceAngle.Normalize180();
|
|
|
|
diffAngle = idAngles( 0, 0, muzzleAngle.yaw - surfaceAngle.yaw ).Normalize180();
|
|
|
|
rollDiff = diffAngle.roll * 1 / ( 90 / surfaceAngle.pitch );
|
|
|
|
surfaceAngle.roll = muzzleAngle.roll - rollDiff;
|
|
surfaceAngle.Normalize180();
|
|
|
|
hands[hand].crosshairEntity.axis = surfaceAngle.ToMat3() * muzscale;
|
|
}
|
|
else
|
|
{
|
|
hands[hand].crosshairEntity.axis = muzzleAxis * muzscale;
|
|
}
|
|
}
|
|
|
|
hands[hand].crosshairEntity.origin = start + muzzleAxis[0] * beamLength;
|
|
|
|
|
|
// Carl: teleport
|
|
if ( showTeleport )
|
|
{
|
|
|
|
// teleportAimPoint is where you are actually aiming. teleportPoint is where AAS has nudged the teleport cursor to (so you can't teleport too close to a wall).
|
|
// teleportAimPointPitch is the pitch of the surface you are aiming at, where 90 is the floor and 0 is the wall
|
|
teleportAimPoint = hands[hand].crosshairEntity.origin;
|
|
teleportAimPointPitch = surfaceAngle.pitch; // if the elevator is moving up, we don't want to fall through the floor
|
|
if ( aimElevator )
|
|
teleportPoint = teleportAimPoint + idVec3(0, 0, 10);
|
|
// 45 degrees is maximum slope you can walk up
|
|
bool pitchValid = ( teleportAimPointPitch >= 45 && !aimActor ) || aimLadder; // -90 = ceiling, 0 = wall, 90 = floor
|
|
// can always teleport into nearby elevator, otherwise we need to check
|
|
aimValidForTeleport = pitchValid && (( aimElevator && beamLength <= 300 ) || CanReachPosition( teleportAimPoint, teleportPoint ));
|
|
|
|
if ( aimValidForTeleport )
|
|
{
|
|
hands[hand].crosshairEntity.origin = teleportPoint;
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairCircleDot;
|
|
}
|
|
else if ( pitchValid )
|
|
{
|
|
hands[hand].crosshairEntity.origin = teleportPoint;
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairCross;
|
|
}
|
|
else if ( vr_teleport.GetInteger() == 1 && commonVr->VR_USE_MOTION_CONTROLS )
|
|
{
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairDot;
|
|
}
|
|
else
|
|
{
|
|
hands[hand].crosshairEntity.customSkin = skinCrosshairCross;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aimValidForTeleport = false;
|
|
}
|
|
oldTeleport = showTeleport;
|
|
|
|
if ( hands[hand].crosshairHandle == -1 )
|
|
{
|
|
hands[hand].crosshairHandle = gameRenderWorld->AddEntityDef( &hands[hand].crosshairEntity );
|
|
}
|
|
else
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( hands[hand].crosshairHandle, &hands[hand].crosshairEntity );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::GetCurrentWeapon
|
|
=================
|
|
*/
|
|
idStr idPlayer::GetCurrentWeapon()
|
|
{
|
|
return hands[ vr_weaponHand.GetInteger() ].GetCurrentWeaponString();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idPlayer::GetCurrentWeapon
|
|
=================
|
|
*/
|
|
int idPlayer::GetCurrentWeaponId()
|
|
{
|
|
return hands[ vr_weaponHand.GetInteger() ].currentWeapon;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idPlayer::GetExpansionType
|
|
========================
|
|
*/
|
|
gameExpansionType_t idPlayer::GetExpansionType() const
|
|
{
|
|
const char* expansion = spawnArgs.GetString( "player_expansion", "d3" );
|
|
if( idStr::Icmp( expansion, "d3" ) == 0 )
|
|
{
|
|
return GAME_BASE;
|
|
}
|
|
if( idStr::Icmp( expansion, "d3xp" ) == 0 )
|
|
{
|
|
return GAME_D3XP;
|
|
}
|
|
if( idStr::Icmp( expansion, "d3le" ) == 0 )
|
|
{
|
|
return GAME_D3LE;
|
|
}
|
|
return GAME_UNKNOWN;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::CalculateViewWeaponPos
|
|
|
|
Calculate the bobbing position of the view weapon
|
|
==============
|
|
*/
|
|
|
|
void idPlayer::CalculateViewWeaponPos( int hand, idVec3& origin, idMat3& axis )
|
|
{
|
|
if ( game->isVR )
|
|
{
|
|
CalculateViewWeaponPosVR( hand, origin, axis );
|
|
return;
|
|
}
|
|
/*
|
|
|
|
float scale;
|
|
float fracsin;
|
|
int delta;
|
|
|
|
// CalculateRenderView must have been called first
|
|
const idVec3 &viewOrigin = firstPersonViewOrigin;
|
|
const idMat3 &viewAxis = firstPersonViewAxis;
|
|
|
|
if (pVRClientInfo &&
|
|
GetCurrentWeaponId() != weapon_pda)
|
|
{
|
|
float *pAngles = pointer ? pVRClientInfo->weaponangles_unadjusted : pVRClientInfo->weaponangles;
|
|
angles.pitch = pAngles[PITCH];
|
|
angles.yaw = viewAngles.yaw +
|
|
(pAngles[YAW] - pVRClientInfo->hmdorientation[YAW]);
|
|
angles.roll = pAngles[ROLL];
|
|
|
|
axis = angles.ToMat3();
|
|
|
|
idVec3 gunpos( -pVRClientInfo->current_weaponoffset[2],
|
|
-pVRClientInfo->current_weaponoffset[0],
|
|
pVRClientInfo->current_weaponoffset[1]);
|
|
|
|
idAngles a(0, viewAngles.yaw - pVRClientInfo->hmdorientation[YAW], 0);
|
|
gunpos *= a.ToMat3();
|
|
gunpos *= cvarSystem->GetCVarFloat( "vr_worldscale" );
|
|
|
|
if (GetCurrentWeaponId( == WEAPON_FLASHLIGHT) // Flashlight adjustment
|
|
{
|
|
idVec3 gunOfs( -14, 9, 24 );
|
|
origin = viewOrigin + gunpos + (gunOfs * axis);
|
|
}
|
|
else
|
|
{
|
|
idVec3 gunOfs( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
|
|
origin = viewOrigin + gunpos + (gunOfs * axis);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// 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;
|
|
|
|
///HACK
|
|
if (GetCurrentWeaponId( == weapon_pda)
|
|
{
|
|
//idVec3 pdaOffs( 30, -7, -14 );
|
|
|
|
//Put it behind us
|
|
idVec3 pdaOffs( -30, -7, -14 );
|
|
origin += pdaOffs * 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;*/
|
|
}
|
|
|
|
// Carl: TODO Dual wielding
|
|
void idPlayer::CalculateViewWeaponPosVR( int hand, idVec3 &origin, idMat3 &axis )
|
|
{
|
|
weapon_t currentWeaponEnum = WEAPON_NONE;
|
|
idVec3 gunOrigin;
|
|
idVec3 originOffset = vec3_zero;
|
|
idAngles hmdAngles;
|
|
idVec3 headPositionDelta;
|
|
idVec3 bodyPositionDelta;
|
|
idVec3 absolutePosition;
|
|
idQuat weaponPitch;
|
|
|
|
currentWeaponEnum = hands[ hand ].weapon->IdentifyWeapon();
|
|
idWeapon* weapon = hands[ hand ].weapon;
|
|
int currentWeaponIndex = hands[ hand ].currentWeapon;
|
|
|
|
if ( hands[hand].holdingFlashlight() || weapon->isPlayerFlashlight ) return;
|
|
|
|
gunOrigin = GetEyePosition();
|
|
|
|
if ( game->isVR && commonVr->VR_USE_MOTION_CONTROLS ) gunOrigin += commonVr->leanOffset;
|
|
|
|
// direction the player body is facing.
|
|
idMat3 bodyAxis = idAngles( 0.0, viewAngles.yaw, 0.0f ).ToMat3();
|
|
idVec3 gravity = physicsObj.GetGravityNormal();
|
|
|
|
|
|
if ( currentWeaponEnum != WEAPON_PDA )
|
|
{
|
|
hands[hand].PDAfixed = false; // release the PDA if weapon has been switched.
|
|
}
|
|
|
|
if ( !commonVr->VR_USE_MOTION_CONTROLS || ( vr_PDAfixLocation.GetBool() && currentWeaponEnum == WEAPON_PDA ) ) // non-motion control & fixed pda positioning.
|
|
{
|
|
|
|
idMat3 pdaPitch = idAngles( vr_pdaPitch.GetFloat(), 0.0f, 0.0f ).ToMat3();
|
|
|
|
axis = bodyAxis;
|
|
origin = gunOrigin;
|
|
|
|
if ( currentWeaponEnum == WEAPON_PDA ) //&& weapon->GetStatus() == WP_READY )
|
|
{
|
|
|
|
if ( hands[ hand ].PDAfixed )
|
|
{ // pda has already been locked in space, use stored values
|
|
|
|
origin = PDAorigin;
|
|
axis = PDAaxis;
|
|
|
|
|
|
//if the player has moved ( or been moved, if on an elevator or lift )
|
|
//move the PDA to maintain a constant relative position
|
|
idVec3 curPlayerPos = physicsObj.GetOrigin();
|
|
origin -= ( hands[hand].playerPdaPos - curPlayerPos ) + commonVr->fixedPDAMoveDelta;
|
|
//common->Printf( "playerPDA x %f y %f currentPlay x %f y %f fixMoveDel x %f y %f\n", playerPdaPos.x, playerPdaPos.y, curPlayerPos.x, curPlayerPos.y, commonVr->fixedPDAMoveDelta.x, commonVr->fixedPDAMoveDelta.y );
|
|
|
|
SetHandIKPos( hand, origin, axis, pdaPitch.ToQuat() , false );
|
|
originOffset = weapon->weaponHandDefaultPos[hand];
|
|
origin -= originOffset * axis;
|
|
origin += handWeaponAttacherToDefaultOffset[hand][currentWeaponIndex] * axis; // add the attacher offsets
|
|
}
|
|
else
|
|
{ // fix the PDA in space, set flag and store position
|
|
|
|
hands[ hand ].playerPdaPos = physicsObj.GetOrigin();
|
|
|
|
origin = gunOrigin;
|
|
origin += vr_pdaPosX.GetFloat() * bodyAxis[0] + vr_pdaPosY.GetFloat() * bodyAxis[1] + vr_pdaPosZ.GetFloat() * bodyAxis[2];
|
|
PDAorigin = origin;
|
|
PDAaxis = pdaPitch * bodyAxis;
|
|
axis = PDAaxis;
|
|
hands[ hand ].PDAfixed = true;
|
|
|
|
SetHandIKPos( hand, origin, axis, pdaPitch.ToQuat(), false );
|
|
originOffset = weapon->weaponHandDefaultPos[hand];
|
|
origin -= originOffset * axis;
|
|
origin += handWeaponAttacherToDefaultOffset[hand][currentWeaponIndex] * axis; // add the attacher offsets
|
|
|
|
|
|
// the non weapon hand was set to the PDA fixed location, now fall thru and normal motion controls will place the pointer hand location
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (commonVr->VR_USE_MOTION_CONTROLS )
|
|
{
|
|
// motion control weapon positioning.
|
|
//-----------------------------------
|
|
|
|
idVec3 weapOrigin = vec3_zero;
|
|
idMat3 weapAxis = mat3_identity;
|
|
|
|
// idVec3 fixPosVec = idVec3( -17.0f, 6.0f, 0.0f );
|
|
// idVec3 fixPos = fixPosVec;
|
|
// idQuat fixRot = idAngles( 40.0f, -40.0f, 20.0f ).ToQuat();
|
|
idVec3 attacherToDefault = vec3_zero;
|
|
// idMat3 rot180 = idAngles( 0.0f, 180.0f, 0.0f ).ToMat3();
|
|
|
|
if ( !hands[ hand ].PDAfixed && currentWeaponEnum == WEAPON_PDA )
|
|
{
|
|
// do the non-PDA hand first (the hand with the pointy finger)
|
|
int fingerHand = 1 - hand;
|
|
|
|
attacherToDefault = handWeaponAttacherToDefaultOffset[fingerHand][currentWeaponIndex];
|
|
originOffset = weapon->weaponHandDefaultPos[fingerHand];
|
|
commonVr->MotionControlGetHand( fingerHand, hands[fingerHand].motionPosition, hands[fingerHand].motionRotation );
|
|
|
|
weaponPitch = idAngles( vr_motionWeaponPitchAdj.GetFloat(), 0.f, 0.0f ).ToQuat();
|
|
hands[fingerHand].motionRotation = weaponPitch * hands[fingerHand].motionRotation;
|
|
|
|
GetViewPos( weapOrigin, weapAxis );
|
|
|
|
weapOrigin += commonVr->leanOffset;
|
|
//commonVr->HMDGetOrientation( hmdAngles, headPositionDelta, bodyPositionDelta, absolutePosition, false );// gameLocal.inCinematic );
|
|
|
|
hmdAngles = commonVr->poseHmdAngles;
|
|
headPositionDelta = commonVr->poseHmdHeadPositionDelta;
|
|
bodyPositionDelta = commonVr->poseHmdBodyPositionDelta;
|
|
absolutePosition = commonVr->poseHmdAbsolutePosition;
|
|
|
|
|
|
|
|
weapAxis = idAngles( 0.0, weapAxis.ToAngles().yaw - commonVr->bodyYawOffset, 0.0f ).ToMat3();
|
|
|
|
weapOrigin += weapAxis[0] * headPositionDelta.x + weapAxis[1] * headPositionDelta.y + weapAxis[2] * headPositionDelta.z;
|
|
|
|
weapOrigin += hands[fingerHand].motionPosition * weapAxis;
|
|
weapAxis = hands[fingerHand].motionRotation.ToMat3() * weapAxis;
|
|
|
|
//weapon->CalculateHideRise( weapOrigin, weapAxis );
|
|
|
|
idAngles motRot = hands[fingerHand].motionRotation.ToAngles();
|
|
motRot.yaw -= commonVr->bodyYawOffset;
|
|
motRot.Normalize180();
|
|
hands[fingerHand].motionRotation = motRot.ToQuat();
|
|
|
|
SetHandIKPos( fingerHand, weapOrigin, weapAxis, hands[fingerHand].motionRotation, false );
|
|
|
|
// now switch hands and fall through again.,
|
|
}
|
|
// NOT the PDA
|
|
|
|
attacherToDefault = handWeaponAttacherToDefaultOffset[hand][currentWeaponIndex];
|
|
originOffset = weapon->weaponHandDefaultPos[hand];
|
|
|
|
commonVr->MotionControlGetHand( hand, hands[ hand ].motionPosition, hands[ hand ].motionRotation );
|
|
|
|
weaponPitch = idAngles( vr_motionWeaponPitchAdj.GetFloat(), 0.0f, 0.0f ).ToQuat();
|
|
hands[ hand ].motionRotation = weaponPitch * hands[ hand ].motionRotation;
|
|
|
|
GetViewPos( weapOrigin, weapAxis );
|
|
|
|
weapOrigin += commonVr->leanOffset;
|
|
|
|
//commonVr->HMDGetOrientation( hmdAngles, headPositionDelta, bodyPositionDelta, absolutePosition, false );// gameLocal.inCinematic );
|
|
|
|
hmdAngles = commonVr->poseHmdAngles;
|
|
headPositionDelta = commonVr->poseHmdHeadPositionDelta;
|
|
bodyPositionDelta = commonVr->poseHmdBodyPositionDelta;
|
|
absolutePosition = commonVr->poseHmdAbsolutePosition;
|
|
|
|
|
|
weapAxis = idAngles( 0.0f, weapAxis.ToAngles().yaw - commonVr->bodyYawOffset, 0.0f ).ToMat3();
|
|
|
|
weapOrigin += weapAxis[0] * headPositionDelta.x + weapAxis[1] * headPositionDelta.y + weapAxis[2] * headPositionDelta.z;
|
|
|
|
weapOrigin += hands[ hand ].motionPosition * weapAxis;
|
|
|
|
if ( currentWeaponEnum != WEAPON_ARTIFACT && currentWeaponEnum != WEAPON_SOULCUBE )
|
|
{
|
|
weapAxis = hands[ hand ].motionRotation.ToMat3() * weapAxis;
|
|
}
|
|
else
|
|
{
|
|
weapAxis = idAngles( 0.0f ,viewAngles.yaw , 0.0f).ToMat3();
|
|
}
|
|
|
|
//DebugCross( weapOrigin, weapAxis, colorYellow );
|
|
|
|
if ( currentWeaponEnum != WEAPON_PDA )
|
|
{
|
|
hands[hand].TrackWeaponDirection( weapOrigin );
|
|
hands[1 - hand].TrackWeaponDirection( weapOrigin );
|
|
weapon->CalculateHideRise( weapOrigin, weapAxis );
|
|
//check for melee hit?
|
|
}
|
|
else if( !hands[ hand ].PDAfixed )
|
|
{
|
|
// Koz FIXME hack hack hack this is getting so ungodly ugly.
|
|
// Lovely. I forgot to correct the origin for the PDA model when I switched
|
|
// to always showing the body, so now when holding the PDA it doesn't align with your controller.
|
|
// will fix the assets later but for now hack this correction in.
|
|
|
|
/* GB Debugger Change Values
|
|
* float _vr_pdaPosX = 0.0;
|
|
float _vr_pdaPosY = 0.0;
|
|
float _vr_pdaPosZ = 0.0;
|
|
if(_vr_pdaPosX != 0.0f && _vr_pdaPosX != vr_pdaPosX.GetFloat())
|
|
cvarSystem->SetCVarFloat("vr_pdaPosX", _vr_pdaPosX);
|
|
if(_vr_pdaPosY != 0.0f && _vr_pdaPosY != vr_pdaPosY.GetFloat())
|
|
cvarSystem->SetCVarFloat("vr_pdaPosY", _vr_pdaPosY);
|
|
if(_vr_pdaPosZ != 0.0f && _vr_pdaPosZ != vr_pdaPosZ.GetFloat())
|
|
cvarSystem->SetCVarFloat("vr_pdaPosZ", _vr_pdaPosZ);
|
|
*/
|
|
const idVec3 pdaHackOrigin[2] { idVec3( vr_pdaPosX.GetFloat(), vr_pdaPosY.GetFloat(), vr_pdaPosZ.GetFloat() ), idVec3( vr_pdaPosX.GetFloat(), -vr_pdaPosY.GetFloat(), vr_pdaPosZ.GetFloat() ) };
|
|
weapOrigin += pdaHackOrigin[hand] * weapAxis;
|
|
}
|
|
|
|
idAngles motRot = hands[ hand ].motionRotation.ToAngles();
|
|
motRot.yaw -= commonVr->bodyYawOffset;
|
|
motRot.Normalize180();
|
|
hands[ hand ].motionRotation = motRot.ToQuat();
|
|
|
|
SetHandIKPos( hand, weapOrigin, weapAxis, hands[ hand ].motionRotation, false );
|
|
|
|
if ( hands[ hand ].PDAfixed ) return;
|
|
|
|
if ( currentWeaponEnum == WEAPON_PDA )
|
|
{
|
|
|
|
PDAaxis = weapAxis;
|
|
PDAorigin = weapOrigin;
|
|
if ( hands[ hand ].wasPDA == false )
|
|
{
|
|
SetFlashHandPose(); // Call set flashlight hand pose script function
|
|
SetWeaponHandPose();
|
|
hands[ hand ].wasPDA = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hands[hand].wasPDA = false;
|
|
}
|
|
|
|
axis = weapAxis;
|
|
origin = weapOrigin;
|
|
|
|
origin -= originOffset * weapAxis;
|
|
origin += attacherToDefault * weapAxis; // handWeaponAttacherToDefaultOffset[hand][currentWeaponIndex] * weapAxis; // add the attacher offsets
|
|
}
|
|
}
|
|
|
|
bool idPlayer::CanDualWield( int num ) const
|
|
{
|
|
vr_dualwield_t d = (vr_dualwield_t)vr_dualWield.GetInteger();
|
|
if( d == VR_DUALWIELD_YES || num == -1 )
|
|
return true;
|
|
if( d == VR_DUALWIELD_NOT_EVEN_FISTS )
|
|
return false;
|
|
if( num == weapon_fists )
|
|
return d != VR_DUALWIELD_NOT_EVEN_FISTS;
|
|
if( num == weapon_flashlight || num == weapon_flashlight_new )
|
|
return d == VR_DUALWIELD_ONLY_FLASHLIGHT || d == VR_DUALWIELD_ONLY_GRENADES_FLASHLIGHT || d == VR_DUALWIELD_ONLY_PISTOLS_FLASHLIGHT || d == VR_DUALWIELD_ONLY_PISTOLS_GRENADES_FLASHLIGHT;
|
|
if( num == weapon_pistol )
|
|
return d == VR_DUALWIELD_ONLY_PISTOLS || d == VR_DUALWIELD_ONLY_PISTOLS_FLASHLIGHT || d == VR_DUALWIELD_ONLY_PISTOLS_GRENADES_FLASHLIGHT;
|
|
if( num == weapon_handgrenade )
|
|
return d == VR_DUALWIELD_ONLY_GRENADES || d == VR_DUALWIELD_ONLY_GRENADES_FLASHLIGHT || d == VR_DUALWIELD_ONLY_PISTOLS_GRENADES_FLASHLIGHT;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::OffsetThirdPersonView
|
|
===============
|
|
*/
|
|
void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
|
|
idVec3 view;
|
|
idVec3 focusAngles;
|
|
trace_t trace;
|
|
idVec3 focusPoint;
|
|
float focusDist;
|
|
float forwardScale, sideScale;
|
|
idVec3 origin;
|
|
idAngles angles;
|
|
idMat3 axis;
|
|
idBounds bounds;
|
|
|
|
angles = viewAngles;
|
|
GetViewPos( origin, axis );
|
|
|
|
if ( angle ) {
|
|
angles.pitch = 0.0f;
|
|
}
|
|
|
|
if ( angles.pitch > 45.0f ) {
|
|
angles.pitch = 45.0f; // don't go too far overhead
|
|
}
|
|
|
|
focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
|
|
focusPoint.z += height;
|
|
view = origin;
|
|
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;
|
|
}
|
|
|
|
// Carl:
|
|
#define TP_HAND_DISABLED 0
|
|
#define TP_HAND_GUNSIGHT 1
|
|
#define TP_HAND_RIGHT 2
|
|
#define TP_HAND_LEFT 3
|
|
#define TP_HAND_HEAD 4
|
|
bool idPlayer::GetHandOrHeadPositionWithHacks( int hand, idVec3& origin, idMat3& axis )
|
|
{
|
|
int weaponHand;
|
|
if( hand == TP_HAND_RIGHT )
|
|
weaponHand = HAND_RIGHT;
|
|
else if( hand == TP_HAND_LEFT )
|
|
weaponHand = HAND_LEFT;
|
|
else
|
|
weaponHand = vr_weaponHand.GetInteger();
|
|
|
|
idWeapon* weapon = hands[ weaponHand ].weapon;
|
|
// In Multiplayer, weapon might not have been spawned yet.
|
|
if (weapon == NULL || hand == TP_HAND_HEAD)
|
|
{
|
|
origin = commonVr->lastViewOrigin; // Koz fixme set the origin and axis to the players view
|
|
axis = commonVr->lastViewAxis;
|
|
return false;
|
|
}
|
|
weapon_t currentWeap = weapon->IdentifyWeapon();
|
|
// Carl: weapon hand
|
|
if ( hand == 1 || hand == TP_HAND_RIGHT + vr_weaponHand.GetInteger() )
|
|
{
|
|
switch ( currentWeap )
|
|
{
|
|
case WEAPON_NONE:
|
|
case WEAPON_FISTS:
|
|
case WEAPON_SOULCUBE:
|
|
case WEAPON_PDA:
|
|
case WEAPON_HANDGRENADE:
|
|
origin = weapon->viewWeaponOrigin; // Koz fixme set the origin and axis to the weapon default
|
|
axis = weapon->viewWeaponAxis;
|
|
return false;
|
|
break;
|
|
|
|
default:
|
|
return weapon->GetMuzzlePositionWithHacks( origin, axis );
|
|
break;
|
|
}
|
|
}
|
|
// Carl: flashlight hand
|
|
else if ( commonVr->GetCurrentFlashlightMode() == FLASHLIGHT_HAND && weaponEnabled && !spectating && !gameLocal.world->spawnArgs.GetBool("no_Weapons") && !game->IsPDAOpen() && !commonVr->PDAforcetoggle && hands[0].currentWeapon != weapon_pda && hands[1].currentWeapon != weapon_pda )
|
|
{
|
|
weapon_t currentWeapon = flashlight->IdentifyWeapon();
|
|
CalculateViewFlashlightPos( origin, axis, flashlightOffsets[ int( currentWeapon ) ] );
|
|
return false;
|
|
}
|
|
// Carl: todo empty non-weapon hand (currently using head instead)
|
|
else
|
|
{
|
|
origin = commonVr->lastViewOrigin; // Koz fixme set the origin and axis to the players view
|
|
axis = commonVr->lastViewAxis;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz idPlayer::CalculateViewFlashlightPos
|
|
Calculate the flashlight orientation
|
|
==============
|
|
*/
|
|
void idPlayer::CalculateViewFlashlightPos( idVec3 &origin, idMat3 &axis, idVec3 flashlightOffset )
|
|
{
|
|
static idVec3 viewOrigin = vec3_zero;
|
|
static idMat3 viewAxis = mat3_identity;
|
|
static bool setLeftHand = false;
|
|
static weapon_t curWeap = WEAPON_NONE;
|
|
static idMat3 swapMat = idAngles( 180.0f, 0.0f, 180.0f ).ToMat3();
|
|
|
|
origin = GetEyePosition();
|
|
|
|
origin += commonVr->leanOffset;
|
|
|
|
axis = idAngles( 0.0, viewAngles.yaw, 0.0f ).ToMat3();
|
|
axis = idAngles( 0.0, viewAngles.yaw - commonVr->bodyYawOffset, 0.0f ).ToMat3();
|
|
|
|
int flashlightMode = commonVr->GetCurrentFlashlightMode();
|
|
|
|
setLeftHand = false;
|
|
//move the flashlight to alternate location for items with no mount
|
|
|
|
if ( spectating || !weaponEnabled || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) flashlightMode = FLASHLIGHT_BODY;
|
|
|
|
if ( flashlightMode == FLASHLIGHT_HAND )
|
|
{
|
|
if ( game->IsPDAOpen() || commonVr->PDAforcetoggle || hands[ 0 ].currentWeapon == weapon_pda || hands[ 1 ].currentWeapon == weapon_pda || !commonVr->VR_USE_MOTION_CONTROLS || (commonVr->handInGui && flashlightMode == FLASHLIGHT_GUN) )
|
|
{
|
|
flashlightMode = FLASHLIGHT_HEAD;
|
|
}
|
|
}
|
|
|
|
idWeapon* weaponWithFlashlightMounted = NULL;
|
|
|
|
if( flashlightMode == FLASHLIGHT_GUN )
|
|
{
|
|
weaponWithFlashlightMounted = GetWeaponWithMountedFlashlight();
|
|
if( !weaponWithFlashlightMounted || !weaponWithFlashlightMounted->GetMuzzlePositionWithHacks( origin, axis ) || commonVr->handInGui )
|
|
{
|
|
idAngles flashlightAx = axis.ToAngles();
|
|
flashlightMode = FLASHLIGHT_HEAD;
|
|
if( game->isVR ) axis = idAngles( flashlightAx.pitch, flashlightAx.yaw - commonVr->bodyYawOffset, flashlightAx.roll ).ToMat3();
|
|
|
|
}
|
|
}
|
|
else if( flashlightMode == FLASHLIGHT_PISTOL )
|
|
{
|
|
if( hands[0].currentWeapon == weapon_pistol && !hands[0].isTheDuplicate )
|
|
weaponWithFlashlightMounted = hands[0].weapon.GetEntity();
|
|
else if( hands[1].currentWeapon == weapon_pistol && !hands[1].isTheDuplicate )
|
|
weaponWithFlashlightMounted = hands[1].weapon.GetEntity();
|
|
else
|
|
weaponWithFlashlightMounted = NULL;
|
|
|
|
if( !weaponWithFlashlightMounted || !weaponWithFlashlightMounted->GetMuzzlePositionWithHacks( origin, axis ) || commonVr->handInGui )
|
|
{
|
|
idAngles flashlightAx = axis.ToAngles();
|
|
flashlightMode = FLASHLIGHT_INVENTORY;
|
|
if( game->isVR ) axis = idAngles( flashlightAx.pitch, flashlightAx.yaw - commonVr->bodyYawOffset, flashlightAx.roll ).ToMat3();
|
|
}
|
|
else
|
|
flashlightMode = FLASHLIGHT_GUN;
|
|
}
|
|
|
|
int oldFlashlightPosition = commonVr->currentFlashlightPosition;
|
|
commonVr->currentFlashlightPosition = flashlightMode;
|
|
|
|
switch ( flashlightMode )
|
|
{
|
|
|
|
case FLASHLIGHT_GUN:
|
|
// move the flashlight to the weapon
|
|
|
|
/* was for adjusting
|
|
flashlightOffset.x += ftx.GetFloat();
|
|
flashlightOffset.y += fty.GetFloat();
|
|
flashlightOffset.z += ftz.GetFloat();
|
|
*/
|
|
|
|
origin += flashlightOffset.x * axis[1] + flashlightOffset.y * axis[0] + flashlightOffset.z * axis[2];
|
|
|
|
curWeap = weaponWithFlashlightMounted->IdentifyWeapon();
|
|
if ( curWeap == WEAPON_ROCKETLAUNCHER )
|
|
{
|
|
//hack was already present in the code to fix borked alignments for these weapons,
|
|
//we need to put them back
|
|
//std::swap( axis[0], axis[2] );
|
|
axis = -1 * axis;
|
|
//axis = idAngles( 180.0f, 0.0f, 180.0f ).ToMat3() * axis;
|
|
axis = swapMat * axis;
|
|
}
|
|
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = 0;
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = 0;
|
|
// if we just got the flashlight/weapon out, start with the flashlight turned on
|
|
if( oldFlashlightPosition == FLASHLIGHT_INVENTORY || oldFlashlightPosition == FLASHLIGHT_NONE )
|
|
FlashlightOn();
|
|
setLeftHand = true;
|
|
break;
|
|
|
|
case FLASHLIGHT_HEAD:
|
|
// Flashlight on helmet
|
|
origin = commonVr->lastViewOrigin;
|
|
axis = commonVr->lastViewAxis;
|
|
|
|
origin += vr_flashlightHelmetPosY.GetFloat() * axis[1] + vr_flashlightHelmetPosZ.GetFloat() * axis[0] + vr_flashlightHelmetPosX.GetFloat() * axis[2];
|
|
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
|
|
if ( curWeap == WEAPON_PDA && commonVr->VR_USE_MOTION_CONTROLS ) return;
|
|
|
|
setLeftHand = true;
|
|
break;
|
|
|
|
case FLASHLIGHT_INVENTORY:
|
|
case FLASHLIGHT_NONE:
|
|
// don't draw the flashlight or its shadow
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
// if we just put the flashlight/weapon away, turn it off
|
|
if( oldFlashlightPosition != FLASHLIGHT_INVENTORY && oldFlashlightPosition != FLASHLIGHT_NONE )
|
|
FlashlightOff();
|
|
setLeftHand = true;
|
|
break;
|
|
|
|
case FLASHLIGHT_BODY:
|
|
default: // this is the original body mount code.
|
|
{
|
|
idWeapon* weapon = GetMainWeapon();
|
|
origin = weapon->playerViewOrigin;
|
|
axis = weapon->playerViewAxis;
|
|
float fraccos = cos( (gameLocal.framenum & 255) / 127.0f * idMath::PI );
|
|
static unsigned int divisor = 32;
|
|
unsigned int val = (gameLocal.framenum + gameLocal.framenum / divisor) & 255;
|
|
float fraccos2 = cos( val / 127.0f * idMath::PI );
|
|
static idVec3 baseAdjustPos = idVec3( -8.0f, -20.0f, -10.0f ); // rt, fwd, up
|
|
//static idVec3 baseAdjustPos = idVec3( 0, 0, 0 ); // rt, fwd, up
|
|
|
|
if ( game->isVR )
|
|
{
|
|
baseAdjustPos.x = vr_flashlightBodyPosX.GetFloat();
|
|
baseAdjustPos.y = vr_flashlightBodyPosY.GetFloat();
|
|
baseAdjustPos.z = vr_flashlightBodyPosZ.GetFloat();
|
|
}
|
|
|
|
static float pscale = 0.5f;
|
|
static float yscale = 0.125f;
|
|
idVec3 adjustPos = baseAdjustPos;// + ( idVec3( fraccos, 0.0f, fraccos2 ) * scale );
|
|
origin += adjustPos.x * axis[1] + adjustPos.y * axis[0] + adjustPos.z * axis[2];
|
|
//viewWeaponOrigin += owner->viewBob;
|
|
// static idAngles baseAdjustAng = idAngles( 88.0f, 10.0f, 0.0f ); //
|
|
static idAngles baseAdjustAng = idAngles( 0.0f,10.0f, 0.0f ); //
|
|
idAngles adjustAng = baseAdjustAng + idAngles( fraccos * pscale, fraccos2 * yscale, 0.0f );
|
|
// adjustAng += owner->GetViewBobAngles();
|
|
axis = adjustAng.ToMat3() * axis;
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
// Carl: This fixes the flashlight shadow in Mars City 1.
|
|
// I could check if ( spectating || !weaponEnabled || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
// but I don't think an armor-mounted light should ever cast a flashlight-shaped shadow
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
setLeftHand = true;
|
|
}
|
|
}
|
|
|
|
|
|
// Koz fixme this is where we set the left hand position. Yes it's a stupid place to do it move later
|
|
//GBFIX - For some reason it always thinks the left hand has PDA open
|
|
//if ( game->IsPDAOpen() || commonVr->PDAforcetoggle || hands[0].currentWeapon == weapon_pda || hands[1].currentWeapon == weapon_pda ) return; //dont dont anything with the left hand if motion controlling the PDA, only if fixed.
|
|
|
|
if ( commonVr->VR_USE_MOTION_CONTROLS ) // && ( !game->IsPDAOpen() || commonVr->PDAforcetoggle || currentWeapon == weapon_pda ) )
|
|
{
|
|
static idVec3 motionPosition = vec3_zero;
|
|
static idQuat motionRotation;
|
|
static idVec3 originOffset = vec3_zero;
|
|
static int currentHand = 0;
|
|
static idAngles hmdAngles;
|
|
static idVec3 headPositionDelta;
|
|
static idVec3 bodyPositionDelta;
|
|
static idVec3 absolutePosition;
|
|
static idQuat flashlightPitch;
|
|
static bool isFlashlight = false;
|
|
|
|
currentHand = 1 - vr_weaponHand.GetInteger();
|
|
originOffset = flashlight->weaponHandDefaultPos[currentHand];
|
|
flashlightPitch = idAngles( vr_motionFlashPitchAdj.GetFloat(), 0.f, 0.0f ).ToQuat();
|
|
isFlashlight = true;
|
|
|
|
commonVr->MotionControlGetHand( currentHand, motionPosition, motionRotation );
|
|
|
|
motionRotation = flashlightPitch * motionRotation;
|
|
|
|
GetViewPos( viewOrigin, viewAxis ); //GetEyePosition();
|
|
|
|
viewOrigin += commonVr->leanOffset;
|
|
|
|
//commonVr->HMDGetOrientation( hmdAngles, headPositionDelta, bodyPositionDelta, absolutePosition, false );
|
|
|
|
hmdAngles = commonVr->poseHmdAngles;
|
|
headPositionDelta = commonVr->poseHmdHeadPositionDelta;
|
|
bodyPositionDelta = commonVr->poseHmdBodyPositionDelta;
|
|
absolutePosition = commonVr->poseHmdAbsolutePosition;
|
|
|
|
viewAxis = idAngles( 0.0, viewAxis.ToAngles().yaw - commonVr->bodyYawOffset, 0.0f ).ToMat3();
|
|
viewOrigin += viewAxis[0] * headPositionDelta.x + viewAxis[1] * headPositionDelta.y + viewAxis[2] * headPositionDelta.z;
|
|
|
|
viewOrigin += motionPosition * viewAxis;
|
|
viewAxis = motionRotation.ToMat3() * viewAxis;
|
|
|
|
idAngles motRot = motionRotation.ToAngles();
|
|
motRot.yaw -= commonVr->bodyYawOffset;
|
|
motRot.Normalize180();
|
|
motionRotation = motRot.ToQuat();
|
|
|
|
|
|
// Koz fixme:
|
|
// Koz hack , the alignment isn't quite right, so do a quick hack here so the hand and flashlight align
|
|
// better with the controllers.
|
|
// need to really fix this right, the whole body/viewweapon pose attacher code is a complete trainwreck now.
|
|
|
|
const idVec3 flashlightPosHack[2] = { idVec3( 0.0f, -1.0f, 0.5f ), idVec3( 0.0f, 0.85f, 0.5f ) };
|
|
viewOrigin += flashlightPosHack[currentHand] * viewAxis;
|
|
|
|
//DebugCross( viewOrigin, viewAxis, colorYellow );
|
|
SetHandIKPos( currentHand , viewOrigin, viewAxis, motionRotation, isFlashlight );
|
|
|
|
if ( flashlightMode == FLASHLIGHT_HAND )
|
|
{
|
|
origin = viewOrigin;
|
|
origin -= originOffset * viewAxis;
|
|
|
|
int wepn = (hands[0].weapon->IdentifyWeapon() == WEAPON_PDA || hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_PDA ) ? weapon_pda : weapon_flashlight;
|
|
origin += handWeaponAttacherToDefaultOffset[currentHand][wepn/*weapon_flashlight*/] * viewAxis;
|
|
axis = viewAxis;
|
|
|
|
//DebugCross( origin, axis, colorOrange );
|
|
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = 0;
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = 0;
|
|
}
|
|
}
|
|
|
|
if ( gameLocal.inCinematic )
|
|
{
|
|
flashlight->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
flashlight->GetRenderEntity()->suppressShadowInViewID = entityNumber + 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetHarvestWeapon
|
|
Carl: Dual wielding, get the specific weapon used to harvest souls (Soul Cube or Artifact)
|
|
Returns the required one if you're holding it, or the other one, or the main weapon
|
|
===============
|
|
*/
|
|
idWeapon* idPlayer::GetHarvestWeapon( idStr requiredWeapons )
|
|
{
|
|
// Carl: TODO dual wielding
|
|
if( hands[ 0 ].weapon && ( hands[ 0 ].weapon->IdentifyWeapon() == WEAPON_SOULCUBE || hands[ 0 ].weapon->IdentifyWeapon() == WEAPON_ARTIFACT ) )
|
|
return hands[ 0 ].weapon;
|
|
if( hands[ 1 ].weapon && ( hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_SOULCUBE || hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_ARTIFACT ) )
|
|
return hands[ 1 ].weapon;
|
|
return GetMainWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetMainWeapon
|
|
Carl: Dual wielding, when the code needs just one weapon, guess which one is the "main" one
|
|
===============
|
|
*/
|
|
idWeapon* idPlayer::GetMainWeapon()
|
|
{
|
|
// Carl: TODO dual wielding
|
|
return hands[ GetBestWeaponHand() ].weapon;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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) + idVec3(0, 0, commonVr->headHeightDiff);
|
|
|
|
/*
|
|
if (pVRClientInfo)
|
|
{
|
|
float eyeHeight = 0;
|
|
float vrEyeHeight = (-(pVRClientInfo->hmdposition[1] + vr_heightoffset.GetFloat()) * vr_worldscale.GetFloat());
|
|
|
|
//Add special handling for physical crouching at some point
|
|
if (physicsObj.IsCrouching() && PHYSICAL_CROUCH) {
|
|
eyeHeight = vrEyeHeight;
|
|
}
|
|
else
|
|
{
|
|
eyeHeight = vrEyeHeight - (eyeOffset.z - pm_normalviewheight.GetFloat());
|
|
}
|
|
|
|
return org + ( GetPhysics()->GetGravityNormal() * eyeHeight);
|
|
} else{
|
|
return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
|
|
}*/
|
|
}
|
|
|
|
void idPlayer::SetVRClientInfo(vrClientInfo *pVR)
|
|
{
|
|
commonVr->pVRClientInfo = pVR;
|
|
pVRClientInfo = pVR;
|
|
//Need to do this at least once
|
|
if(!commonVr->initialResetPerformed) {
|
|
OrientHMDBody();
|
|
}
|
|
commonVr->FrameStart();
|
|
}
|
|
|
|
vrClientInfo* idPlayer::GetVRClientInfo()
|
|
{
|
|
return pVRClientInfo;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetViewPos
|
|
===============
|
|
*/
|
|
void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
|
|
idAngles angles;
|
|
|
|
if ( game->isVR )
|
|
{
|
|
GetViewPosVR( origin, axis );
|
|
return;
|
|
}
|
|
//GBFIX DrBeef Old Code
|
|
/*
|
|
// if dead, fix the angle and don't add any kick
|
|
if ( health <= 0 ) {
|
|
angles.yaw = viewAngles.yaw;
|
|
//angles.roll = 40;
|
|
//angles.pitch = -15;
|
|
axis = angles.ToMat3();
|
|
origin = GetEyePosition();
|
|
} else {
|
|
origin = GetEyePosition() + viewBob;
|
|
if (pVRClientInfo)
|
|
{
|
|
//Use pitch and roll from HMD
|
|
angles.Set(pVRClientInfo->hmdorientation[PITCH], viewAngles.yaw, pVRClientInfo->hmdorientation[ROLL]);
|
|
} else{
|
|
angles = viewAngles + viewBobAngles + playerView.AngleOffset();
|
|
}
|
|
|
|
axis = angles.ToMat3() * physicsObj.GetGravityAxis();
|
|
|
|
// adjust the origin based on the camera nodal distance (eye distance from neck)
|
|
origin += physicsObj.GetGravityNormal() * g_viewNodalZ.GetFloat();
|
|
origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetWeaponInHand
|
|
Carl: Dual wielding. Returns NULL if no weapon in the hand or if hand < 0 (no hand).
|
|
===============
|
|
*/
|
|
idWeapon* idPlayer::GetWeaponInHand( int hand ) const
|
|
{
|
|
if( hand < 0 )
|
|
return NULL;
|
|
else
|
|
return hands[ hand ].weapon;
|
|
}
|
|
|
|
// Carl: for now, only one of the weapons we are holding can have a flashlight mounted to it
|
|
// because we haven't implemented dual wielding flashlights yet.
|
|
idWeapon * idPlayer::GetWeaponWithMountedFlashlight()
|
|
{
|
|
// The pistol we start with in RoE for XBox has a flashlight mounted, so prefer that weapon first (for now)
|
|
if( commonVr->currentFlashlightMode == FLASHLIGHT_PISTOL )
|
|
{
|
|
if( hands[0].currentWeapon == weapon_pistol && !hands[0].isTheDuplicate )
|
|
return hands[0].weapon;
|
|
if( hands[1].currentWeapon == weapon_pistol && !hands[1].isTheDuplicate )
|
|
return hands[1].weapon;
|
|
}
|
|
// Carl: todo
|
|
return GetMainWeapon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::GetViewPosVR
|
|
===============
|
|
*/
|
|
void idPlayer::GetViewPosVR( idVec3 &origin, idMat3 &axis ) const {
|
|
|
|
idAngles angles;
|
|
|
|
// if dead, fix the angle and don't add any kick
|
|
if ( health <= 0 )
|
|
{
|
|
angles = viewAngles;
|
|
axis = angles.ToMat3();
|
|
origin = GetEyePosition();
|
|
return;
|
|
}
|
|
|
|
//Carl: Use head and neck rotation model
|
|
float eyeHeightAboveRotationPoint;
|
|
float eyeShiftRight = 0;
|
|
|
|
eyeHeightAboveRotationPoint = 5;//
|
|
|
|
origin = GetEyePosition(); // +viewBob;
|
|
// Carl: No view bobbing unless knockback is enabled. This isn't strictly a knockback, but close enough.
|
|
// This is the bounce when you land after jumping
|
|
|
|
// re-enabling this until a better method is implemented, as headbob is how vertical smoothing is implemented when going over stairs/bumps
|
|
// bobbing can be disabled via the walkbob and runbob cvars until a better method devised.
|
|
// if (vr_knockBack.GetBool())
|
|
|
|
|
|
origin += viewBob;
|
|
angles = viewAngles; // NO VIEW KICKING +playerView.AngleOffset();
|
|
axis = angles.ToMat3();// *physicsObj.GetGravityAxis();
|
|
|
|
// Move pivot point down so looking straight ahead is a no-op on the Z
|
|
// const idVec3 & gravityVector = physicsObj.GetGravityNormal();
|
|
//origin += gravityVector * g_viewNodalZ.GetFloat();
|
|
|
|
|
|
// adjust the origin based on the camera nodal distance (eye distance from neck)
|
|
//origin += axis[0] * g_viewNodalX.GetFloat() - axis[1] * eyeShiftRight + axis[2] * eyeHeightAboveRotationPoint;
|
|
//origin += axis[1] * -eyeShiftRight + axis[2] * eyeHeightAboveRotationPoint;
|
|
origin += axis[2] * eyeHeightAboveRotationPoint;
|
|
|
|
//common->Printf( "GetViewPosVr returning %s\n", origin.ToString() );
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::CalculateFirstPersonView
|
|
===============
|
|
*/
|
|
void idPlayer::CalculateFirstPersonView( void ) {
|
|
if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
|
|
// Displays the view from the point of view of the "camera" joint in the player model
|
|
|
|
idMat3 axis;
|
|
idVec3 origin;
|
|
idAngles ang;
|
|
|
|
ang = viewBobAngles + playerView.AngleOffset();
|
|
ang.yaw += viewAxis[ 0 ].ToYaw();
|
|
|
|
jointHandle_t joint = animator.GetJointHandle( "camera" );
|
|
animator.GetJointTransform( joint, gameLocal.time, origin, axis );
|
|
firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
|
|
firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
|
|
} else {
|
|
// offset for local bobbing and kicks
|
|
GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
|
|
#if 0
|
|
// shakefrom sound stuff only happens in first person
|
|
firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
|
|
#endif
|
|
}
|
|
CalculateLeftHand();
|
|
CalculateRightHand();
|
|
CalculateWaist();
|
|
}
|
|
|
|
void idPlayer::CalculateWaist()
|
|
{
|
|
idMat3 & hmdAxis = commonVr->lastHMDViewAxis;
|
|
|
|
waistOrigin = hmdAxis * neckOffset + commonVr->lastHMDViewOrigin;
|
|
waistOrigin.z += waistZ;
|
|
|
|
if ( hmdAxis[0].z < 0 ) // looking down
|
|
{
|
|
if ( hmdAxis[2].z > 0 )
|
|
{
|
|
// use a point between head forward and upward
|
|
float h = hmdAxis[2].z - hmdAxis[0].z;
|
|
float x = -hmdAxis[0].z / h;
|
|
float y = hmdAxis[2].z / h;
|
|
idVec3 i = hmdAxis[0] * y + hmdAxis[2] * x;
|
|
float yaw = atan2( i.y, i.x ) * idMath::M_RAD2DEG;
|
|
waistAxis = idAngles( 0, yaw, 0 ).ToMat3();
|
|
}
|
|
else
|
|
{
|
|
// use a point between head backward and upward
|
|
float h = -hmdAxis[2].z - hmdAxis[0].z;
|
|
float x = -hmdAxis[0].z / h;
|
|
float y = hmdAxis[2].z / h;
|
|
idVec3 i = hmdAxis[0] * y + hmdAxis[2] * x;
|
|
float yaw = atan2( i.y, i.x ) * idMath::M_RAD2DEG;
|
|
waistAxis = idAngles( 0, yaw, 0 ).ToMat3();
|
|
}
|
|
}
|
|
else // fallback
|
|
{
|
|
waistAxis = idAngles( 0, hmdAxis.ToAngles().yaw, 0 ).ToMat3();
|
|
}
|
|
}
|
|
|
|
void idPlayer::CalculateLeftHand()
|
|
{
|
|
slotIndex_t oldSlot = hands[HAND_LEFT].handSlot;
|
|
slotIndex_t slot = SLOT_NONE;
|
|
if ( commonVr->hasHMD )
|
|
{
|
|
// remove pitch
|
|
idMat3 axis = firstPersonViewAxis;
|
|
//float pitch = idMath::M_RAD2DEG * asin(axis[0][2]);
|
|
//idAngles angles(pitch, 0, 0);
|
|
//axis = angles.ToMat3() * axis;
|
|
//leftHandOrigin = hmdOrigin + (usercmd.vrLeftControllerOrigin - usercmd.vrHeadOrigin) * vrFaceForward * axis;
|
|
//leftHandAxis = usercmd.vrLeftControllerAxis * vrFaceForward * axis;
|
|
|
|
if( !vr_slotDisable.GetBool() )
|
|
{
|
|
for( int i = 0; i < SLOT_COUNT; i++ )
|
|
{
|
|
idVec3 slotOrigin = slots[i].origin;
|
|
if ( vr_weaponHand.GetInteger() && i != SLOT_FLASHLIGHT_SHOULDER )
|
|
slotOrigin.y *= -1;
|
|
idVec3 origin = waistOrigin + slotOrigin * waistAxis;
|
|
if( ( hands[HAND_LEFT].handOrigin - origin ).LengthSqr() < slots[i].radiusSq )
|
|
{
|
|
slot = (slotIndex_t)i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//hands[HAND_LEFT].handOrigin = hmdOrigin + hmdAxis[2] * -5;
|
|
//hands[HAND_LEFT].handAxis = hmdAxis;
|
|
}
|
|
if( oldSlot != slot )
|
|
{
|
|
hands[HAND_LEFT].SetControllerShake( vr_slotMag.GetFloat(), vr_slotDur.GetInteger(), vr_slotMag.GetFloat(), vr_slotDur.GetInteger() );
|
|
}
|
|
hands[HAND_LEFT].handSlot = slot;
|
|
}
|
|
|
|
void idPlayer::CalculateRightHand()
|
|
{
|
|
slotIndex_t oldSlot = hands[HAND_RIGHT].handSlot;
|
|
slotIndex_t slot = SLOT_NONE;
|
|
if ( commonVr->hasHMD )
|
|
{
|
|
// remove pitch
|
|
idMat3 axis = firstPersonViewAxis;
|
|
//float pitch = idMath::M_RAD2DEG * asin(axis[0][2]);
|
|
//idAngles angles(pitch, 0, 0);
|
|
//axis = angles.ToMat3() * axis;
|
|
//rightHandOrigin = hmdOrigin + (usercmd.vrRightControllerOrigin - usercmd.vrHeadOrigin) * vrFaceForward * axis;
|
|
//rightHandAxis = usercmd.vrRightControllerAxis * vrFaceForward * axis;
|
|
|
|
if( !vr_slotDisable.GetBool() )
|
|
{
|
|
for( int i = 0; i < SLOT_COUNT; i++ )
|
|
{
|
|
idVec3 slotOrigin = slots[i].origin;
|
|
if ( vr_weaponHand.GetInteger() && i != SLOT_FLASHLIGHT_SHOULDER )
|
|
slotOrigin.y *= -1;
|
|
idVec3 origin = waistOrigin + slotOrigin * waistAxis;
|
|
if( (hands[HAND_RIGHT].handOrigin - origin).LengthSqr() < slots[i].radiusSq )
|
|
{
|
|
slot = (slotIndex_t)i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//rightHandOrigin = hmdOrigin + hmdAxis[2] * -5;
|
|
//rightHandAxis = hmdAxis;
|
|
}
|
|
if( oldSlot != slot )
|
|
{
|
|
hands[HAND_RIGHT].SetControllerShake(vr_slotMag.GetFloat(), vr_slotDur.GetInteger(), vr_slotMag.GetFloat(), vr_slotDur.GetInteger() );
|
|
}
|
|
hands[HAND_RIGHT].handSlot = slot;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
idPlayer::GetRenderView
|
|
|
|
Returns the renderView that was calculated for this tic
|
|
==================
|
|
*/
|
|
renderView_t *idPlayer::GetRenderView( void ) {
|
|
return renderView;
|
|
}
|
|
|
|
bool idPlayer::GetTeleportBeamOrigin( idVec3 &beamOrigin, idMat3 &beamAxis ) // returns true if the teleport beam should be displayed
|
|
{
|
|
//const idVec3 beamOff[2] = { idVec3( 2.5f, 0.0f, 1.0f ), idVec3( 2.5f, 0.0f, 1.5f ) };
|
|
const idVec3 beamOff[2] = { idVec3( 4.5f, 0.0f, 1.0f ), idVec3( 4.5f, 0.0f, 1.5f ) };
|
|
|
|
if ( gameLocal.inCinematic || AI_DEAD || game->IsPDAOpen() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int teleportHand, hand;
|
|
teleportHand = vr_teleport.GetInteger();
|
|
if( teleportHand == TP_HAND_RIGHT )
|
|
hand = HAND_RIGHT;
|
|
else if( teleportHand == TP_HAND_LEFT )
|
|
hand = HAND_LEFT;
|
|
else if( teleportHand == TP_HAND_HEAD )
|
|
hand = vr_weaponHand.GetInteger();
|
|
else if( teleportHand == 1 )
|
|
hand = vr_weaponHand.GetInteger();
|
|
else
|
|
hand = 1 - vr_weaponHand.GetInteger();
|
|
|
|
if ( teleportHand <= 0 || ( teleportHand == 1 && commonVr->VR_USE_MOTION_CONTROLS ) )// teleport aim mode is to use the standard weaponsight, so just return.
|
|
{
|
|
return false;
|
|
}
|
|
else if( teleportHand == TP_HAND_HEAD || !commonVr->VR_USE_MOTION_CONTROLS ) // beam originates from in front of the head
|
|
{
|
|
beamAxis = commonVr->lastHMDViewAxis;
|
|
beamOrigin = commonVr->lastHMDViewOrigin + 12 * beamAxis[ 0 ];
|
|
beamOrigin = beamOrigin + 5 * beamAxis[ 2 ];
|
|
}
|
|
else // teleport aim origin from the hand
|
|
{
|
|
if ( !hands[hand].weapon->ShowCrosshair() ||
|
|
hands[hand].weapon->IsHidden() ||
|
|
hands[ hand ].weapon->hideOffset != 0 || // Koz - turn off lasersight If gun is lowered ( in gui ).
|
|
commonVr->handInGui // turn off lasersight if hand is in gui.
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( hands[hand].holdingFlashlight() ) // flashlight is in the hand, so originate the beam slightly in front of the flashlight.
|
|
{
|
|
beamAxis = flashlight->GetRenderEntity()->axis;
|
|
beamOrigin = flashlight->GetRenderEntity()->origin + 10 * beamAxis[ 0 ];
|
|
}
|
|
else if ( !hands[hand].controllingWeapon() ) // just send it from the hand.
|
|
{
|
|
if( animator.GetJointTransform( ik_hand[ hand ], gameLocal.time, beamOrigin, beamAxis ) )
|
|
{
|
|
beamAxis = ik_handCorrectAxis[ hand ][ 1 ].Inverse() * beamAxis;
|
|
|
|
beamOrigin = beamOrigin * renderEntity.axis + renderEntity.origin;
|
|
beamAxis = beamAxis * renderEntity.axis;
|
|
beamOrigin += beamOff[ hand ] * beamAxis;
|
|
}
|
|
else
|
|
{
|
|
// we failed to get the joint for some reason, so just default to the weapon origin and axis
|
|
beamOrigin = hands[ hand ].weapon->viewWeaponOrigin;
|
|
beamAxis = hands[ hand ].weapon->viewWeaponAxis;
|
|
}
|
|
}
|
|
else if ( !hands[ hand ].weapon->GetMuzzlePositionWithHacks( beamOrigin, beamAxis ) )
|
|
{
|
|
// weapon has no muzzle, so get the position and axis of the animated hand joint
|
|
if ( animator.GetJointTransform( ik_hand[hand], gameLocal.time, beamOrigin, beamAxis ) )
|
|
{
|
|
beamAxis = ik_handCorrectAxis[hand][1].Inverse() * beamAxis;
|
|
|
|
beamOrigin = beamOrigin * renderEntity.axis + renderEntity.origin;
|
|
beamAxis = beamAxis * renderEntity.axis;
|
|
beamOrigin += beamOff[hand] * beamAxis;
|
|
}
|
|
else
|
|
{
|
|
// we failed to get the joint for some reason, so just default to the weapon origin and axis
|
|
beamOrigin = hands[ hand ].weapon->viewWeaponOrigin;
|
|
beamAxis = hands[ hand ].weapon->viewWeaponAxis;
|
|
}
|
|
}
|
|
else // had a valid muzzle;
|
|
{
|
|
beamOrigin -= 2 * beamAxis[1]; // if coming from the muzzle, move 2 in down, looks better when it doesn't interfere with the laser sight.
|
|
}
|
|
|
|
if ( hands[hand].weapon->IdentifyWeapon() == WEAPON_CHAINSAW )
|
|
{
|
|
beamOrigin += 6 * beamAxis[0]; // move the beam origin 6 inches forward
|
|
}
|
|
else
|
|
{
|
|
beamOrigin += 4 * beamAxis[0]; // move the beam origin 4 inches forward
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::CalculateRenderView
|
|
|
|
create the renderView for the current tic
|
|
==================
|
|
*/
|
|
void idPlayer::CalculateRenderView( void ) {
|
|
// Koz add headtracking
|
|
static idAngles hmdAngles( 0.0, 0.0, 0.0 );
|
|
static idVec3 headPositionDelta = vec3_zero;
|
|
static idVec3 bodyPositionDelta = vec3_zero;
|
|
|
|
static bool wasCinematic = false;
|
|
static idVec3 cinematicOffset = vec3_zero;
|
|
static float cineYawOffset = 0.0f;
|
|
|
|
static bool wasThirdPerson = false;
|
|
static idVec3 thirdPersonOffset = vec3_zero;
|
|
static idVec3 thirdPersonOrigin = vec3_zero;
|
|
static idMat3 thirdPersonAxis = mat3_identity;
|
|
static float thirdPersonBodyYawOffset = 0.0f;
|
|
|
|
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() || commonVr->VR_GAME_PAUSED) {
|
|
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 ( pm_thirdPersonDeath.GetBool() ) {
|
|
range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
|
|
OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
|
|
} 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() );
|
|
}
|
|
|
|
if ( game->isVR )
|
|
{
|
|
|
|
// Koz headtracker does not modify the model rotations
|
|
// offsets to body rotation added here
|
|
|
|
// body position based on neck model
|
|
// Koz fixme fix this.
|
|
|
|
// Koz begin : Add headtracking
|
|
static idVec3 absolutePosition;
|
|
|
|
hmdAngles = commonVr->poseHmdAngles;
|
|
|
|
|
|
|
|
headPositionDelta = commonVr->poseHmdHeadPositionDelta;
|
|
bodyPositionDelta = commonVr->poseHmdBodyPositionDelta;
|
|
absolutePosition = commonVr->poseHmdAbsolutePosition;
|
|
|
|
idVec3 origin = renderView->vieworg;
|
|
idAngles angles = renderView->viewaxis.ToAngles();
|
|
idMat3 axis = renderView->viewaxis;
|
|
float yawOffset = commonVr->bodyYawOffset;
|
|
|
|
if ( gameLocal.inCinematic || privateCameraView )
|
|
{
|
|
if ( wasCinematic == false )
|
|
{
|
|
wasCinematic = true;
|
|
commonVr->cinematicStartViewYaw = hmdAngles.yaw + commonVr->trackingOriginYawOffset;
|
|
commonVr->cinematicStartPosition = absolutePosition + (commonVr->trackingOriginOffset * idAngles( 0.0f, commonVr->trackingOriginYawOffset, 0.0f ).ToMat3());
|
|
cineYawOffset = hmdAngles.yaw - yawOffset;
|
|
//commonVr->cinematicStartPosition.x = -commonVr->hmdTrackingState.HeadPose.ThePose.Position.z;
|
|
//commonVr->cinematicStartPosition.y = -commonVr->hmdTrackingState.HeadPose.ThePose.Position.x;
|
|
//commonVr->cinematicStartPosition.z = commonVr->hmdTrackingState.HeadPose.ThePose.Position.y;
|
|
|
|
playerView.Flash(colorWhite, 300);
|
|
|
|
if ( vr_cinematics.GetInteger() == 2)
|
|
{
|
|
cinematicOffset = vec3_zero;
|
|
}
|
|
else
|
|
{
|
|
cinematicOffset = absolutePosition;
|
|
}
|
|
}
|
|
|
|
if (vr_cinematics.GetInteger() == 2)
|
|
{
|
|
headPositionDelta = bodyPositionDelta = vec3_zero;
|
|
}
|
|
else
|
|
{
|
|
headPositionDelta = absolutePosition - cinematicOffset;
|
|
bodyPositionDelta = vec3_zero;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wasCinematic = false;
|
|
cineYawOffset = 0.0f;
|
|
}
|
|
|
|
|
|
if (!(gameLocal.inCinematic && vr_cinematics.GetInteger() == 2))
|
|
{
|
|
|
|
//move the head in relation to the body.
|
|
//bodyYawOffsets are external rotations of the body where the head remains looking in the same direction
|
|
//e.g. when using movepoint and snapping the body to the view.
|
|
|
|
idAngles bodyAng = axis.ToAngles();
|
|
idMat3 bodyAx = idAngles( bodyAng.pitch, bodyAng.yaw - yawOffset, bodyAng.roll ).Normalize180().ToMat3();
|
|
origin = origin + bodyAx[0] * headPositionDelta.x + bodyAx[1] * headPositionDelta.y + bodyAx[2] * headPositionDelta.z;
|
|
|
|
origin += commonVr->leanOffset;
|
|
|
|
// Koz to do clean up later - added to allow cropped cinematics with original camera movements.
|
|
idQuat q1, q2;
|
|
|
|
q1 = angles.ToQuat();
|
|
q2 = idAngles( hmdAngles.pitch, ( hmdAngles.yaw - yawOffset ) - cineYawOffset, hmdAngles.roll ).ToQuat();
|
|
|
|
angles = ( q2 * q1 ).ToAngles();
|
|
|
|
angles.Normalize180();
|
|
|
|
commonVr->lastHMDYaw = hmdAngles.yaw;
|
|
commonVr->lastHMDPitch = hmdAngles.pitch;
|
|
commonVr->lastHMDRoll = hmdAngles.roll;
|
|
|
|
axis = angles.ToMat3(); // this sets the actual view axis, separate from the body axis.
|
|
|
|
commonVr->lastHMDViewOrigin = origin;
|
|
commonVr->lastHMDViewAxis = axis;
|
|
|
|
commonVr->uncrouchedHMDViewOrigin = origin;
|
|
commonVr->uncrouchedHMDViewOrigin.z -= commonVr->headHeightDiff;
|
|
|
|
|
|
if (commonVr->thirdPersonMovement)
|
|
{
|
|
if (wasThirdPerson == false)
|
|
{
|
|
wasThirdPerson = true;
|
|
thirdPersonOffset = absolutePosition;
|
|
thirdPersonOrigin = commonVr->lastHMDViewOrigin;//origin;
|
|
thirdPersonAxis = idAngles(0.0f, commonVr->lastHMDViewAxis.ToAngles().yaw, 0.0f).ToMat3();//axis;
|
|
thirdPersonBodyYawOffset = hmdAngles.yaw;// -yawOffset;
|
|
|
|
}
|
|
origin = thirdPersonOrigin;
|
|
axis = thirdPersonAxis;
|
|
yawOffset = thirdPersonBodyYawOffset;
|
|
angles = thirdPersonAxis.ToAngles();
|
|
headPositionDelta = absolutePosition - thirdPersonOffset;
|
|
bodyPositionDelta = vec3_zero;
|
|
|
|
idAngles bodyAng = thirdPersonAxis.ToAngles();
|
|
idMat3 bodyAx = idAngles(bodyAng.pitch, bodyAng.yaw - yawOffset, bodyAng.roll).Normalize180().ToMat3();
|
|
origin = thirdPersonOrigin + bodyAx[0] * headPositionDelta.x + bodyAx[1] * headPositionDelta.y + bodyAx[2] * headPositionDelta.z;
|
|
|
|
origin += commonVr->leanOffset;
|
|
|
|
angles.yaw += hmdAngles.yaw - thirdPersonBodyYawOffset; // add the current hmd orientation
|
|
angles.pitch += hmdAngles.pitch;
|
|
angles.roll += hmdAngles.roll;
|
|
angles.Normalize180();
|
|
axis = angles.ToMat3();
|
|
commonVr->thirdPersonHudAxis = axis;
|
|
commonVr->thirdPersonHudPos = origin;
|
|
|
|
commonVr->thirdPersonDelta = (origin - firstPersonViewOrigin).LengthSqr();
|
|
|
|
}
|
|
else
|
|
{
|
|
if (wasThirdPerson)
|
|
{
|
|
commonVr->thirdPersonDelta = 0.0f;;
|
|
playerView.Flash(colorBlack, 140);
|
|
}
|
|
wasThirdPerson = false;
|
|
}
|
|
|
|
}
|
|
renderView->vieworg = origin;
|
|
renderView->viewaxis = axis;
|
|
|
|
|
|
|
|
// if leaning, check if the eye is in a wall
|
|
if ( commonVr->isLeaning )
|
|
{
|
|
idBounds bounds = idBounds( idVec3( 0, -1, -1 ), idVec3( 0, 1, 1 ) );
|
|
trace_t trace;
|
|
|
|
gameLocal.clip.TraceBounds( trace, origin, origin, bounds, MASK_SHOT_RENDERMODEL, this);
|
|
if ( trace.fraction != 1.0f )
|
|
{
|
|
commonVr->leanBlank = true;
|
|
commonVr->leanBlankOffset = commonVr->leanOffset;
|
|
commonVr->leanBlankOffsetLengthSqr = commonVr->leanOffset.LengthSqr();
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
commonVr->leanBlank = false;
|
|
commonVr->leanBlankOffset = vec3_zero;
|
|
commonVr->leanBlankOffsetLengthSqr = 0.0f;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Koz fixme pause - handle the PDA model if game is paused
|
|
// really really need to move this somewhere else,
|
|
|
|
if ( !commonVr->PDAforcetoggle && commonVr->PDAforced && hands[ 0 ].weapon->IdentifyWeapon() != WEAPON_PDA && hands[ 1 ].weapon->IdentifyWeapon() != WEAPON_PDA ) // PDAforced cannot be valid if the weapon is not the PDA
|
|
{
|
|
commonVr->PDAforced = false;
|
|
commonVr->VR_GAME_PAUSED = false;
|
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
|
player->SetupPDASlot( true );
|
|
player->SetupHolsterSlot( vr_weaponHand.GetInteger(), true );
|
|
}
|
|
|
|
if ( commonVr->PDAforcetoggle )
|
|
{
|
|
int pdahand = 1 - vr_weaponHand.GetInteger();
|
|
if ( !commonVr->PDAforced )
|
|
{
|
|
if ( hands[ 0 ].weapon->IdentifyWeapon() != WEAPON_PDA && hands[ 1 ].weapon->IdentifyWeapon() != WEAPON_PDA )
|
|
{
|
|
//common->Printf( "idPlayer::CalculateRenderView calling SelectWeapon for PDA\nPDA Forced = %i, PDAForceToggle = %i\n",commonVr->PDAforced,commonVr->PDAforcetoggle );
|
|
//common->Printf( "CRV3 Calling SetupHolsterSlot( %i, %i ) \n", 1 - pdahand, commonVr->PDAforced );
|
|
|
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
|
player->SetupPDASlot( commonVr->PDAforced );
|
|
player->SetupHolsterSlot( 1 - pdahand, commonVr->PDAforced );
|
|
|
|
hands[ pdahand ].SelectWeapon( weapon_pda, true, false );
|
|
SetWeaponHandPose();
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( (hands[ 0 ].weapon->IdentifyWeapon() == WEAPON_PDA && hands[ 0 ].weapon->status == WP_READY) || ( hands[ 1 ].weapon->IdentifyWeapon() == WEAPON_PDA && hands[ 1 ].weapon->status == WP_READY ) )
|
|
{
|
|
commonVr->PDAforced = true;
|
|
commonVr->PDAforcetoggle = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // pda has been already been forced active, put it away.
|
|
|
|
TogglePDA( pdahand );
|
|
commonVr->PDAforcetoggle = false;
|
|
commonVr->PDAforced = false;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPlayer::AddAIKill
|
|
=============
|
|
*/
|
|
void idPlayer::AddAIKill( void ) {
|
|
int max_souls;
|
|
int ammo_souls;
|
|
|
|
if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
|
|
return;
|
|
}
|
|
|
|
assert( hud );
|
|
|
|
ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
|
|
max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
|
|
if ( inventory.ammo[ ammo_souls ] < max_souls ) {
|
|
inventory.ammo[ ammo_souls ]++;
|
|
if ( inventory.ammo[ ammo_souls ] >= max_souls ) {
|
|
hud->HandleNamedEvent( "soulCubeReady" );
|
|
StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPlayer::SetSoulCubeProjectile
|
|
=============
|
|
*/
|
|
void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
|
|
soulCubeProjectile = projectile;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
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 ( 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPlayer::SetInfluenceLevel
|
|
=============
|
|
*/
|
|
void idPlayer::SetInfluenceLevel( int level ) {
|
|
if ( level != influenceActive ) {
|
|
if ( level ) {
|
|
for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( ent->IsType( idProjectile::Type ) ) {
|
|
// remove all projectiles
|
|
ent->PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
}
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( weaponEnabled && hands[ h ].weapon.GetEntity() )
|
|
hands[ h ].weapon.GetEntity()->EnterCinematic();
|
|
}
|
|
} else {
|
|
physicsObj.SetLinearVelocity( vec3_origin );
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( weaponEnabled && hands[ h ].weapon.GetEntity() )
|
|
hands[ h ].weapon.GetEntity()->ExitCinematic();
|
|
}
|
|
}
|
|
influenceActive = level;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPlayer::SetInfluenceView
|
|
=============
|
|
*/
|
|
void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
|
|
influenceMaterial = NULL;
|
|
influenceEntity = NULL;
|
|
influenceSkin = NULL;
|
|
if ( mtr && *mtr ) {
|
|
influenceMaterial = declManager->FindMaterial( mtr );
|
|
}
|
|
if ( skinname && *skinname ) {
|
|
influenceSkin = declManager->FindSkin( skinname );
|
|
if ( head ) {
|
|
head->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
}
|
|
UpdateVisuals();
|
|
}
|
|
influenceRadius = radius;
|
|
if ( radius > 0.0f ) {
|
|
influenceEntity = ent;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
idPlayer::SetInfluenceFov
|
|
=============
|
|
*/
|
|
void idPlayer::SetInfluenceFov( float fov ) {
|
|
//Clamp this for VR
|
|
influenceFov = (fov == 0.0f) ? 0.0f : idMath::ClampFloat(80.0f, 120.0f, fov);
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::OnLadder
|
|
================
|
|
*/
|
|
bool idPlayer::OnLadder( void ) const {
|
|
return physicsObj.OnLadder();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::OrientHMDBody Koz align the body with the view ( move the body to point the same direction as the HMD view - does not change the view. )
|
|
==============
|
|
*/
|
|
void idPlayer::OrientHMDBody()
|
|
{
|
|
SnapBodyToView();
|
|
commonVr->bodyYawOffset = 0;
|
|
commonVr->lastHMDYaw = 0;
|
|
commonVr->HMDResetTrackingOriginOffset();
|
|
commonVr->MotionControlSetOffset();
|
|
commonVr->bodyMoveAng = 0.0f;
|
|
}
|
|
|
|
bool idPlayer::OtherHandImpulseSlot()
|
|
{
|
|
int hand = 1 - vr_weaponHand.GetInteger();
|
|
slotIndex_t otherHandSlot = hands[ hand ].handSlot;
|
|
if( otherHandSlot == SLOT_PDA_HIP )
|
|
{
|
|
// we don't have a PDA, so toggle the menu instead
|
|
if ( commonVr->PDAforced || inventory.pdas.Num() == 0 )
|
|
{
|
|
PerformImpulse( 40 );
|
|
}
|
|
else if( objectiveSystemOpen ) // if we're holding our PDA and we try to grab the PDA slot
|
|
{
|
|
// our hand is always full in this case, but that's only an issue we care and the holster contains the flashlight
|
|
if( vr_mustEmptyHands.GetBool() && commonVr->currentFlashlightMode == FLASHLIGHT_HAND )
|
|
return false;
|
|
TogglePDA( hand );
|
|
}
|
|
else if( weapon_pda >= 0 ) // if the PDA is in the slot and we try to grab it
|
|
{
|
|
if( hands[ hand ].tooFullToInteract() )
|
|
return false;
|
|
SetupPDASlot( false );
|
|
SetupHolsterSlot( 1 - hand, false );
|
|
hands[ hand ].SelectWeapon( weapon_pda, true, false );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if( otherHandSlot == SLOT_FLASHLIGHT_HEAD && !commonVr->PDAforced && !objectiveSystemOpen
|
|
&& flashlight.IsValid() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool("no_Weapons") && !vr_flashlightStrict.GetBool() )
|
|
{
|
|
// swap flashlight between head and hand
|
|
if ( commonVr->currentFlashlightPosition == FLASHLIGHT_HEAD && HasHoldableFlashlight() )
|
|
{
|
|
// Carl: move weapon from hand to inventory
|
|
hands[hand].idealWeapon = weapon_fists;
|
|
// move flashlight from head to hand
|
|
vr_flashlightMode.SetInteger(FLASHLIGHT_HAND);
|
|
vr_flashlightMode.SetModified();
|
|
}
|
|
else if ( commonVr->currentFlashlightPosition == FLASHLIGHT_HAND )
|
|
{
|
|
vr_flashlightMode.SetInteger(FLASHLIGHT_HEAD);
|
|
vr_flashlightMode.SetModified();
|
|
}
|
|
return true;
|
|
}
|
|
if( otherHandSlot == SLOT_FLASHLIGHT_SHOULDER && !commonVr->PDAforced && !objectiveSystemOpen
|
|
&& flashlight.IsValid() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool("no_Weapons") && !vr_flashlightStrict.GetBool() )
|
|
{
|
|
// swap flashlight between body and hand
|
|
if ( commonVr->currentFlashlightPosition == FLASHLIGHT_BODY && HasHoldableFlashlight() )
|
|
{
|
|
// Carl: move weapon from hand to inventory
|
|
hands[hand].idealWeapon = weapon_fists;
|
|
// move flashlight from shoulder to hand
|
|
vr_flashlightMode.SetInteger(FLASHLIGHT_HAND);
|
|
vr_flashlightMode.SetModified();
|
|
}
|
|
else if ( commonVr->currentFlashlightPosition == FLASHLIGHT_HAND )
|
|
{
|
|
vr_flashlightMode.SetInteger(FLASHLIGHT_BODY);
|
|
vr_flashlightMode.SetModified();
|
|
}
|
|
return true;
|
|
}
|
|
if (otherHandSlot == SLOT_WEAPON_HIP)
|
|
{
|
|
// Holster the PDA we are holding on the other side
|
|
if( commonVr->PDAforced )
|
|
{
|
|
SwapWeaponHand();
|
|
PerformImpulse( 40 );
|
|
}
|
|
else if( objectiveSystemOpen )
|
|
{
|
|
SwapWeaponHand();
|
|
if (hands[hand].previousWeapon == weapon_fists)
|
|
hands[hand].previousWeapon = holsteredWeapon;
|
|
TogglePDA( hand );
|
|
}
|
|
else
|
|
{
|
|
int h = 1 - vr_weaponHand.GetInteger();
|
|
if( hands[ h ].tooFullToInteract() && holsteredWeapon != weapon_fists )
|
|
return false;
|
|
SwapWeaponHand();
|
|
// pick up whatever weapon we have holstered, and magically holster our current weapon
|
|
SetupHolsterSlot( vr_weaponHand.GetInteger() );
|
|
}
|
|
return true;
|
|
}
|
|
if ( otherHandSlot == SLOT_WEAPON_BACK_BOTTOM )
|
|
{
|
|
SwapWeaponHand();
|
|
// Holster the PDA we are holding on the other side
|
|
// we don't have a PDA, so toggle the menu instead
|
|
if ( commonVr->PDAforced )
|
|
{
|
|
PerformImpulse( 40 );
|
|
}
|
|
else if( objectiveSystemOpen )
|
|
{
|
|
TogglePDA( hand );
|
|
}
|
|
|
|
hands[ hand ].PrevWeapon();
|
|
return true;
|
|
}
|
|
if ( otherHandSlot == SLOT_WEAPON_BACK_TOP )
|
|
{
|
|
// SwapWeaponHand();
|
|
// If we are holding a PDA, put it away.
|
|
// if it's the pause menu "PDA", toggle the menu instead
|
|
if ( commonVr->PDAforced )
|
|
{
|
|
PerformImpulse(40);
|
|
}
|
|
else if ( objectiveSystemOpen )
|
|
{
|
|
TogglePDA( hand );
|
|
}
|
|
hands[ hand ].NextWeapon();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idPlayer::PathToGoal
|
|
=====================
|
|
*/
|
|
bool idPlayer::PathToGoal(aasPath_t& path, int areaNum, const idVec3& origin, int goalAreaNum, const idVec3& goalOrigin) const
|
|
{
|
|
idVec3 org;
|
|
idVec3 goal;
|
|
|
|
if (!aas)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
org = origin;
|
|
|
|
if (ai_debugMove.GetBool())
|
|
{
|
|
aas->DrawArea( areaNum );
|
|
aas->DrawArea( goalAreaNum );
|
|
}
|
|
|
|
aas->PushPointIntoAreaNum(areaNum, org);
|
|
if (!areaNum)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
goal = goalOrigin;
|
|
aas->PushPointIntoAreaNum(goalAreaNum, goal);
|
|
if (!goalAreaNum)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ai_debugMove.GetBool())
|
|
{
|
|
aas->ShowWalkPath(org, goalAreaNum, goal, travelFlags);
|
|
}
|
|
return aas->WalkPathToGoal( path, areaNum, org, goalAreaNum, goal, travelFlags );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
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;
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon )
|
|
hands[ h ].weapon->ExitCinematic();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_DisableWeapon
|
|
==================
|
|
*/
|
|
void idPlayer::Event_DisableWeapon( void ) {
|
|
hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
|
|
weaponEnabled = false;
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon )
|
|
hands[ h ].weapon->EnterCinematic();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetCurrentWeapon
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetCurrentWeapon( void ) {
|
|
const char *weapon;
|
|
|
|
if( hands[vr_weaponHand.GetInteger()].currentWeapon >= 0 )
|
|
{
|
|
weapon = spawnArgs.GetString( va( "def_weapon%d", hands[ vr_weaponHand.GetInteger() ].currentWeapon ) );
|
|
idThread::ReturnString( weapon );
|
|
}
|
|
else
|
|
{
|
|
idThread::ReturnString( "" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetFlashHand // get flashlight hand
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetFlashHand() // get flashlight hand
|
|
{
|
|
static int flashlightHand = 1;
|
|
//returns 0 for right, 1 for left
|
|
flashlightHand = vr_weaponHand.GetInteger() == 0 ? 1 : 0;
|
|
|
|
idThread::ReturnInt( flashlightHand );
|
|
}
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetFlashState // get flashlight state
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetFlashState() // get flashlight state
|
|
{
|
|
static int flashlighton;
|
|
flashlighton = flashlight->lightOn ? 1 : 0 ;
|
|
// Koz debug common->Printf( "Returning flashlight state = %d\n",flashlighton );
|
|
idThread::ReturnInt( flashlighton );
|
|
}
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetFlashHandState // get flashlight hand state
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetFlashHandState() // get flashlight hand state
|
|
{
|
|
|
|
// this is for the flashlight hand
|
|
// this is not for weapon hand animations like firing, this is to change hand pose if no weapon is present or if using guis interactively
|
|
// 0 = hand empty no weapon
|
|
// 1 = fist (no flashlight)
|
|
// 2 = flashlight
|
|
// 3 = normal weapon idle hand anim - used for holding PDA.
|
|
|
|
|
|
int flashlightHand = 1;
|
|
int hand = 1 - vr_weaponHand.GetInteger();
|
|
|
|
if ( hands[hand].weapon && hands[hand].weapon->IdentifyWeapon() == WEAPON_PDA )
|
|
{
|
|
flashlightHand = 3;
|
|
}
|
|
else if ( spectating || !weaponEnabled || hiddenWeapon || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
flashlightHand = 0;
|
|
}
|
|
else if( hands[hand].currentWeapon == weapon_fists && !hands[hand].holdingFlashlight() )
|
|
{
|
|
flashlightHand = 1;
|
|
}
|
|
|
|
//else if ( commonVr->currentFlashlightPosition == FLASHLIGHT_HAND && !spectating && weaponEnabled &&!hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
else if ( commonVr->currentFlashlightPosition == FLASHLIGHT_HAND ) //&& !spectating && weaponEnabled &&!hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
flashlightHand = 2;
|
|
}
|
|
else if( hands[hand].floatingWeapon() )
|
|
{
|
|
flashlightHand = 0;
|
|
}
|
|
else if( hands[ hand ].holdingWeapon() || hands[ hand ].floatingWeapon() )
|
|
{
|
|
flashlightHand = 3;
|
|
}
|
|
|
|
if ( flashlightHand <= 1 && vr_useHandPoses.GetBool() )
|
|
{
|
|
int fingerPose;
|
|
fingerPose = vr_weaponHand.GetInteger() != 0 ? commonVr->fingerPose[HAND_RIGHT] : commonVr->fingerPose[HAND_LEFT];
|
|
fingerPose = fingerPose << 4;
|
|
//common->Printf( "Flashlight hand finger pose = %d , %d\n ", flashlightHand, fingerPose );
|
|
flashlightHand += fingerPose;
|
|
}
|
|
idThread::ReturnInt( flashlightHand );
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetPreviousWeapon
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetPreviousWeapon( void ) {
|
|
const char *weapon;
|
|
|
|
if( hands[ vr_weaponHand.GetInteger() ].previousWeapon >= 0 )
|
|
{
|
|
int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : hands[ vr_weaponHand.GetInteger() ].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 ) {
|
|
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "Before Event_SelectWeapon(\"%s\"):\n", weaponName );
|
|
hands[HAND_LEFT].debugPrint();
|
|
hands[HAND_RIGHT].debugPrint();
|
|
}
|
|
int i;
|
|
int weaponNum;
|
|
|
|
if( gameLocal.isClient )
|
|
{
|
|
gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
|
|
return;
|
|
}
|
|
|
|
// Carl: This function is called by item_pda::Lower() with the value returned from Event_GetPreviousWeapon,
|
|
// in an attempt to put away the PDA and return both hands to what they were holding before.
|
|
// That won't work when dual wielding, so check if we are in the PDA (before we change weapon) and put it away properly.
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].holdingPDA() )
|
|
{
|
|
//GB Good as place as any to try and get the flashlight back
|
|
if(h != vr_weaponHand.GetInteger() && flashlightPreviouslyInHand)
|
|
{
|
|
flashlightPreviouslyInHand = false;
|
|
commonVr->currentFlashlightMode = FLASHLIGHT_HAND;
|
|
}
|
|
hands[h].idealWeapon = hands[h].previousWeapon;
|
|
//if ( hands[1 - h].previousWeapon >= 0 && hands[1 - h].previousWeapon != weapon_fists )
|
|
// UpdateHudWeapon( 1 - h );
|
|
//else
|
|
UpdateHudWeapon( h );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Carl: We're probably being called by weapon_bloodstone_passive::Fire() to switch back to the previous weapon
|
|
/*
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[h].currentWeapon == weapon_bloodstone || hands[h].idealWeapon == weapon_bloodstone )
|
|
{
|
|
hands[h].idealWeapon = hands[h].previousWeapon;
|
|
UpdateHudWeapon( h );
|
|
return;
|
|
}
|
|
}*/
|
|
|
|
if( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
hands[ vr_weaponHand.GetInteger() ].idealWeapon = weapon_fists;
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[ h ].weapon->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;
|
|
// Carl: check for dual wielding
|
|
if( hands[ 1 - vr_weaponHand.GetInteger() ].idealWeapon != weaponNum )
|
|
{
|
|
hands[ vr_weaponHand.GetInteger() ].idealWeapon = weaponNum;
|
|
UpdateHudWeapon( vr_weaponHand.GetInteger() );
|
|
}
|
|
else
|
|
{
|
|
UpdateHudWeapon( 1 - vr_weaponHand.GetInteger() );
|
|
}
|
|
if( vr_debugHands.GetBool() )
|
|
{
|
|
common->Printf( "After Event_SelectWeapon(\"%s\") (not BFG or artifact):\n", weaponName );
|
|
hands[HAND_LEFT].debugPrint();
|
|
hands[HAND_RIGHT].debugPrint();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetWeaponEntity
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetWeaponEntity( void ) {
|
|
int h = risingWeaponHand;
|
|
if ( h < 0 )
|
|
h = vr_weaponHand.GetInteger();
|
|
if( hands[ 1 - h ].weapon->IdentifyWeapon() == WEAPON_ARTIFACT )
|
|
h = 1 - h;
|
|
idThread::ReturnEntity( hands[h].weapon );
|
|
}
|
|
|
|
// Koz begin
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetWeaponHand
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetWeaponHand()
|
|
{
|
|
//returns 0 for right hand 1 for left
|
|
if ( vr_weaponHand.GetInteger() == 1 )
|
|
{
|
|
|
|
idThread::ReturnInt( 1 );
|
|
}
|
|
else
|
|
{
|
|
idThread::ReturnInt( 0 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_GetWeaponHandState
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetWeaponHandState()
|
|
{
|
|
|
|
// weapon hand
|
|
// not for animations like firing, this is to change hand pose if no weapon is present or if using guis interactively
|
|
// 0 = no weapon
|
|
// 1 = normal (weapon in hand)
|
|
// 2 = pointy gui finger
|
|
|
|
int handState = 0;
|
|
int fingerPose = 0;
|
|
int hand = vr_weaponHand.GetInteger();
|
|
if ( commonVr->handInGui || commonVr->PDAforcetoggle || hands[ 1 - hand ].currentWeapon == weapon_pda )
|
|
{
|
|
handState = 2 ;
|
|
}
|
|
else if ( !spectating && weaponEnabled &&!hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
|
|
handState = 1 ;
|
|
}
|
|
|
|
|
|
if ( handState != 1 && vr_useHandPoses.GetBool() && !commonVr->PDAforcetoggle && !commonVr->PDAforced )
|
|
{
|
|
fingerPose = vr_weaponHand.GetInteger() == 0 ? commonVr->fingerPose[HAND_RIGHT] : commonVr->fingerPose[HAND_LEFT];
|
|
fingerPose = fingerPose << 4;
|
|
|
|
handState += fingerPose;
|
|
//common->Printf( "Weapon hand finger pose = %d, %d\n", handState, fingerPose );
|
|
}
|
|
|
|
idThread::ReturnInt( handState );
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_OpenPDA
|
|
==================
|
|
*/
|
|
void idPlayer::Event_OpenPDA( void ) {
|
|
if ( !gameLocal.isMultiplayer ) {
|
|
// Koz debug common->Printf( "idPlayer::Event_OpenPDA() calling TogglePDA\n" );
|
|
TogglePDA( 1 - vr_weaponHand.GetInteger() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_InPDA
|
|
==================
|
|
*/
|
|
void idPlayer::Event_InPDA( void ) {
|
|
idThread::ReturnInt( objectiveSystemOpen );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::TeleportDeath
|
|
==================
|
|
*/
|
|
void idPlayer::TeleportDeath( int killer ) {
|
|
teleportKiller = killer;
|
|
}
|
|
|
|
/* Carl: TouchTriggers() at every point in a pathfinding walk from the player's position to target, then teleport to target.
|
|
It does so even if there is no path, or the current position and/or target aren't valid.
|
|
====================
|
|
idPlayer::TeleportPath
|
|
====================
|
|
*/
|
|
void idPlayer::TeleportPath( const idVec3& target )
|
|
{
|
|
aasPath_t path;
|
|
int originAreaNum, toAreaNum;
|
|
idVec3 origin = physicsObj.GetOrigin();
|
|
idVec3 trueOrigin = physicsObj.GetOrigin();
|
|
idVec3 toPoint = target;
|
|
idVec3 lastPos = origin;
|
|
bool blocked = false;
|
|
// Find path start and end areas and points
|
|
originAreaNum = PointReachableAreaNum( origin );
|
|
if ( aas )
|
|
aas->PushPointIntoAreaNum( originAreaNum, origin );
|
|
toAreaNum = PointReachableAreaNum( toPoint );
|
|
if ( aas )
|
|
aas->PushPointIntoAreaNum( toAreaNum, toPoint );
|
|
// if there's no path, just go in a straight line (or should we just teleport straight there?)
|
|
if ( !aas || !originAreaNum || !toAreaNum || !aas->WalkPathToGoal( path, originAreaNum, origin, toAreaNum, toPoint, travelFlags ) )
|
|
{
|
|
blocked = !TeleportPathSegment( physicsObj.GetOrigin(), target, lastPos );
|
|
}
|
|
else
|
|
{
|
|
// move from actual position to start of path
|
|
blocked = !TeleportPathSegment( physicsObj.GetOrigin(), origin, lastPos );
|
|
idVec3 currentPos = origin;
|
|
int currentArea = originAreaNum;
|
|
// Move along path
|
|
while ( !blocked && currentArea && currentArea != toAreaNum )
|
|
{
|
|
if ( !TeleportPathSegment( currentPos, path.moveGoal, lastPos ) )
|
|
{
|
|
blocked = true;
|
|
break;
|
|
}
|
|
currentPos = path.moveGoal;
|
|
currentArea = path.moveAreaNum;
|
|
// Find next path segment. Sometimes it tells us to go to the current location and gets stuck in a loop, so check for that.
|
|
// TODO: Work out why it gets stuck in a loop, and fix it. Currently we just go in a straight line from stuck point to destination.
|
|
if ( !aas->WalkPathToGoal( path, currentArea, currentPos, toAreaNum, toPoint, travelFlags ) || ( path.moveAreaNum == currentArea && path.moveGoal == currentPos ) )
|
|
{
|
|
path.moveGoal = toPoint;
|
|
path.moveAreaNum = toAreaNum;
|
|
}
|
|
}
|
|
// Is this needed? Doesn't hurt.
|
|
blocked = blocked || !TeleportPathSegment( currentPos, toPoint, lastPos );
|
|
// move from end of path to actual target
|
|
blocked = blocked || !TeleportPathSegment( toPoint, target, lastPos );
|
|
}
|
|
|
|
if ( !blocked )
|
|
{
|
|
lastPos = target;
|
|
// Check we didn't teleport inside a door that's not open.
|
|
// It's OK to teleport THROUGH a closed but unlocked door, but we can't end up inside it.
|
|
int i, numClipModels;
|
|
idClipModel* cm;
|
|
idClipModel* clipModels[MAX_GENTITIES];
|
|
idEntity* ent;
|
|
trace_t trace;
|
|
|
|
memset( &trace, 0, sizeof(trace) );
|
|
trace.endpos = target;
|
|
trace.endAxis = GetPhysics()->GetAxis();
|
|
|
|
numClipModels = gameLocal.clip.ClipModelsTouchingBounds( GetPhysics()->GetAbsBounds(), CONTENTS_SOLID, clipModels, MAX_GENTITIES );
|
|
|
|
for ( i = 0; i < numClipModels; i++ )
|
|
{
|
|
cm = clipModels[i];
|
|
|
|
// don't touch it if we're the owner
|
|
if (cm->GetOwner() == this)
|
|
continue;
|
|
|
|
ent = cm->GetEntity();
|
|
|
|
if ( !blocked && ent->IsType(idDoor::Type) )
|
|
{
|
|
idDoor *door = (idDoor *)ent;
|
|
// A door that is in the process of opening falsely registers as open.
|
|
// But we can rely on the fact that we're touching it, to know it's still partly closed.
|
|
//if ( !door->IsOpen() )
|
|
{
|
|
idVec3 away = door->GetPhysics()->GetOrigin() - target;
|
|
away.z = 0;
|
|
float dist = away.Length();
|
|
if (dist < 50.0f)
|
|
{
|
|
away /= dist;
|
|
lastPos = target + (away * (dist - 50));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
// Actually teleport
|
|
|
|
if ( vr_teleportMode.GetInteger() == 0 )
|
|
{
|
|
Teleport( lastPos, viewAngles, NULL );
|
|
}
|
|
else
|
|
{
|
|
extern idCVar timescale;
|
|
warpMove = true;
|
|
noclip = true;
|
|
warpDest = lastPos;
|
|
//warpDest.z += 1;
|
|
warpVel = ( warpDest - trueOrigin ) / 0.075f; // 75 ms
|
|
//warpVel[2] = warpVel[2] + 50; // add a small fixed upwards velocity to handle noclip problem
|
|
warpTime = gameLocal.time + 75;
|
|
timescale.SetFloat( 0.5f );
|
|
//playerView.EnableBFGVision(true);
|
|
}
|
|
}
|
|
|
|
/* Carl: TouchTriggers() at every point along straight line from start to end
|
|
====================
|
|
idPlayer::TeleportPathSegment
|
|
====================
|
|
*/
|
|
bool idPlayer::TeleportPathSegment( const idVec3& start, const idVec3& end, idVec3& lastPos )
|
|
{
|
|
idVec3 total = end - start;
|
|
float length = total.Length();
|
|
if ( length >= 0.1f )
|
|
{
|
|
const float stepSize = 8.0f;
|
|
int steps = (int)(length / stepSize);
|
|
if (steps <= 0) steps = 1;
|
|
idVec3 step = total / steps;
|
|
idVec3 pos = start;
|
|
for (int i = 0; i < steps; i++)
|
|
{
|
|
bool blocked = false;
|
|
physicsObj.SetOrigin(pos);
|
|
// Like TouchTriggers() but also checks for locked doors
|
|
{
|
|
int i, numClipModels;
|
|
idClipModel* cm;
|
|
idClipModel* clipModels[MAX_GENTITIES];
|
|
idEntity* ent;
|
|
trace_t trace;
|
|
|
|
memset(&trace, 0, sizeof(trace));
|
|
trace.endpos = pos;
|
|
trace.endAxis = GetPhysics()->GetAxis();
|
|
|
|
numClipModels = gameLocal.clip.ClipModelsTouchingBounds(GetPhysics()->GetAbsBounds(), CONTENTS_TRIGGER | CONTENTS_SOLID, clipModels, MAX_GENTITIES);
|
|
|
|
for (i = 0; i < numClipModels; i++)
|
|
{
|
|
cm = clipModels[i];
|
|
|
|
// don't touch it if we're the owner
|
|
if (cm->GetOwner() == this)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ent = cm->GetEntity();
|
|
|
|
if (!blocked && ent->IsType(idDoor::Type))
|
|
{
|
|
idDoor *door = (idDoor *)ent;
|
|
if (door->IsLocked() || ( !vr_teleportThroughDoors.GetBool() && (cm->GetContents() & CONTENTS_SOLID) ))
|
|
{
|
|
// check if we're moving toward the door
|
|
idVec3 away = door->GetPhysics()->GetOrigin() - pos;
|
|
away.z = 0;
|
|
float dist = away.Length();
|
|
if (dist < 60.0f)
|
|
{
|
|
away /= dist;
|
|
idVec3 my_dir = step;
|
|
my_dir.Normalize();
|
|
float angle = idMath::ACos(away * my_dir);
|
|
if (angle < DEG2RAD(45) || (angle < DEG2RAD(90) && dist < 20))
|
|
blocked = true;
|
|
if (blocked && door->IsLocked())
|
|
{
|
|
// Trigger the door to make the locked sound, if we're not close enough to happen naturally
|
|
if (dist > 30)
|
|
{
|
|
physicsObj.SetOrigin(pos + (away * (dist - 30)));
|
|
TouchTriggers();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ent->RespondsTo(EV_Touch) && !ent->HasSignal(SIG_TOUCH))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!GetPhysics()->ClipContents(cm))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//SetTimeState ts(ent->timeGroup);
|
|
|
|
trace.c.contents = cm->GetContents();
|
|
trace.c.entityNum = cm->GetEntity()->entityNumber;
|
|
trace.c.id = cm->GetId();
|
|
|
|
ent->Signal(SIG_TOUCH);
|
|
ent->ProcessEvent(&EV_Touch, this, &trace);
|
|
}
|
|
}
|
|
|
|
if (blocked)
|
|
return false;
|
|
lastPos = pos;
|
|
pos += step;
|
|
}
|
|
// we don't call TouchTriggers after the final step because it's either
|
|
// the start of the next path segment, or the teleport destination
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_ExitTeleporter
|
|
==================
|
|
*/
|
|
void idPlayer::Event_ExitTeleporter( void ) {
|
|
idEntity *exitEnt;
|
|
float pushVel;
|
|
|
|
// verify and setup
|
|
exitEnt = teleportEntity;
|
|
if ( !exitEnt ) {
|
|
common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
|
|
return;
|
|
}
|
|
|
|
pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
|
|
|
|
if ( gameLocal.isServer ) {
|
|
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::Event_ForceOrigin
|
|
==================
|
|
*/
|
|
void idPlayer::Event_ForceOrigin( idVec3& origin, idAngles& angles )
|
|
{
|
|
SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
|
|
//SetViewAngles( angles );
|
|
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::ClientPredictionThink
|
|
================
|
|
*/
|
|
void idPlayer::ClientPredictionThink( void ) {
|
|
|
|
UpdateSkinSetup();
|
|
|
|
renderEntity_t *headRenderEnt;
|
|
|
|
oldFlags = usercmd.flags;
|
|
oldButtons = usercmd.buttons;
|
|
|
|
usercmd = gameLocal.usercmds[ entityNumber ];
|
|
|
|
if ( entityNumber != gameLocal.localClientNum ) {
|
|
// ignore attack button of other clients. that's no good for predictions
|
|
usercmd.buttons &= ~BUTTON_ATTACK;
|
|
}
|
|
|
|
buttonMask &= usercmd.buttons;
|
|
usercmd.buttons &= ~buttonMask;
|
|
|
|
if ( objectiveSystemOpen ) {
|
|
usercmd.forwardmove = 0;
|
|
usercmd.rightmove = 0;
|
|
usercmd.upmove = 0;
|
|
}
|
|
|
|
// clear the ik before we do anything else so the skeleton doesn't get updated twice
|
|
walkIK.ClearJointMods();
|
|
|
|
// Koz
|
|
armIK.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();
|
|
|
|
// update the smoothed view angles
|
|
if ( gameLocal.framenum >= smoothedFrame && entityNumber != gameLocal.localClientNum ) {
|
|
idAngles anglesDiff = viewAngles - smoothedAngles;
|
|
anglesDiff.Normalize180();
|
|
if ( idMath::Fabs( anglesDiff.yaw ) < 90.0f && idMath::Fabs( anglesDiff.pitch ) < 90.0f ) {
|
|
// smoothen by pushing back to the previous angles
|
|
viewAngles -= gameLocal.clientSmoothing * anglesDiff;
|
|
viewAngles.Normalize180();
|
|
}
|
|
smoothedAngles = viewAngles;
|
|
}
|
|
smoothedOriginUpdated = false;
|
|
|
|
if ( !af.IsActive() ) {
|
|
AdjustBodyAngles();
|
|
}
|
|
|
|
if ( !isLagged ) {
|
|
// 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 && hands[ vr_weaponHand.GetInteger() ].weapon && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
|
|
UpdateWeapon();
|
|
}
|
|
|
|
UpdateNeckPose();
|
|
|
|
UpdateFlashlight();
|
|
|
|
UpdateHud();
|
|
|
|
if ( gameLocal.isNewFrame ) {
|
|
UpdatePowerUps();
|
|
}
|
|
|
|
UpdateDeathSkin( false );
|
|
|
|
if ( head ) {
|
|
headRenderEnt = head->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 ) {
|
|
// Koz begin
|
|
if ( game->isVR )
|
|
{
|
|
headRenderEnt->suppressShadowInViewID = 0; //Carl:Draw the head's shadow when showing the body
|
|
} else {
|
|
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 ) {
|
|
if ( armIK.IsInitialized() ) armIK.Evaluate();
|
|
UpdateAnimation();
|
|
}
|
|
|
|
if ( gameLocal.isMultiplayer ) {
|
|
DrawPlayerIcons();
|
|
}
|
|
|
|
Present();
|
|
|
|
UpdateDamageEffects();
|
|
|
|
LinkCombat();
|
|
|
|
UpdateTeleportAim();
|
|
|
|
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::GetQuickSlot
|
|
===============
|
|
*/
|
|
int idPlayer::GetQuickSlot( int index )
|
|
{
|
|
|
|
if( index >= NUM_QUICK_SLOTS || index < 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return quickSlot[ index ];
|
|
}
|
|
|
|
/*
|
|
================
|
|
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 );
|
|
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( hands[vr_weaponHand.GetInteger()].idealWeapon, idMath::BitsForInteger( MAX_WEAPONS ) );
|
|
msg.WriteBits( inventory.weapons, MAX_WEAPONS );
|
|
msg.WriteBits( hands[vr_weaponHand.GetInteger()].weapon.GetSpawnId(), 32 ); // Carl dont change msg format
|
|
msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
|
|
msg.WriteBits( lastHitToggle, 1 );
|
|
msg.WriteBits( hands[ vr_weaponHand.GetInteger() ].weaponGone, 1 );
|
|
msg.WriteBits( isLagged, 1 );
|
|
msg.WriteBits( isChatting, 1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
int i, oldHealth, newIdealWeapon, weaponSpawnId;
|
|
int flashlightSpawnId;
|
|
bool newHitToggle, stateHitch;
|
|
|
|
if ( snapshotSequence - lastSnapshotSequence > 1 ) {
|
|
stateHitch = true;
|
|
} else {
|
|
stateHitch = false;
|
|
}
|
|
lastSnapshotSequence = snapshotSequence;
|
|
|
|
oldHealth = health;
|
|
|
|
physicsObj.ReadFromSnapshot( msg );
|
|
ReadBindFromSnapshot( msg );
|
|
deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
|
|
deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
|
|
deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
|
|
health = msg.ReadShort();
|
|
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( hands[vr_weaponHand.GetInteger()].weapon.SetSpawnId( weaponSpawnId ) )
|
|
{
|
|
if( hands[ vr_weaponHand.GetInteger() ].weapon )
|
|
{
|
|
// maintain ownership locally
|
|
hands[ vr_weaponHand.GetInteger() ].weapon->SetOwner( this, vr_weaponHand.GetInteger() );
|
|
}
|
|
hands[ vr_weaponHand.GetInteger() ].currentWeapon = -1;
|
|
}
|
|
|
|
if( flashlight.SetSpawnId( flashlightSpawnId ) )
|
|
{
|
|
if( flashlight )
|
|
{
|
|
flashlight->SetFlashlightOwner( this );
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
ClearPowerUps();
|
|
SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
|
|
SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
|
|
SetWaitState( "" );
|
|
animator.ClearAllJoints();
|
|
if ( entityNumber == gameLocal.localClientNum ) {
|
|
playerView.Fade( colorBlack, 12000 );
|
|
}
|
|
StartRagdoll();
|
|
physicsObj.SetMovementType( PM_DEAD );
|
|
if ( !stateHitch ) {
|
|
StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
|
|
}
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon )
|
|
hands[ h ].weapon->OwnerDied();
|
|
}
|
|
if( flashlight )
|
|
{
|
|
FlashlightOff();
|
|
flashlight->OwnerDied();
|
|
}
|
|
|
|
if( IsLocallyControlled() )
|
|
{
|
|
ControllerShakeFromDamage( oldHealth - health );
|
|
}
|
|
|
|
/*{
|
|
common->Vibrate(250, 0, 1.0);
|
|
common->Vibrate(250, 1, 1.0);
|
|
}*/
|
|
} else if ( oldHealth <= 0 && health > 0 ) {
|
|
// respawn
|
|
Init();
|
|
StopRagdoll();
|
|
SetPhysics( &physicsObj );
|
|
physicsObj.EnableClip();
|
|
SetCombatContents( true );
|
|
} 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( IsLocallyControlled() )
|
|
{
|
|
ControllerShakeFromDamage( oldHealth - health );
|
|
}
|
|
/*{
|
|
common->Vibrate(250, 0, 0.6);
|
|
common->Vibrate(250, 1, 0.6);
|
|
}*/
|
|
}
|
|
} else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !stateHitch ) {
|
|
// just pulse, for any health raise
|
|
healthPulse = true;
|
|
}
|
|
|
|
// If the player is alive, restore proper physics object
|
|
if ( health > 0 && IsActiveAF() ) {
|
|
StopRagdoll();
|
|
SetPhysics( &physicsObj );
|
|
physicsObj.EnableClip();
|
|
SetCombatContents( true );
|
|
}
|
|
|
|
const int oldIdealWeapon = hands[ vr_weaponHand.GetInteger() ].idealWeapon;
|
|
//GB Not using Predicted Values - seee if this causes any problems
|
|
//hands[ vr_weaponHand.GetInteger() ].idealWeapon.UpdateFromSnapshot( newIdealWeapon, GetEntityNumber() );
|
|
|
|
if( oldIdealWeapon != hands[ vr_weaponHand.GetInteger() ].idealWeapon )
|
|
{
|
|
if( snapshotStale )
|
|
{
|
|
weaponCatchup = true;
|
|
}
|
|
UpdateHudWeapon( vr_weaponHand.GetInteger() );
|
|
}
|
|
|
|
if ( lastHitToggle != newHitToggle ) {
|
|
SetLastHitTime( gameLocal.realClientTime );
|
|
}
|
|
|
|
if ( msg.HasChanged() ) {
|
|
UpdateVisuals();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::WritePlayerStateToSnapshot
|
|
================
|
|
*/
|
|
void idPlayer::WritePlayerStateToSnapshot( idBitMsgDelta &msg ) const {
|
|
int i;
|
|
|
|
msg.WriteByte( bobCycle );
|
|
msg.WriteInt( stepUpTime );
|
|
msg.WriteFloat( stepUpDelta );
|
|
msg.WriteShort( inventory.weapons );
|
|
msg.WriteByte( inventory.armor );
|
|
|
|
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
|
msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
|
|
}
|
|
for( i = 0; i < MAX_WEAPONS; i++ ) {
|
|
msg.WriteBits( inventory.clip[i], ASYNC_PLAYER_INV_CLIP_BITS );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::ReadPlayerStateFromSnapshot
|
|
================
|
|
*/
|
|
void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
|
|
int i, ammo;
|
|
|
|
bobCycle = msg.ReadByte();
|
|
stepUpTime = msg.ReadInt();
|
|
stepUpDelta = msg.ReadFloat();
|
|
inventory.weapons = msg.ReadShort();
|
|
inventory.armor = msg.ReadByte();
|
|
|
|
for( i = 0; i < AMMO_NUMTYPES; i++ ) {
|
|
ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
|
|
if ( gameLocal.time >= inventory.ammoPredictTime ) {
|
|
inventory.ammo[ i ] = ammo;
|
|
}
|
|
}
|
|
for( i = 0; i < MAX_WEAPONS; i++ ) {
|
|
inventory.clip[i] = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Koz idPlayer::RecreateCopyJoints()
|
|
After restoring from a savegame with different player models, the copyjoints for the head are wrong.
|
|
This will recreate a working copyJoint list.
|
|
==============
|
|
*/
|
|
void idPlayer::RecreateCopyJoints()
|
|
{
|
|
idEntity* headEnt = head;
|
|
idAnimator* headAnimator;
|
|
const idKeyValue* kv;
|
|
idStr jointName;
|
|
copyJoints_t copyJoint;
|
|
|
|
copyJoints.Clear();
|
|
if ( headEnt )
|
|
{
|
|
headAnimator = headEnt->GetAnimator();
|
|
}
|
|
else
|
|
{
|
|
headAnimator = &animator;
|
|
}
|
|
|
|
if ( headEnt )
|
|
{
|
|
// set up the list of joints to copy to the head
|
|
for ( kv = spawnArgs.MatchPrefix( "copy_joint", NULL ); kv != NULL; kv = spawnArgs.MatchPrefix( "copy_joint", kv ) )
|
|
{
|
|
if ( kv->GetValue() == "" )
|
|
{
|
|
// probably clearing out inherited key, so skip it
|
|
continue;
|
|
}
|
|
|
|
jointName = kv->GetKey();
|
|
if ( jointName.StripLeadingOnce( "copy_joint_world " ) )
|
|
{
|
|
copyJoint.mod = JOINTMOD_WORLD_OVERRIDE;
|
|
}
|
|
else
|
|
{
|
|
jointName.StripLeadingOnce( "copy_joint " );
|
|
copyJoint.mod = JOINTMOD_LOCAL_OVERRIDE;
|
|
}
|
|
|
|
copyJoint.from = animator.GetJointHandle( jointName );
|
|
if ( copyJoint.from == INVALID_JOINT )
|
|
{
|
|
gameLocal.Warning( "Unknown copy_joint '%s' on entity %s", jointName.c_str(), name.c_str() );
|
|
continue;
|
|
}
|
|
|
|
jointName = kv->GetValue();
|
|
copyJoint.to = headAnimator->GetJointHandle( jointName );
|
|
if ( copyJoint.to == INVALID_JOINT )
|
|
{
|
|
gameLocal.Warning( "Unknown copy_joint '%s' on head of entity %s", jointName.c_str(), name.c_str() );
|
|
continue;
|
|
}
|
|
|
|
copyJoints.Append( copyJoint );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
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 ) {
|
|
int powerup;
|
|
bool start;
|
|
|
|
switch ( event ) {
|
|
case EVENT_EXIT_TELEPORTER:
|
|
Event_ExitTeleporter();
|
|
return true;
|
|
case EVENT_ABORT_TELEPORTER:
|
|
SetPrivateCameraView( NULL );
|
|
return true;
|
|
case EVENT_POWERUP: {
|
|
powerup = msg.ReadShort();
|
|
start = msg.ReadBits( 1 ) != 0;
|
|
if ( start ) {
|
|
GivePowerUp( powerup, 0 );
|
|
} else {
|
|
ClearPowerup( powerup );
|
|
}
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return idActor::ClientReceiveEvent( event, time, msg );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::Hide
|
|
================
|
|
*/
|
|
void idPlayer::Hide( void ) {
|
|
idWeapon *weap;
|
|
|
|
idActor::Hide();
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
weap = hands[ h ].weapon;
|
|
if( weap )
|
|
weap->HideWorldModel();
|
|
}
|
|
idWeapon* flashlight_weapon = flashlight;
|
|
if( flashlight_weapon )
|
|
{
|
|
flashlight_weapon->HideWorldModel();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idPlayer::Show
|
|
================
|
|
*/
|
|
void idPlayer::Show( void ) {
|
|
idWeapon *weap;
|
|
|
|
idActor::Show();
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
weap = hands[ h ].weapon;
|
|
if( weap )
|
|
weap->ShowWorldModel();
|
|
}
|
|
idWeapon* flashlight_weapon = flashlight;
|
|
if( flashlight_weapon )
|
|
{
|
|
flashlight_weapon->ShowWorldModel();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::StartAudioLog
|
|
===============
|
|
*/
|
|
void idPlayer::StartAudioLog( void ) {
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "audioLogUp" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::StopAudioLog
|
|
===============
|
|
*/
|
|
void idPlayer::StopAudioLog( void ) {
|
|
if ( hud ) {
|
|
hud->HandleNamedEvent( "audioLogDown" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::ShowTip
|
|
===============
|
|
*/
|
|
void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
|
|
if ( tipUp ) {
|
|
return;
|
|
}
|
|
hud->SetStateString( "tip", tip );
|
|
hud->SetStateString( "tiptitle", title );
|
|
hud->HandleNamedEvent( "tipWindowUp" );
|
|
if ( autoHide ) {
|
|
PostEventSec( &EV_Player_HideTip, 5.0f );
|
|
}
|
|
tipUp = true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::HideTip
|
|
===============
|
|
*/
|
|
void idPlayer::HideTip( void ) {
|
|
hud->HandleNamedEvent( "tipWindowDown" );
|
|
tipUp = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Event_HideTip
|
|
===============
|
|
*/
|
|
void idPlayer::Event_HideTip( void ) {
|
|
HideTip();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::ShowObjective
|
|
===============
|
|
*/
|
|
void idPlayer::ShowObjective( const char *obj ) {
|
|
hud->HandleNamedEvent( obj );
|
|
objectiveUp = true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::HideObjective
|
|
===============
|
|
*/
|
|
void idPlayer::HideObjective( void ) {
|
|
hud->HandleNamedEvent( "closeObjective" );
|
|
objectiveUp = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::Event_StopAudioLog
|
|
===============
|
|
*/
|
|
void idPlayer::Event_StopAudioLog( void ) {
|
|
StopAudioLog();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::SetSpectateOrigin
|
|
===============
|
|
*/
|
|
void idPlayer::SetSpectateOrigin( void ) {
|
|
idVec3 neworig;
|
|
|
|
neworig = GetPhysics()->GetOrigin();
|
|
neworig[ 2 ] += EyeHeight();
|
|
neworig[ 2 ] += 25;
|
|
SetOrigin( neworig );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetWeaponHandPose()
|
|
Updates the pose of the player model weapon hand
|
|
======
|
|
*/
|
|
void idPlayer::SetWeaponHandPose()
|
|
{
|
|
const function_t* func;
|
|
func = scriptObject.GetFunction( "SetWeaponHandPose" );
|
|
if ( func )
|
|
{
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
//common->Printf( "Calling SetWeaponHandPose\n" );g
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
}
|
|
else
|
|
{
|
|
common->Warning( "Can't find function 'SetWeaponHandPose' in object '%s'", scriptObject.GetTypeName() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetupHolsterSlot
|
|
|
|
stashed: -1 = switch weapons, 1 = empty holster of stashed weapon, 0 = stash current weapon in holster but don't switch
|
|
==============
|
|
*/
|
|
void idPlayer::SetupHolsterSlot( int hand, int stashed )
|
|
{
|
|
// if there's nothing to stash because we were already using fists or PDA
|
|
if ( stashed == 0 && (hands[hand].currentWeapon == weapon_pda || hands[ hand ].currentWeapon == weapon_fists) )
|
|
return;
|
|
// if we were using fists before activating pda, we didn't stash anything in our holster, so don't unstash anything
|
|
if( stashed == 1 && hands[ hand ].previousWeapon == weapon_fists )
|
|
return;
|
|
// if we want to read or switch the current weapon but it's not ready
|
|
if( !hands[ hand ].weapon->IsReady() && stashed != 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const char * modelname;
|
|
idRenderModel* renderModel;
|
|
|
|
if ( stashed == 0 )
|
|
{
|
|
extraHolsteredWeapon = holsteredWeapon;
|
|
if ( holsterRenderEntity.hModel )
|
|
extraHolsteredWeaponModel = holsterRenderEntity.hModel->Name();
|
|
else
|
|
extraHolsteredWeaponModel = NULL;
|
|
}
|
|
|
|
FreeHolsterSlot();
|
|
if( vr_slotDisable.GetBool() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( stashed == 1 )
|
|
{
|
|
modelname = extraHolsteredWeaponModel;
|
|
extraHolsteredWeaponModel = NULL;
|
|
}
|
|
else
|
|
modelname = hands[hand].weapon->weaponDef->dict.GetString("model");
|
|
|
|
// can we holster?
|
|
if( !modelname ||
|
|
strcmp(modelname, "models/weapons/soulcube/w_soulcube.lwo") == 0 ||
|
|
strcmp(modelname, "_DEFAULT") == 0 ||
|
|
strcmp(modelname, "models/items/grenade_ammo/grenade.lwo") == 0 ||
|
|
strcmp(modelname, "models/items/pda/pda_world.lwo") == 0 ||
|
|
!(renderModel = renderModelManager->FindModel( modelname )) )
|
|
{
|
|
// can't holster, just unholster
|
|
if( holsteredWeapon != weapon_fists )
|
|
{
|
|
if ( stashed < 0 )
|
|
SelectWeapon(holsteredWeapon, false, true);
|
|
holsteredWeapon = weapon_fists;
|
|
memset(&holsterRenderEntity, 0, sizeof(holsterRenderEntity));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// we can holster! so unholster or change weapons
|
|
if (stashed < 0)
|
|
{
|
|
int prevWeapon = hands[hand].currentWeapon;
|
|
hands[hand].SelectWeapon(holsteredWeapon, false, true);
|
|
holsteredWeapon = prevWeapon;
|
|
}
|
|
else
|
|
{
|
|
if (stashed == 0) // stash current weapon, holstered weapon moves to invisible "extra" slot
|
|
{
|
|
holsteredWeapon = hands[hand].currentWeapon;
|
|
}
|
|
else // unstash holstered weapon, extra weapon moves back to holster
|
|
{
|
|
hands[ hand ].SelectWeapon(holsteredWeapon, true, true);
|
|
holsteredWeapon = extraHolsteredWeapon;
|
|
extraHolsteredWeapon = weapon_fists;
|
|
}
|
|
}
|
|
|
|
memset( &holsterRenderEntity, 0, sizeof( holsterRenderEntity ) );
|
|
|
|
holsterRenderEntity.hModel = renderModel;
|
|
if( holsterRenderEntity.hModel )
|
|
{
|
|
holsterRenderEntity.hModel->Reset();
|
|
holsterRenderEntity.bounds = holsterRenderEntity.hModel->Bounds( &holsterRenderEntity );
|
|
}
|
|
holsterRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
|
holsterRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
|
|
holsterRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
|
|
holsterRenderEntity.shaderParms[3] = 1.0f;
|
|
holsterRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
|
|
holsterRenderEntity.shaderParms[5] = 0.0f;
|
|
holsterRenderEntity.shaderParms[6] = 0.0f;
|
|
holsterRenderEntity.shaderParms[7] = 0.0f;
|
|
|
|
if( strcmp(modelname, "models/weapons/pistol/w_pistol.lwo") == 0 )
|
|
{
|
|
holsterAxis = idAngles(90, 0, 0).ToMat3() * 0.75f;
|
|
}
|
|
else if( strcmp(modelname, "models/weapons/shotgun/w_shotgun2.lwo") == 0 ||
|
|
strcmp(modelname, "models/weapons/bfg/bfg_world.lwo") == 0)
|
|
{
|
|
holsterAxis = idAngles(0, -90, -90).ToMat3();
|
|
}
|
|
else if (strcmp(modelname, "models/weapons/machinegun/w_machinegun.lwo") == 0)
|
|
{
|
|
holsterAxis = idAngles(0, 90, 90).ToMat3() * 0.75f;
|
|
}
|
|
else if (strcmp(modelname, "models/weapons/plasmagun/plasmagun_world.lwo") == 0)
|
|
{
|
|
holsterAxis = idAngles(0, 90, 90).ToMat3() * 0.75f;
|
|
}
|
|
else if (strcmp(modelname, "models/weapons/chainsaw/w_chainsaw.lwo") == 0)
|
|
{
|
|
holsterAxis = idAngles(0, 90, 90).ToMat3() * 0.9f;
|
|
}
|
|
else if (strcmp(modelname, "models/weapons/chaingun/w_chaingun.lwo") == 0)
|
|
{
|
|
holsterAxis = idAngles(0, 90, 90).ToMat3() * 0.9f;
|
|
}
|
|
else
|
|
{
|
|
holsterAxis = idAngles(0, 90, 90).ToMat3();
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::SetupPDASlot
|
|
==============
|
|
*/
|
|
void idPlayer::SetupPDASlot( bool holsterPDA )
|
|
{
|
|
const char * modelname;
|
|
idRenderModel* renderModel;
|
|
|
|
FreePDASlot();
|
|
|
|
if( vr_slotDisable.GetBool() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( holsterPDA )
|
|
{
|
|
// we will holster the PDA
|
|
modelname = "models/items/pda/pda_world.lwo";
|
|
pdaHolsterAxis = (pdaAngle1.ToMat3() * pdaAngle2.ToMat3() * pdaAngle3.ToMat3()) * 0.6f;
|
|
}
|
|
else
|
|
{
|
|
// we will holster the flashlight if carrying it
|
|
if ( commonVr->currentFlashlightMode == FLASHLIGHT_HAND && flashlight->IsLinked() && !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) )
|
|
{
|
|
flashlightPreviouslyInHand = true;
|
|
modelname = flashlight->weaponDef->dict.GetString("model");
|
|
pdaHolsterAxis = idAngles(0, 90, 90).ToMat3();
|
|
}
|
|
else modelname = "";
|
|
}
|
|
|
|
memset( &pdaRenderEntity, 0, sizeof( pdaRenderEntity ) );
|
|
|
|
// can we holster?
|
|
if ( !(renderModel = renderModelManager->FindModel(modelname)) )
|
|
{
|
|
// can't holster, just unholster
|
|
return;
|
|
}
|
|
|
|
pdaRenderEntity.hModel = renderModel;
|
|
if (pdaRenderEntity.hModel)
|
|
{
|
|
pdaRenderEntity.hModel->Reset();
|
|
pdaRenderEntity.bounds = pdaRenderEntity.hModel->Bounds( &pdaRenderEntity );
|
|
}
|
|
pdaRenderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
|
|
pdaRenderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f;
|
|
pdaRenderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
|
|
pdaRenderEntity.shaderParms[3] = 1.0f;
|
|
pdaRenderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
|
|
pdaRenderEntity.shaderParms[5] = 0.0f;
|
|
pdaRenderEntity.shaderParms[6] = 0.0f;
|
|
pdaRenderEntity.shaderParms[7] = 0.0f;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
idPlayer::RemoveWeapon
|
|
===============
|
|
*/
|
|
void idPlayer::RemoveWeapon( const char *weap ) {
|
|
if ( weap && *weap ) {
|
|
inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
========================
|
|
idPlayer::ResetControllerShake
|
|
========================
|
|
*/
|
|
void idPlayer::ResetControllerShake()
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
hands[h].ResetControllerShake();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::CanShowWeaponViewmodel
|
|
===============
|
|
*/
|
|
bool idPlayer::CanShowWeaponViewmodel( void ) const {
|
|
return showWeaponViewModel;
|
|
}
|
|
|
|
bool idPlayer::CheckTeleportPathSegment(const idVec3& start, const idVec3& end, idVec3& lastPos)
|
|
{
|
|
idVec3 total = end - start;
|
|
float length = total.Length();
|
|
if ( length >= 0.1f )
|
|
{
|
|
const float stepSize = 15.0f; // We have a radius of 16, so this should catch everything
|
|
int steps = (int)( length / stepSize );
|
|
if ( steps <= 0 ) steps = 1;
|
|
idVec3 step = total / steps;
|
|
idVec3 pos = start;
|
|
for ( int i = 0; i < steps; i++ )
|
|
{
|
|
physicsObj.SetOrigin( pos );
|
|
// Check for doors
|
|
{
|
|
int i, numClipModels;
|
|
idClipModel* cm;
|
|
idClipModel* clipModels[MAX_GENTITIES];
|
|
idEntity* ent;
|
|
trace_t trace;
|
|
|
|
memset( &trace, 0, sizeof(trace) );
|
|
trace.endpos = pos;
|
|
trace.endAxis = GetPhysics()->GetAxis();
|
|
|
|
numClipModels = gameLocal.clip.ClipModelsTouchingBounds( GetPhysics()->GetAbsBounds(), CONTENTS_SOLID, clipModels, MAX_GENTITIES );
|
|
|
|
for ( i = 0; i < numClipModels; i++ )
|
|
{
|
|
cm = clipModels[i];
|
|
|
|
// don't touch it if we're the owner
|
|
if (cm->GetOwner() == this)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ent = cm->GetEntity();
|
|
|
|
// check if it's a closed or locked door
|
|
if ( ent->IsType(idDoor::Type) )
|
|
{
|
|
idDoor *door = (idDoor *)ent;
|
|
if ( door->IsLocked() || ( !vr_teleportThroughDoors.GetBool() && ( cm->GetContents() & CONTENTS_SOLID ) ) )
|
|
{
|
|
// check if we're moving toward the door
|
|
idVec3 away = door->GetPhysics()->GetOrigin() - pos;
|
|
away.z = 0;
|
|
float dist = away.Length();
|
|
if ( dist < 60.0f )
|
|
{
|
|
away /= dist;
|
|
idVec3 my_dir = step;
|
|
my_dir.Normalize();
|
|
float angle = idMath::ACos(away * my_dir);
|
|
if ( angle < DEG2RAD( 45 ) || ( angle < DEG2RAD( 90 ) && dist < 20 ) )
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
// Check if it's a glass window. func_static with textures/glass/glass2
|
|
else if ( ent->IsType( idStaticEntity::Type ) )
|
|
{
|
|
renderEntity_t *rent = ent->GetRenderEntity();
|
|
if ( rent )
|
|
{
|
|
const idMaterial *mat = rent->customShader;
|
|
if ( !mat )
|
|
mat = rent->referenceShader;
|
|
if ( !mat && rent->hModel )
|
|
{
|
|
for ( int i = 0; i < rent->hModel->NumSurfaces(); i++ )
|
|
if ( rent->hModel->Surface(i)->shader )
|
|
{
|
|
mat = rent->hModel->Surface(i)->shader;
|
|
break;
|
|
}
|
|
}
|
|
if ( mat )
|
|
{
|
|
const char* name = mat->GetName();
|
|
// trying to teleport through glass: textures/glass/glass2 or textures/glass/glass1
|
|
if ( name && idStr::Cmpn( name, "textures/glass/glass", 20 ) == 0 )
|
|
return false;
|
|
//else if (name)
|
|
// common->Printf("teleporting through \"%s\"\n", name);
|
|
//else
|
|
// common->Printf("teleporting through NULL\n");
|
|
}
|
|
else if ( ent->name )
|
|
{
|
|
//common->Printf("teleporting through entity %s", ent->name);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lastPos = pos;
|
|
pos += step;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Carl: Check if we are trying to teleport through a locked or closed door
|
|
====================
|
|
idPlayer::CheckTeleportPath
|
|
====================
|
|
*/
|
|
bool idPlayer::CheckTeleportPath(const idVec3& target, int toAreaNum)
|
|
{
|
|
aasPath_t path;
|
|
int originAreaNum;
|
|
idVec3 origin = physicsObj.GetOrigin();
|
|
idVec3 trueOrigin = origin;
|
|
idVec3 toPoint = target;
|
|
idVec3 lastPos = origin;
|
|
bool blocked = false;
|
|
// Find path start and end areas and points
|
|
originAreaNum = PointReachableAreaNum( origin );
|
|
if ( aas )
|
|
aas->PushPointIntoAreaNum( originAreaNum, origin );
|
|
if ( !toAreaNum )
|
|
toAreaNum = PointReachableAreaNum( toPoint );
|
|
if ( aas )
|
|
aas->PushPointIntoAreaNum( toAreaNum, toPoint );
|
|
// if there's no path, just go in a straight line
|
|
if ( !aas || !originAreaNum || !toAreaNum || !aas->WalkPathToGoal(path, originAreaNum, origin, toAreaNum, toPoint, travelFlags ) )
|
|
{
|
|
if ( !CheckTeleportPathSegment( physicsObj.GetOrigin(), target, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// move from actual position to start of path
|
|
if ( !CheckTeleportPathSegment( trueOrigin, origin, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
idVec3 currentPos = origin;
|
|
int currentArea = originAreaNum;
|
|
|
|
int lastAreas[4], lastAreaIndex;
|
|
lastAreas[0] = lastAreas[1] = lastAreas[2] = lastAreas[3] = currentArea;
|
|
lastAreaIndex = 0;
|
|
|
|
|
|
// Move along path
|
|
while ( currentArea && currentArea != toAreaNum )
|
|
{
|
|
if ( !CheckTeleportPathSegment( currentPos, path.moveGoal, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
|
|
lastAreas[lastAreaIndex] = currentArea;
|
|
lastAreaIndex = ( lastAreaIndex + 1 ) & 3;
|
|
|
|
currentPos = path.moveGoal;
|
|
currentArea = path.moveAreaNum;
|
|
|
|
// Sometimes it tells us to go to the current location and gets stuck in a loop, so check for that.
|
|
// TODO: Work out why it gets stuck in a loop, and fix it. Currently we just go in a straight line from stuck point to destination.
|
|
if ( currentArea == lastAreas[0] || currentArea == lastAreas[1] ||
|
|
currentArea == lastAreas[2] || currentArea == lastAreas[3] )
|
|
{
|
|
common->Warning( "CheckTeleportPath: local routing minimum going from area %d to area %d", currentArea, toAreaNum );
|
|
if ( !CheckTeleportPathSegment( currentPos, toPoint, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
currentPos = toPoint;
|
|
currentArea = toAreaNum;
|
|
break;
|
|
}
|
|
|
|
// Find next path segment.
|
|
if ( !aas->WalkPathToGoal(path, currentArea, currentPos, toAreaNum, toPoint, travelFlags) )
|
|
{
|
|
path.moveGoal = toPoint;
|
|
path.moveAreaNum = toAreaNum;
|
|
currentPos = toPoint;
|
|
currentArea = toAreaNum;
|
|
}
|
|
}
|
|
// Is this needed? Doesn't hurt.
|
|
if ( !CheckTeleportPathSegment( currentPos, toPoint, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
// move from end of path to actual target
|
|
if ( !CheckTeleportPathSegment( toPoint, target, lastPos ) )
|
|
{
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
physicsObj.SetOrigin( trueOrigin );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
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::Event_GetIdealWeapon
|
|
==================
|
|
*/
|
|
void idPlayer::Event_GetIdealWeapon( void ) {
|
|
const char *weapon;
|
|
|
|
if( hands[vr_weaponHand.GetInteger()].idealWeapon >= 0 )
|
|
{
|
|
weapon = spawnArgs.GetString( va( "def_weapon%d", hands[ vr_weaponHand.GetInteger() ].idealWeapon ) );
|
|
idThread::ReturnString( weapon );
|
|
}
|
|
else
|
|
{
|
|
idThread::ReturnString( "" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::UpdatePlayerIcons
|
|
===============
|
|
*/
|
|
void idPlayer::UpdatePlayerIcons( void ) {
|
|
int time = networkSystem->ServerGetClientTimeSinceLastPacket( entityNumber );
|
|
if ( time > cvarSystem->GetCVarInteger( "net_clientMaxPrediction" ) ) {
|
|
isLagged = true;
|
|
} else {
|
|
isLagged = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
idPlayer::UpdatePlayerSkinsPoses()
|
|
Updates the skins of the weapon and flashlight to hide/show arms/watch and updates poses of player model hands.
|
|
======
|
|
*/
|
|
void idPlayer::UpdatePlayerSkinsPoses()
|
|
{
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( hands[ h ].weapon )
|
|
hands[ h ].weapon->UpdateSkin();
|
|
}
|
|
if ( flashlight )
|
|
{
|
|
flashlight->UpdateSkin();
|
|
}
|
|
SetFlashHandPose(); // Call set flashlight hand pose script function
|
|
SetWeaponHandPose();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::DrawPlayerIcons
|
|
===============
|
|
*/
|
|
void idPlayer::DrawPlayerIcons( void ) {
|
|
if ( !NeedsIcon() ) {
|
|
playerIcon.FreeIcon();
|
|
return;
|
|
}
|
|
playerIcon.Draw( this, headJoint );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::HidePlayerIcons
|
|
===============
|
|
*/
|
|
void idPlayer::HidePlayerIcons( void ) {
|
|
playerIcon.FreeIcon();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idPlayer::NeedsIcon
|
|
==============
|
|
*/
|
|
bool idPlayer::NeedsIcon( void ) {
|
|
// local clients don't render their own icons... they're only info for other clients
|
|
return entityNumber != gameLocal.localClientNum && ( isLagged || isChatting );
|
|
}
|