mirror of
https://github.com/DrBeef/Doom3Quest.git
synced 2025-02-24 21:01:16 +00:00
Bringing all the changes done (mostly by baggyg) in the OVRPassToVR branch bringing over the major FP related changes to master. Please refer to that branch for the individual changes that have all been brought over in this single commit. Co-Authored-By: Grant Bagwell <general@grantbagwell.co.uk>
4318 lines
123 KiB
C++
4318 lines
123 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 <idlib/containers/Sort.h>
|
|
#include "sys/platform.h"
|
|
#include "framework/DeclEntityDef.h"
|
|
#include "framework/DeclSkin.h"
|
|
#include "framework/Session.h"
|
|
|
|
#include "gamesys/SysCvar.h"
|
|
#include "ai/AI.h"
|
|
#include "Player.h"
|
|
#include "Trigger.h"
|
|
#include "SmokeParticles.h"
|
|
#include "WorldSpawn.h"
|
|
|
|
#include "Weapon.h"
|
|
#include "Vr.h"
|
|
|
|
extern bool IsDoom3DemoVersion(); // DG: hack to support the Demo version of Doom3
|
|
|
|
// Koz begin
|
|
idClipModel* PDAclipModel; // Koz fixme pda more crappy globals.
|
|
|
|
idCVar vr_guiH( "vr_guiH", "100", CVAR_INTEGER, "" );
|
|
idCVar vr_guiA( "vr_guiA", "100", CVAR_INTEGER, "" );
|
|
|
|
idCVar vr_throwPower( "vr_throwPower", "4.0", CVAR_FLOAT | CVAR_GAME | CVAR_ARCHIVE, "Throw power" );
|
|
// Koz end
|
|
|
|
/***********************************************************************
|
|
|
|
idWeapon
|
|
|
|
***********************************************************************/
|
|
|
|
//
|
|
// event defs
|
|
//
|
|
const idEventDef EV_Weapon_Clear( "<clear>" );
|
|
const idEventDef EV_Weapon_GetOwner( "getOwner", NULL, 'e' );
|
|
const idEventDef EV_Weapon_Next( "nextWeapon" );
|
|
const idEventDef EV_Weapon_State( "weaponState", "sd" );
|
|
const idEventDef EV_Weapon_UseAmmo( "useAmmo", "d" );
|
|
const idEventDef EV_Weapon_AddToClip( "addToClip", "d" );
|
|
const idEventDef EV_Weapon_AmmoInClip( "ammoInClip", NULL, 'f' );
|
|
const idEventDef EV_Weapon_AmmoAvailable( "ammoAvailable", NULL, 'f' );
|
|
const idEventDef EV_Weapon_TotalAmmoCount( "totalAmmoCount", NULL, 'f' );
|
|
const idEventDef EV_Weapon_ClipSize( "clipSize", NULL, 'f' );
|
|
const idEventDef EV_Weapon_WeaponOutOfAmmo( "weaponOutOfAmmo" );
|
|
const idEventDef EV_Weapon_WeaponReady( "weaponReady" );
|
|
const idEventDef EV_Weapon_WeaponReloading( "weaponReloading" );
|
|
const idEventDef EV_Weapon_WeaponHolstered( "weaponHolstered" );
|
|
const idEventDef EV_Weapon_WeaponRising( "weaponRising" );
|
|
const idEventDef EV_Weapon_WeaponLowering( "weaponLowering" );
|
|
const idEventDef EV_Weapon_Flashlight( "flashlight", "d" );
|
|
const idEventDef EV_Weapon_LaunchProjectiles( "launchProjectiles", "dffff" );
|
|
const idEventDef EV_Weapon_CreateProjectile( "createProjectile", NULL, 'e' );
|
|
const idEventDef EV_Weapon_EjectBrass( "ejectBrass" );
|
|
const idEventDef EV_Weapon_Melee( "melee", NULL, 'd' );
|
|
const idEventDef EV_Weapon_GetWorldModel( "getWorldModel", NULL, 'e' );
|
|
const idEventDef EV_Weapon_AllowDrop( "allowDrop", "d" );
|
|
const idEventDef EV_Weapon_AutoReload( "autoReload", NULL, 'f' );
|
|
const idEventDef EV_Weapon_NetReload( "netReload" );
|
|
const idEventDef EV_Weapon_IsInvisible( "isInvisible", NULL, 'f' );
|
|
const idEventDef EV_Weapon_NetEndReload( "netEndReload" );
|
|
// Koz
|
|
const idEventDef EV_Weapon_GetWeaponSkin( "getWeaponSkin", NULL, 's' );
|
|
const idEventDef EV_Weapon_IsMotionControlled( "isMotionControlled", NULL, 'd' );
|
|
|
|
//
|
|
// class def
|
|
//
|
|
CLASS_DECLARATION( idAnimatedEntity, idWeapon )
|
|
EVENT( EV_Weapon_Clear, idWeapon::Event_Clear )
|
|
EVENT( EV_Weapon_GetOwner, idWeapon::Event_GetOwner )
|
|
EVENT( EV_Weapon_State, idWeapon::Event_WeaponState )
|
|
EVENT( EV_Weapon_WeaponReady, idWeapon::Event_WeaponReady )
|
|
EVENT( EV_Weapon_WeaponOutOfAmmo, idWeapon::Event_WeaponOutOfAmmo )
|
|
EVENT( EV_Weapon_WeaponReloading, idWeapon::Event_WeaponReloading )
|
|
EVENT( EV_Weapon_WeaponHolstered, idWeapon::Event_WeaponHolstered )
|
|
EVENT( EV_Weapon_WeaponRising, idWeapon::Event_WeaponRising )
|
|
EVENT( EV_Weapon_WeaponLowering, idWeapon::Event_WeaponLowering )
|
|
EVENT( EV_Weapon_UseAmmo, idWeapon::Event_UseAmmo )
|
|
EVENT( EV_Weapon_AddToClip, idWeapon::Event_AddToClip )
|
|
EVENT( EV_Weapon_AmmoInClip, idWeapon::Event_AmmoInClip )
|
|
EVENT( EV_Weapon_AmmoAvailable, idWeapon::Event_AmmoAvailable )
|
|
EVENT( EV_Weapon_TotalAmmoCount, idWeapon::Event_TotalAmmoCount )
|
|
EVENT( EV_Weapon_ClipSize, idWeapon::Event_ClipSize )
|
|
EVENT( AI_PlayAnim, idWeapon::Event_PlayAnim )
|
|
EVENT( AI_PlayCycle, idWeapon::Event_PlayCycle )
|
|
EVENT( AI_SetBlendFrames, idWeapon::Event_SetBlendFrames )
|
|
EVENT( AI_GetBlendFrames, idWeapon::Event_GetBlendFrames )
|
|
EVENT( AI_AnimDone, idWeapon::Event_AnimDone )
|
|
EVENT( EV_Weapon_Next, idWeapon::Event_Next )
|
|
EVENT( EV_SetSkin, idWeapon::Event_SetSkin )
|
|
EVENT( EV_Weapon_Flashlight, idWeapon::Event_Flashlight )
|
|
EVENT( EV_Light_GetLightParm, idWeapon::Event_GetLightParm )
|
|
EVENT( EV_Light_SetLightParm, idWeapon::Event_SetLightParm )
|
|
EVENT( EV_Light_SetLightParms, idWeapon::Event_SetLightParms )
|
|
EVENT( EV_Weapon_LaunchProjectiles, idWeapon::Event_LaunchProjectiles )
|
|
EVENT( EV_Weapon_CreateProjectile, idWeapon::Event_CreateProjectile )
|
|
EVENT( EV_Weapon_EjectBrass, idWeapon::Event_EjectBrass )
|
|
EVENT( EV_Weapon_Melee, idWeapon::Event_Melee )
|
|
EVENT( EV_Weapon_GetWorldModel, idWeapon::Event_GetWorldModel )
|
|
EVENT( EV_Weapon_AllowDrop, idWeapon::Event_AllowDrop )
|
|
EVENT( EV_Weapon_AutoReload, idWeapon::Event_AutoReload )
|
|
EVENT( EV_Weapon_NetReload, idWeapon::Event_NetReload )
|
|
EVENT( EV_Weapon_IsInvisible, idWeapon::Event_IsInvisible )
|
|
EVENT( EV_Weapon_NetEndReload, idWeapon::Event_NetEndReload )
|
|
EVENT( EV_Weapon_GetWeaponSkin, idWeapon::Event_GetWeaponSkin )
|
|
EVENT( EV_Weapon_IsMotionControlled, idWeapon::Event_IsMotionControlled )
|
|
END_CLASS
|
|
|
|
/***********************************************************************
|
|
|
|
init
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::idWeapon()
|
|
================
|
|
*/
|
|
idWeapon::idWeapon() {
|
|
owner = NULL;
|
|
worldModel = NULL;
|
|
weaponDef = NULL;
|
|
thread = NULL;
|
|
|
|
memset( &guiLight, 0, sizeof( guiLight ) );
|
|
memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
|
|
memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) );
|
|
memset( &nozzleGlow, 0, sizeof( nozzleGlow ) );
|
|
|
|
muzzleFlashEnd = 0;
|
|
flashColor = vec3_origin;
|
|
muzzleFlashHandle = -1;
|
|
worldMuzzleFlashHandle = -1;
|
|
guiLightHandle = -1;
|
|
nozzleGlowHandle = -1;
|
|
modelDefHandle = -1;
|
|
|
|
berserk = 2;
|
|
brassDelay = 0;
|
|
|
|
allowDrop = true;
|
|
isPlayerFlashlight = false;
|
|
isPlayerLeftHand = false; // Koz
|
|
|
|
lastIdentifiedFrame = 0;
|
|
currentIdentifiedWeapon = WEAPON_NONE;
|
|
lastIdentifiedWeapon = WEAPON_NONE; // lastweapon holds the last actual weapon value, so the weapon enum will never return a value of 'weapon_flaslight'. nothing to do with the players previous weapon
|
|
|
|
fraccos = 0.0f;
|
|
fraccos2 = 0.0f;
|
|
|
|
Clear();
|
|
|
|
fl.networkSync = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::~idWeapon()
|
|
================
|
|
*/
|
|
idWeapon::~idWeapon() {
|
|
Clear();
|
|
delete worldModel.GetEntity();
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::Spawn
|
|
================
|
|
*/
|
|
void idWeapon::Spawn( void ) {
|
|
if ( !gameLocal.isClient ) {
|
|
// setup the world model
|
|
worldModel = static_cast< idAnimatedEntity * >( gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL ) );
|
|
worldModel.GetEntity()->fl.networkSync = true;
|
|
}
|
|
|
|
thread = new idThread();
|
|
thread->ManualDelete();
|
|
thread->ManualControl();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetOwner
|
|
|
|
Only called at player spawn time, not each weapon switch
|
|
================
|
|
*/
|
|
void idWeapon::SetOwner( idPlayer* _owner, int ownerHand ) {
|
|
assert( !owner );
|
|
owner = _owner;
|
|
hand = ownerHand;
|
|
const char* handNames[ 2 ] = { "right", "left" };
|
|
if( hand < 0 || hand >= 2 || hand == vr_weaponHand.GetInteger() )
|
|
{
|
|
SetName( va( "%s_weapon", owner->name.c_str() ) );
|
|
}
|
|
else
|
|
{
|
|
SetName( va( "%s_weapon_%s", owner->name.c_str(), handNames[ hand ] ) );
|
|
}
|
|
|
|
if( worldModel.GetEntity() )
|
|
{
|
|
worldModel.GetEntity()->SetName( va( "%s_worldmodel", name.c_str() ) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShouldConstructScriptObjectAtSpawn
|
|
|
|
Called during idEntity::Spawn to see if it should construct the script object or not.
|
|
Overridden by subclasses that need to spawn the script object themselves.
|
|
================
|
|
*/
|
|
bool idWeapon::ShouldConstructScriptObjectAtSpawn( void ) const {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::CacheWeapon
|
|
================
|
|
*/
|
|
void idWeapon::CacheWeapon( const char *weaponName ) {
|
|
const idDeclEntityDef *weaponDef;
|
|
const char *brassDefName;
|
|
const char *clipModelName;
|
|
idTraceModel trm;
|
|
const char *guiName;
|
|
|
|
weaponDef = gameLocal.FindEntityDef( weaponName, false );
|
|
if ( !weaponDef ) {
|
|
return;
|
|
}
|
|
|
|
// precache the brass collision model
|
|
brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
|
|
if ( brassDefName[0] ) {
|
|
const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
|
|
if ( brassDef ) {
|
|
brassDef->dict.GetString( "clipmodel", "", &clipModelName );
|
|
if ( !clipModelName[0] ) {
|
|
clipModelName = brassDef->dict.GetString( "model" ); // use the visual model
|
|
}
|
|
// load the trace model
|
|
collisionModelManager->TrmFromModel( clipModelName, trm );
|
|
}
|
|
}
|
|
|
|
guiName = weaponDef->dict.GetString( "gui" );
|
|
if ( guiName[0] ) {
|
|
uiManager->FindGui( guiName, true, false, true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Save
|
|
================
|
|
*/
|
|
void idWeapon::Save( idSaveGame *savefile ) const {
|
|
|
|
savefile->WriteInt( status );
|
|
savefile->WriteObject( thread );
|
|
savefile->WriteString( state );
|
|
savefile->WriteString( idealState );
|
|
savefile->WriteInt( animBlendFrames );
|
|
savefile->WriteInt( animDoneTime );
|
|
savefile->WriteBool( isLinked );
|
|
|
|
savefile->WriteObject( owner );
|
|
worldModel.Save( savefile );
|
|
|
|
savefile->WriteInt( hideTime );
|
|
savefile->WriteFloat( hideDistance );
|
|
savefile->WriteInt( hideStartTime );
|
|
savefile->WriteFloat( hideStart );
|
|
savefile->WriteFloat( hideEnd );
|
|
savefile->WriteFloat( hideOffset );
|
|
savefile->WriteBool( hide );
|
|
savefile->WriteBool( disabled );
|
|
|
|
savefile->WriteInt( berserk );
|
|
|
|
savefile->WriteVec3( playerViewOrigin );
|
|
savefile->WriteMat3( playerViewAxis );
|
|
|
|
savefile->WriteVec3( viewWeaponOrigin );
|
|
savefile->WriteMat3( viewWeaponAxis );
|
|
|
|
savefile->WriteVec3( muzzleOrigin );
|
|
savefile->WriteMat3( muzzleAxis );
|
|
|
|
savefile->WriteVec3( pushVelocity );
|
|
|
|
savefile->WriteString( weaponDef->GetName() );
|
|
savefile->WriteFloat( meleeDistance );
|
|
savefile->WriteString( meleeDefName );
|
|
savefile->WriteInt( brassDelay );
|
|
savefile->WriteString( icon );
|
|
|
|
savefile->WriteInt( guiLightHandle );
|
|
savefile->WriteRenderLight( guiLight );
|
|
|
|
savefile->WriteInt( muzzleFlashHandle );
|
|
savefile->WriteRenderLight( muzzleFlash );
|
|
|
|
savefile->WriteInt( worldMuzzleFlashHandle );
|
|
savefile->WriteRenderLight( worldMuzzleFlash );
|
|
|
|
savefile->WriteVec3( flashColor );
|
|
savefile->WriteInt( muzzleFlashEnd );
|
|
savefile->WriteInt( flashTime );
|
|
|
|
savefile->WriteBool( lightOn );
|
|
savefile->WriteBool( silent_fire );
|
|
|
|
savefile->WriteInt( kick_endtime );
|
|
savefile->WriteInt( muzzle_kick_time );
|
|
savefile->WriteInt( muzzle_kick_maxtime );
|
|
savefile->WriteAngles( muzzle_kick_angles );
|
|
savefile->WriteVec3( muzzle_kick_offset );
|
|
|
|
savefile->WriteInt( ammoType );
|
|
savefile->WriteInt( ammoRequired );
|
|
savefile->WriteInt( clipSize );
|
|
savefile->WriteInt( ammoClip );
|
|
savefile->WriteInt( lowAmmo );
|
|
savefile->WriteBool( powerAmmo );
|
|
|
|
// savegames <= 17
|
|
savefile->WriteInt( 0 );
|
|
|
|
savefile->WriteInt( zoomFov );
|
|
|
|
savefile->WriteJoint( barrelJointView );
|
|
savefile->WriteJoint( flashJointView );
|
|
savefile->WriteJoint( ejectJointView );
|
|
savefile->WriteJoint( guiLightJointView );
|
|
savefile->WriteJoint( ventLightJointView );
|
|
|
|
savefile->WriteJoint( flashJointWorld );
|
|
savefile->WriteJoint( barrelJointWorld );
|
|
savefile->WriteJoint( ejectJointWorld );
|
|
|
|
savefile->WriteBool( hasBloodSplat );
|
|
|
|
savefile->WriteSoundShader( sndHum );
|
|
|
|
savefile->WriteParticle( weaponSmoke );
|
|
savefile->WriteInt( weaponSmokeStartTime );
|
|
savefile->WriteBool( continuousSmoke );
|
|
savefile->WriteParticle( strikeSmoke );
|
|
savefile->WriteInt( strikeSmokeStartTime );
|
|
savefile->WriteVec3( strikePos );
|
|
savefile->WriteMat3( strikeAxis );
|
|
savefile->WriteInt( nextStrikeFx );
|
|
|
|
savefile->WriteBool( nozzleFx );
|
|
savefile->WriteInt( nozzleFxFade );
|
|
|
|
savefile->WriteInt( lastAttack );
|
|
|
|
savefile->WriteInt( nozzleGlowHandle );
|
|
savefile->WriteRenderLight( nozzleGlow );
|
|
|
|
savefile->WriteVec3( nozzleGlowColor );
|
|
savefile->WriteMaterial( nozzleGlowShader );
|
|
savefile->WriteFloat( nozzleGlowRadius );
|
|
|
|
savefile->WriteInt( weaponAngleOffsetAverages );
|
|
savefile->WriteFloat( weaponAngleOffsetScale );
|
|
savefile->WriteFloat( weaponAngleOffsetMax );
|
|
savefile->WriteFloat( weaponOffsetTime );
|
|
savefile->WriteFloat( weaponOffsetScale );
|
|
|
|
savefile->WriteBool( allowDrop );
|
|
savefile->WriteObject( projectileEnt );
|
|
|
|
savefile->WriteJoint( smokeJointView );
|
|
|
|
// Koz begin
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
savefile->WriteJoint( weaponHandAttacher[i] );
|
|
savefile->WriteVec3( weaponHandDefaultPos[i] );
|
|
savefile->WriteMat3( weaponHandDefaultAxis[i] );
|
|
}
|
|
// Koz end
|
|
|
|
}
|
|
/*
|
|
================
|
|
idWeapon::SetFlashlightOwner
|
|
|
|
Only called at player spawn time, not each weapon switch
|
|
================
|
|
*/
|
|
void idWeapon::SetFlashlightOwner( idPlayer* _owner )
|
|
{
|
|
assert( !owner );
|
|
owner = _owner;
|
|
SetName( va( "%s_weapon_flashlight", owner->name.c_str() ) );
|
|
|
|
if( worldModel.GetEntity() )
|
|
{
|
|
worldModel.GetEntity()->SetName( va( "%s_weapon_flashlight_worldmodel", owner->name.c_str() ) );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::Restore
|
|
================
|
|
*/
|
|
void idWeapon::Restore( idRestoreGame *savefile ) {
|
|
|
|
savefile->ReadInt( (int &)status );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
|
|
savefile->ReadString( state );
|
|
savefile->ReadString( idealState );
|
|
savefile->ReadInt( animBlendFrames );
|
|
savefile->ReadInt( animDoneTime );
|
|
savefile->ReadBool( isLinked );
|
|
|
|
// Re-link script fields
|
|
WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
|
|
WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
|
|
WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
|
|
WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
|
|
if (!IsDoom3DemoVersion()) // the demo assets don't support WEAPON_NETFIRING
|
|
WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
|
|
WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
|
|
WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
|
|
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
|
|
worldModel.Restore( savefile );
|
|
|
|
savefile->ReadInt( hideTime );
|
|
savefile->ReadFloat( hideDistance );
|
|
savefile->ReadInt( hideStartTime );
|
|
savefile->ReadFloat( hideStart );
|
|
savefile->ReadFloat( hideEnd );
|
|
savefile->ReadFloat( hideOffset );
|
|
savefile->ReadBool( hide );
|
|
savefile->ReadBool( disabled );
|
|
|
|
savefile->ReadInt( berserk );
|
|
|
|
savefile->ReadVec3( playerViewOrigin );
|
|
savefile->ReadMat3( playerViewAxis );
|
|
|
|
savefile->ReadVec3( viewWeaponOrigin );
|
|
savefile->ReadMat3( viewWeaponAxis );
|
|
|
|
savefile->ReadVec3( muzzleOrigin );
|
|
savefile->ReadMat3( muzzleAxis );
|
|
|
|
savefile->ReadVec3( pushVelocity );
|
|
|
|
idStr objectname;
|
|
savefile->ReadString( objectname );
|
|
weaponDef = gameLocal.FindEntityDef( objectname );
|
|
meleeDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_melee" ), false );
|
|
|
|
const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_projectile" ), false );
|
|
if ( projectileDef ) {
|
|
projectileDict = projectileDef->dict;
|
|
} else {
|
|
projectileDict.Clear();
|
|
}
|
|
|
|
const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_ejectBrass" ), false );
|
|
if ( brassDef ) {
|
|
brassDict = brassDef->dict;
|
|
} else {
|
|
brassDict.Clear();
|
|
}
|
|
|
|
savefile->ReadFloat( meleeDistance );
|
|
savefile->ReadString( meleeDefName );
|
|
savefile->ReadInt( brassDelay );
|
|
savefile->ReadString( icon );
|
|
|
|
savefile->ReadInt( guiLightHandle );
|
|
savefile->ReadRenderLight( guiLight );
|
|
|
|
savefile->ReadInt( muzzleFlashHandle );
|
|
savefile->ReadRenderLight( muzzleFlash );
|
|
|
|
savefile->ReadInt( worldMuzzleFlashHandle );
|
|
savefile->ReadRenderLight( worldMuzzleFlash );
|
|
|
|
savefile->ReadVec3( flashColor );
|
|
savefile->ReadInt( muzzleFlashEnd );
|
|
savefile->ReadInt( flashTime );
|
|
|
|
savefile->ReadBool( lightOn );
|
|
savefile->ReadBool( silent_fire );
|
|
|
|
savefile->ReadInt( kick_endtime );
|
|
savefile->ReadInt( muzzle_kick_time );
|
|
savefile->ReadInt( muzzle_kick_maxtime );
|
|
savefile->ReadAngles( muzzle_kick_angles );
|
|
savefile->ReadVec3( muzzle_kick_offset );
|
|
|
|
savefile->ReadInt( (int &)ammoType );
|
|
savefile->ReadInt( ammoRequired );
|
|
savefile->ReadInt( clipSize );
|
|
savefile->ReadInt( ammoClip );
|
|
savefile->ReadInt( lowAmmo );
|
|
savefile->ReadBool( powerAmmo );
|
|
|
|
// savegame versions <= 17
|
|
int foo;
|
|
savefile->ReadInt( foo );
|
|
|
|
savefile->ReadInt( zoomFov );
|
|
|
|
savefile->ReadJoint( barrelJointView );
|
|
savefile->ReadJoint( flashJointView );
|
|
savefile->ReadJoint( ejectJointView );
|
|
savefile->ReadJoint( guiLightJointView );
|
|
savefile->ReadJoint( ventLightJointView );
|
|
|
|
savefile->ReadJoint( flashJointWorld );
|
|
savefile->ReadJoint( barrelJointWorld );
|
|
savefile->ReadJoint( ejectJointWorld );
|
|
|
|
savefile->ReadBool( hasBloodSplat );
|
|
|
|
savefile->ReadSoundShader( sndHum );
|
|
|
|
savefile->ReadParticle( weaponSmoke );
|
|
savefile->ReadInt( weaponSmokeStartTime );
|
|
savefile->ReadBool( continuousSmoke );
|
|
savefile->ReadParticle( strikeSmoke );
|
|
savefile->ReadInt( strikeSmokeStartTime );
|
|
savefile->ReadVec3( strikePos );
|
|
savefile->ReadMat3( strikeAxis );
|
|
savefile->ReadInt( nextStrikeFx );
|
|
|
|
savefile->ReadBool( nozzleFx );
|
|
savefile->ReadInt( nozzleFxFade );
|
|
|
|
savefile->ReadInt( lastAttack );
|
|
|
|
savefile->ReadInt( nozzleGlowHandle );
|
|
savefile->ReadRenderLight( nozzleGlow );
|
|
|
|
savefile->ReadVec3( nozzleGlowColor );
|
|
savefile->ReadMaterial( nozzleGlowShader );
|
|
savefile->ReadFloat( nozzleGlowRadius );
|
|
|
|
savefile->ReadInt( weaponAngleOffsetAverages );
|
|
savefile->ReadFloat( weaponAngleOffsetScale );
|
|
savefile->ReadFloat( weaponAngleOffsetMax );
|
|
savefile->ReadFloat( weaponOffsetTime );
|
|
savefile->ReadFloat( weaponOffsetScale );
|
|
|
|
savefile->ReadBool( allowDrop );
|
|
savefile->ReadObject( reinterpret_cast<idClass *&>( projectileEnt ) );
|
|
|
|
savefile->ReadJoint( smokeJointView );
|
|
|
|
for ( int i = 0; i < 2; i++ ) {
|
|
savefile->ReadJoint(weaponHandAttacher[i]);
|
|
savefile->ReadVec3(weaponHandDefaultPos[i]);
|
|
savefile->ReadMat3(weaponHandDefaultAxis[i]);
|
|
}
|
|
// gui for stats device on player wrist in VR.
|
|
rvrStatGui = uiManager->FindGui( "guis/weapons/rvrstatgui.gui", true, false, true );
|
|
lvrStatGui = uiManager->FindGui( "guis/weapons/lvrstatgui.gui", true, false, true );
|
|
|
|
//scale = 1.0f; // koz code to scale weapon models in the world borks the viewmodel when loading quick or autosaves, so make sure scale is correct here.
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Weapon definition management
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Clear
|
|
================
|
|
*/
|
|
void idWeapon::Clear( void ) {
|
|
CancelEvents( &EV_Weapon_Clear );
|
|
|
|
DeconstructScriptObject();
|
|
scriptObject.Free();
|
|
|
|
WEAPON_ATTACK.Unlink();
|
|
WEAPON_RELOAD.Unlink();
|
|
WEAPON_NETRELOAD.Unlink();
|
|
WEAPON_NETENDRELOAD.Unlink();
|
|
if (WEAPON_NETFIRING.IsLinked())
|
|
WEAPON_NETFIRING.Unlink();
|
|
WEAPON_RAISEWEAPON.Unlink();
|
|
WEAPON_LOWERWEAPON.Unlink();
|
|
|
|
if ( muzzleFlashHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( muzzleFlashHandle );
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if ( muzzleFlashHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( muzzleFlashHandle );
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if ( worldMuzzleFlashHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
if ( guiLightHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( guiLightHandle );
|
|
guiLightHandle = -1;
|
|
}
|
|
if ( nozzleGlowHandle != -1 ) {
|
|
gameRenderWorld->FreeLightDef( nozzleGlowHandle );
|
|
nozzleGlowHandle = -1;
|
|
}
|
|
|
|
memset( &renderEntity, 0, sizeof( renderEntity ) );
|
|
renderEntity.entityNum = entityNumber;
|
|
|
|
renderEntity.noShadow = true;
|
|
renderEntity.noSelfShadow = true;
|
|
renderEntity.customSkin = NULL;
|
|
|
|
// set default shader parms
|
|
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;
|
|
|
|
if ( refSound.referenceSound ) {
|
|
refSound.referenceSound->Free( true );
|
|
}
|
|
memset( &refSound, 0, sizeof( refSound_t ) );
|
|
|
|
// setting diversity to 0 results in no random sound. -1 indicates random.
|
|
refSound.diversity = -1.0f;
|
|
|
|
if ( owner ) {
|
|
// don't spatialize the weapon sounds
|
|
refSound.listenerId = owner->GetListenerId();
|
|
}
|
|
|
|
// clear out the sounds from our spawnargs since we'll copy them from the weapon def
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "snd_" );
|
|
while( kv ) {
|
|
spawnArgs.Delete( kv->GetKey() );
|
|
kv = spawnArgs.MatchPrefix( "snd_" );
|
|
}
|
|
|
|
hideTime = 300;
|
|
hideDistance = -15.0f;
|
|
hideStartTime = gameLocal.time - hideTime;
|
|
hideStart = 0.0f;
|
|
hideEnd = 0.0f;
|
|
hideOffset = 0.0f;
|
|
hide = false;
|
|
disabled = false;
|
|
|
|
weaponSmoke = NULL;
|
|
weaponSmokeStartTime = 0;
|
|
continuousSmoke = false;
|
|
strikeSmoke = NULL;
|
|
strikeSmokeStartTime = 0;
|
|
strikePos.Zero();
|
|
strikeAxis = mat3_identity;
|
|
nextStrikeFx = 0;
|
|
|
|
icon = "";
|
|
|
|
playerViewAxis.Identity();
|
|
playerViewOrigin.Zero();
|
|
viewWeaponAxis.Identity();
|
|
viewWeaponOrigin.Zero();
|
|
muzzleAxis.Identity();
|
|
muzzleOrigin.Zero();
|
|
pushVelocity.Zero();
|
|
|
|
status = WP_HOLSTERED;
|
|
state = "";
|
|
idealState = "";
|
|
animBlendFrames = 0;
|
|
animDoneTime = 0;
|
|
|
|
projectileDict.Clear();
|
|
meleeDef = NULL;
|
|
meleeDefName = "";
|
|
meleeDistance = 0.0f;
|
|
brassDict.Clear();
|
|
|
|
flashTime = 250;
|
|
lightOn = false;
|
|
silent_fire = false;
|
|
|
|
ammoType = 0;
|
|
ammoRequired = 0;
|
|
ammoClip = 0;
|
|
clipSize = 0;
|
|
lowAmmo = 0;
|
|
powerAmmo = false;
|
|
|
|
kick_endtime = 0;
|
|
muzzle_kick_time = 0;
|
|
muzzle_kick_maxtime = 0;
|
|
muzzle_kick_angles.Zero();
|
|
muzzle_kick_offset.Zero();
|
|
|
|
zoomFov = 90;
|
|
|
|
barrelJointView = INVALID_JOINT;
|
|
flashJointView = INVALID_JOINT;
|
|
ejectJointView = INVALID_JOINT;
|
|
guiLightJointView = INVALID_JOINT;
|
|
ventLightJointView = INVALID_JOINT;
|
|
|
|
barrelJointWorld = INVALID_JOINT;
|
|
flashJointWorld = INVALID_JOINT;
|
|
ejectJointWorld = INVALID_JOINT;
|
|
|
|
smokeJointView = INVALID_JOINT;
|
|
|
|
hasBloodSplat = false;
|
|
nozzleFx = false;
|
|
nozzleFxFade = 1500;
|
|
lastAttack = 0;
|
|
nozzleGlowHandle = -1;
|
|
nozzleGlowShader = NULL;
|
|
nozzleGlowRadius = 10;
|
|
nozzleGlowColor.Zero();
|
|
|
|
weaponAngleOffsetAverages = 0;
|
|
weaponAngleOffsetScale = 0.0f;
|
|
weaponAngleOffsetMax = 0.0f;
|
|
weaponOffsetTime = 0.0f;
|
|
weaponOffsetScale = 0.0f;
|
|
|
|
allowDrop = true;
|
|
|
|
animator.ClearAllAnims( gameLocal.time, 0 );
|
|
FreeModelDef();
|
|
|
|
sndHum = NULL;
|
|
|
|
isLinked = false;
|
|
projectileEnt = NULL;
|
|
|
|
isFiring = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::InitWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::InitWorldModel( const idDeclEntityDef *def ) {
|
|
idEntity *ent;
|
|
|
|
ent = worldModel.GetEntity();
|
|
|
|
assert( ent );
|
|
assert( def );
|
|
|
|
const char *model = def->dict.GetString( "model_world" );
|
|
const char *attach = def->dict.GetString( "joint_attach" );
|
|
|
|
ent->SetSkin( NULL );
|
|
if ( model[0] && attach[0] ) {
|
|
ent->Show();
|
|
ent->SetModel( model );
|
|
if ( ent->GetAnimator()->ModelDef() ) {
|
|
ent->SetSkin( ent->GetAnimator()->ModelDef()->GetDefaultSkin() );
|
|
}
|
|
ent->GetPhysics()->SetContents( 0 );
|
|
ent->GetPhysics()->SetClipModel( NULL, 1.0f );
|
|
ent->BindToJoint( owner, attach, true );
|
|
ent->GetPhysics()->SetOrigin( vec3_origin );
|
|
ent->GetPhysics()->SetAxis( mat3_identity );
|
|
|
|
// supress model in player views, but allow it in mirrors and remote views
|
|
renderEntity_t *worldModelRenderEntity = ent->GetRenderEntity();
|
|
if ( worldModelRenderEntity ) {
|
|
// Koz begin
|
|
if ( game->isVR && !strstr(def->dict.GetString("model_world"),"flashlight"))
|
|
{
|
|
// Koz dont show the worldmodel if the player body is shown in vr.
|
|
worldModelRenderEntity->suppressSurfaceInViewID = 0;
|
|
worldModelRenderEntity->suppressShadowInViewID = 0;
|
|
}
|
|
else
|
|
{
|
|
//Carl: Don't suppress drawing the weapon's world model or it's shadow in 1st person, if they want to see it (in VR)
|
|
worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber + 1;
|
|
worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber + 1;
|
|
}
|
|
// Koz end
|
|
worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
}
|
|
} else {
|
|
ent->SetModel( "" );
|
|
ent->Hide();
|
|
}
|
|
|
|
flashJointWorld = ent->GetAnimator()->GetJointHandle( "flash" );
|
|
barrelJointWorld = ent->GetAnimator()->GetJointHandle( "muzzle" );
|
|
ejectJointWorld = ent->GetAnimator()->GetJointHandle( "eject" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponDef
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) {
|
|
const char *shader;
|
|
const char *objectType;
|
|
const char *vmodel;
|
|
const char *guiName;
|
|
const char *projectileName;
|
|
const char *brassDefName;
|
|
const char *smokeName;
|
|
int ammoAvail;
|
|
|
|
Clear();
|
|
|
|
if ( !objectname || !objectname[ 0 ] ) {
|
|
return;
|
|
}
|
|
|
|
assert( owner );
|
|
|
|
weaponDef = gameLocal.FindEntityDef( objectname );
|
|
|
|
ammoType = GetAmmoNumForName( weaponDef->dict.GetString( "ammoType" ) );
|
|
ammoRequired = weaponDef->dict.GetInt( "ammoRequired" );
|
|
clipSize = weaponDef->dict.GetInt( "clipSize" );
|
|
lowAmmo = weaponDef->dict.GetInt( "lowAmmo" );
|
|
|
|
icon = weaponDef->dict.GetString( "icon" );
|
|
silent_fire = weaponDef->dict.GetBool( "silent_fire" );
|
|
powerAmmo = weaponDef->dict.GetBool( "powerAmmo" );
|
|
|
|
muzzle_kick_time = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_time" ) );
|
|
muzzle_kick_maxtime = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_maxtime" ) );
|
|
muzzle_kick_angles = weaponDef->dict.GetAngles( "muzzle_kick_angles" );
|
|
muzzle_kick_offset = weaponDef->dict.GetVector( "muzzle_kick_offset" );
|
|
|
|
hideTime = SEC2MS( weaponDef->dict.GetFloat( "hide_time", "0.3" ) );
|
|
hideDistance = weaponDef->dict.GetFloat( "hide_distance", "-15" );
|
|
|
|
// muzzle smoke
|
|
smokeName = weaponDef->dict.GetString( "smoke_muzzle" );
|
|
if ( *smokeName != '\0' ) {
|
|
weaponSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
} else {
|
|
weaponSmoke = NULL;
|
|
}
|
|
continuousSmoke = weaponDef->dict.GetBool( "continuousSmoke" );
|
|
weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
|
|
|
|
smokeName = weaponDef->dict.GetString( "smoke_strike" );
|
|
if ( *smokeName != '\0' ) {
|
|
strikeSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
|
|
} else {
|
|
strikeSmoke = NULL;
|
|
}
|
|
strikeSmokeStartTime = 0;
|
|
strikePos.Zero();
|
|
strikeAxis = mat3_identity;
|
|
nextStrikeFx = 0;
|
|
|
|
// setup gui light
|
|
memset( &guiLight, 0, sizeof( guiLight ) );
|
|
const char *guiLightShader = weaponDef->dict.GetString( "mtr_guiLightShader" );
|
|
if ( *guiLightShader != '\0' ) {
|
|
// Koz begin
|
|
// the PDA model was scaled by a factor of 2.5 to make it easier to read in VR.
|
|
// The weapon guilight size isn't stored in the weapon def, it's hardcoded here,
|
|
// so check if PDA and resize the guilight accordingly
|
|
|
|
int lightRad = 3;
|
|
if ( game->isVR )
|
|
{
|
|
const char* weapName = weaponDef->dict.GetString( "inv_name" );
|
|
if ( strstr( weapName, "PDA" ) )
|
|
{
|
|
lightRad *= 3; // the PDA guilight needs to be bigger in VR
|
|
}
|
|
}
|
|
guiLight.shader = declManager->FindMaterial( guiLightShader, false );
|
|
guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = lightRad;
|
|
guiLight.pointLight = true;
|
|
}
|
|
|
|
// setup the view model
|
|
vmodel = weaponDef->dict.GetString( "model_view" );
|
|
SetModel( vmodel );
|
|
|
|
// Koz weapon shadows
|
|
renderEntity.noShadow = false;
|
|
|
|
// setup the world model
|
|
InitWorldModel( weaponDef );
|
|
worldModel.GetEntity()->GetRenderEntity()->noShadow = true; // Koz fixme only if using viewweapon for body model
|
|
// copy the sounds from the weapon view model def into out spawnargs
|
|
const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" );
|
|
while( kv ) {
|
|
spawnArgs.Set( kv->GetKey(), kv->GetValue() );
|
|
kv = weaponDef->dict.MatchPrefix( "snd_", kv );
|
|
}
|
|
|
|
if ( game->isVR )
|
|
{
|
|
weaponHandAttacher[0] = animator.GetJointHandle( "RhandAttacher" );
|
|
if ( weaponHandAttacher[0] != INVALID_JOINT )
|
|
{
|
|
// Koz debug common->Printf( "Weapon %s RhandAttacherJoint Found\n", objectname );
|
|
animator.GetJointTransform( weaponHandAttacher[0], gameLocal.time, weaponHandDefaultPos[0], weaponHandDefaultAxis[0] );
|
|
// Koz debug common->Printf( "Default pos %s default axis %s\n", weaponHandDefaultPos[0].ToString(), weaponHandDefaultAxis[0].ToAngles().ToString() );
|
|
|
|
}
|
|
|
|
weaponHandAttacher[1] = animator.GetJointHandle( "LhandAttacher" );
|
|
if ( weaponHandAttacher[1] != INVALID_JOINT )
|
|
{
|
|
// Koz debug common->Printf( "Weapon %s LhandAttacherJoint Found\n", objectname );
|
|
animator.GetJointTransform( weaponHandAttacher[1], gameLocal.time, weaponHandDefaultPos[1], weaponHandDefaultAxis[1] );
|
|
// Koz debug common->Printf( "Default pos %s default axis %s\n", weaponHandDefaultPos[1].ToString(), weaponHandDefaultAxis[1].ToAngles().ToString() );
|
|
}
|
|
|
|
laserSightOffset = weaponDef->dict.GetVector( "laserSightOffset" );
|
|
|
|
}
|
|
|
|
// find some joints in the model for locating effects
|
|
barrelJointView = animator.GetJointHandle( "barrel" );
|
|
flashJointView = animator.GetJointHandle( "flash" );
|
|
ejectJointView = animator.GetJointHandle( "eject" );
|
|
guiLightJointView = animator.GetJointHandle( "guiLight" );
|
|
ventLightJointView = animator.GetJointHandle( "ventLight" );
|
|
|
|
idStr smokeJoint = weaponDef->dict.GetString( "smoke_joint" );
|
|
if( smokeJoint.Length() > 0 )
|
|
{
|
|
smokeJointView = animator.GetJointHandle( smokeJoint );
|
|
}
|
|
else
|
|
{
|
|
smokeJointView = INVALID_JOINT;
|
|
}
|
|
|
|
// get the projectile
|
|
projectileDict.Clear();
|
|
|
|
projectileName = weaponDef->dict.GetString( "def_projectile" );
|
|
if ( projectileName[0] != '\0' ) {
|
|
const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projectileName, false );
|
|
if ( !projectileDef ) {
|
|
gameLocal.Warning( "Unknown projectile '%s' in weapon '%s'", projectileName, objectname );
|
|
} else {
|
|
const char *spawnclass = projectileDef->dict.GetString( "spawnclass" );
|
|
idTypeInfo *cls = idClass::GetClass( spawnclass );
|
|
if ( !cls || !cls->IsType( idProjectile::Type ) ) {
|
|
gameLocal.Warning( "Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname );
|
|
} else {
|
|
projectileDict = projectileDef->dict;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set up muzzleflash render light
|
|
const idMaterial*flashShader;
|
|
idVec3 flashTarget;
|
|
idVec3 flashUp;
|
|
idVec3 flashRight;
|
|
float flashRadius;
|
|
bool flashPointLight;
|
|
|
|
weaponDef->dict.GetString( "mtr_flashShader", "", &shader );
|
|
flashShader = declManager->FindMaterial( shader, false );
|
|
flashPointLight = weaponDef->dict.GetBool( "flashPointLight", "1" );
|
|
weaponDef->dict.GetVector( "flashColor", "0 0 0", flashColor );
|
|
flashRadius = (float)weaponDef->dict.GetInt( "flashRadius" ); // if 0, no light will spawn
|
|
flashTime = SEC2MS( weaponDef->dict.GetFloat( "flashTime", "0.25" ) );
|
|
flashTarget = weaponDef->dict.GetVector( "flashTarget" );
|
|
flashUp = weaponDef->dict.GetVector( "flashUp" );
|
|
flashRight = weaponDef->dict.GetVector( "flashRight" );
|
|
|
|
memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
|
|
muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
muzzleFlash.allowLightInViewID = owner->entityNumber+1;
|
|
|
|
// the weapon lights will only be in first person
|
|
guiLight.allowLightInViewID = owner->entityNumber+1;
|
|
nozzleGlow.allowLightInViewID = owner->entityNumber+1;
|
|
|
|
muzzleFlash.pointLight = flashPointLight;
|
|
muzzleFlash.shader = flashShader;
|
|
muzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
|
|
muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
|
|
muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
|
|
muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
|
|
|
|
muzzleFlash.lightRadius[0] = flashRadius;
|
|
muzzleFlash.lightRadius[1] = flashRadius;
|
|
muzzleFlash.lightRadius[2] = flashRadius;
|
|
|
|
if ( !flashPointLight ) {
|
|
muzzleFlash.target = flashTarget;
|
|
muzzleFlash.up = flashUp;
|
|
muzzleFlash.right = flashRight;
|
|
muzzleFlash.end = flashTarget;
|
|
}
|
|
|
|
// the world muzzle flash is the same, just positioned differently
|
|
worldMuzzleFlash = muzzleFlash;
|
|
worldMuzzleFlash.suppressLightInViewID = owner->entityNumber+1;
|
|
worldMuzzleFlash.allowLightInViewID = 0;
|
|
worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber;
|
|
|
|
//-----------------------------------
|
|
|
|
nozzleFx = weaponDef->dict.GetBool("nozzleFx");
|
|
nozzleFxFade = weaponDef->dict.GetInt("nozzleFxFade", "1500");
|
|
nozzleGlowColor = weaponDef->dict.GetVector("nozzleGlowColor", "1 1 1");
|
|
nozzleGlowRadius = weaponDef->dict.GetFloat("nozzleGlowRadius", "10");
|
|
weaponDef->dict.GetString( "mtr_nozzleGlowShader", "", &shader );
|
|
nozzleGlowShader = declManager->FindMaterial( shader, false );
|
|
|
|
// get the melee damage def
|
|
meleeDistance = weaponDef->dict.GetFloat( "melee_distance" );
|
|
meleeDefName = weaponDef->dict.GetString( "def_melee" );
|
|
if ( meleeDefName.Length() ) {
|
|
meleeDef = gameLocal.FindEntityDef( meleeDefName, false );
|
|
if ( !meleeDef ) {
|
|
gameLocal.Error( "Unknown melee '%s'", meleeDefName.c_str() );
|
|
}
|
|
}
|
|
|
|
// get the brass def
|
|
brassDict.Clear();
|
|
brassDelay = weaponDef->dict.GetInt( "ejectBrassDelay", "0" );
|
|
brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
|
|
|
|
if ( brassDefName[0] ) {
|
|
const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
|
|
if ( !brassDef ) {
|
|
gameLocal.Warning( "Unknown brass '%s'", brassDefName );
|
|
} else {
|
|
brassDict = brassDef->dict;
|
|
}
|
|
}
|
|
|
|
if ( ( ammoType < 0 ) || ( ammoType >= AMMO_NUMTYPES ) ) {
|
|
gameLocal.Warning( "Unknown ammotype in object '%s'", objectname );
|
|
}
|
|
|
|
ammoClip = ammoinclip;
|
|
if ( ( ammoClip < 0 ) || ( ammoClip > clipSize ) ) {
|
|
// first time using this weapon so have it fully loaded to start
|
|
ammoClip = clipSize;
|
|
ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
|
|
if ( ammoClip > ammoAvail ) {
|
|
ammoClip = ammoAvail;
|
|
}
|
|
}
|
|
|
|
renderEntity.gui[ 0 ] = NULL;
|
|
guiName = weaponDef->dict.GetString( "gui" );
|
|
if ( guiName[0] ) {
|
|
renderEntity.gui[ 0 ] = uiManager->FindGui( guiName, true, false, true );
|
|
}
|
|
|
|
// Koz begin - gui for stats device on player wrist in VR.
|
|
rvrStatGui = uiManager->FindGui("guis/weapons/rvrstatgui.gui", true, false, true);
|
|
lvrStatGui = uiManager->FindGui("guis/weapons/lvrstatgui.gui", true, false, true);
|
|
// Koz end
|
|
|
|
zoomFov = weaponDef->dict.GetInt( "zoomFov", "70" );
|
|
berserk = weaponDef->dict.GetInt( "berserk", "2" );
|
|
|
|
weaponAngleOffsetAverages = weaponDef->dict.GetInt( "weaponAngleOffsetAverages", "10" );
|
|
weaponAngleOffsetScale = weaponDef->dict.GetFloat( "weaponAngleOffsetScale", "0.25" );
|
|
weaponAngleOffsetMax = weaponDef->dict.GetFloat( "weaponAngleOffsetMax", "10" );
|
|
|
|
weaponOffsetTime = weaponDef->dict.GetFloat( "weaponOffsetTime", "400" );
|
|
weaponOffsetScale = weaponDef->dict.GetFloat( "weaponOffsetScale", "0.005" );
|
|
|
|
if ( !weaponDef->dict.GetString( "weapon_scriptobject", NULL, &objectType ) ) {
|
|
gameLocal.Error( "No 'weapon_scriptobject' set on '%s'.", objectname );
|
|
}
|
|
|
|
// setup script object
|
|
if ( !scriptObject.SetType( objectType ) ) {
|
|
gameLocal.Error( "Script object '%s' not found on weapon '%s'.", objectType, objectname );
|
|
}
|
|
|
|
WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
|
|
WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
|
|
WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
|
|
WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
|
|
if (!IsDoom3DemoVersion()) // the demo assets don't support WEAPON_NETFIRING
|
|
WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
|
|
WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
|
|
WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
|
|
|
|
spawnArgs = weaponDef->dict;
|
|
|
|
shader = spawnArgs.GetString( "snd_hum" );
|
|
if ( shader && *shader ) {
|
|
sndHum = declManager->FindSound( shader );
|
|
StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
|
|
isLinked = true;
|
|
|
|
// call script object's constructor
|
|
ConstructScriptObject();
|
|
|
|
// make sure we have the correct skin
|
|
UpdateSkin();
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
GUIs
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Icon
|
|
================
|
|
*/
|
|
const char *idWeapon::Icon( void ) const {
|
|
return icon;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::IdentifyWeapon
|
|
Koz return weapon enumeration
|
|
===============
|
|
*/
|
|
|
|
weapon_t idWeapon::IdentifyWeapon()
|
|
{
|
|
/*if ( lastIdentifiedFrame == idLib::frameNumber ) // only check once per game frame.
|
|
{
|
|
//GBFIX - This is true even though it isnt!!
|
|
//return currentIdentifiedWeapon;
|
|
}
|
|
|
|
lastIdentifiedFrame = idLib::frameNumber;*/
|
|
|
|
currentIdentifiedWeapon = WEAPON_NONE;
|
|
|
|
if ( this )
|
|
{
|
|
if ( weaponDef != NULL )
|
|
{
|
|
idStr weaponName = weaponDef->GetName();
|
|
|
|
if ( idStr::Icmp( "weapon_fists", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_FISTS;
|
|
else if ( idStr::Icmp( "weapon_chainsaw", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_CHAINSAW;
|
|
else if ( idStr::Icmp( "weapon_pistol", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_PISTOL;
|
|
else if ( idStr::Icmp( "weapon_shotgun", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_SHOTGUN;
|
|
else if ( idStr::Icmp( "weapon_machinegun", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_MACHINEGUN;
|
|
else if ( idStr::Icmp( "weapon_chaingun", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_CHAINGUN;
|
|
else if ( idStr::Icmp( "weapon_handgrenade", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_HANDGRENADE;
|
|
else if ( idStr::Icmp( "weapon_plasmagun", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_PLASMAGUN;
|
|
else if ( idStr::Icmp( "weapon_rocketlauncher", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ROCKETLAUNCHER;
|
|
else if ( idStr::Icmp( "weapon_bfg", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_BFG;
|
|
else if ( idStr::Icmp( "weapon_soulcube", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_SOULCUBE;
|
|
else if ( idStr::Icmp( "weapon_artifact", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ARTIFACT;
|
|
else if ( idStr::Icmp( "weapon_bloodstone_passive", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ARTIFACT;
|
|
else if ( idStr::Icmp( "weapon_bloodstone_active1", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ARTIFACT;
|
|
else if ( idStr::Icmp( "weapon_bloodstone_active2", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ARTIFACT;
|
|
else if ( idStr::Icmp( "weapon_bloodstone_active3", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_ARTIFACT;
|
|
else if ( idStr::Icmp( "weapon_pda", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_PDA;
|
|
else if ( idStr::Icmp( "weapon_flashlight_new", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = lastIdentifiedWeapon;
|
|
//else if ( idStr::Icmp( "weapon_flashlight_new", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_FLASHLIGHT;
|
|
//else if ( idStr::Icmp( "weapon_flashlight", weaponDef->GetName() ) == 0 ) currentIdentifiedWeapon = WEAPON_FLASHLIGHT;
|
|
else currentIdentifiedWeapon = WEAPON_NONE;
|
|
lastIdentifiedWeapon = currentIdentifiedWeapon;
|
|
}
|
|
}
|
|
if ( currentIdentifiedWeapon == WEAPON_FLASHLIGHT ) common->Printf( "Identify weapon returned %s\n", weaponDef->GetName() );
|
|
|
|
return currentIdentifiedWeapon;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateGUI
|
|
================
|
|
*/
|
|
void idWeapon::UpdateGUI( void ) {
|
|
if ( game->isVR ) UpdateVRGUI();
|
|
|
|
if ( !renderEntity.gui[ 0 ] ) {
|
|
return;
|
|
}
|
|
|
|
if ( status == WP_HOLSTERED ) {
|
|
return;
|
|
}
|
|
|
|
if ( owner->weaponGone ) {
|
|
// dropping weapons was implemented wierd, so we have to not update the gui when it happens or we'll get a negative ammo count
|
|
return;
|
|
}
|
|
|
|
if ( gameLocal.localClientNum != owner->entityNumber ) {
|
|
// if updating the hud for 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 != owner->entityNumber ) {
|
|
return;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
int inclip = AmmoInClip();
|
|
int ammoamount = AmmoAvailable();
|
|
|
|
if ( ammoamount < 0 ) {
|
|
// show infinite ammo
|
|
renderEntity.gui[ 0 ]->SetStateString( "player_ammo", "" );
|
|
} else {
|
|
// show remaining ammo
|
|
renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip) );
|
|
renderEntity.gui[ 0 ]->SetStateString( "player_ammo", ClipSize() ? va( "%i", inclip ) : "--" );
|
|
renderEntity.gui[ 0 ]->SetStateString( "player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--" );
|
|
renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
|
|
}
|
|
renderEntity.gui[ 0 ]->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
|
|
renderEntity.gui[ 0 ]->SetStateBool( "player_clip_empty", ( inclip == 0 ) );
|
|
renderEntity.gui[ 0 ]->SetStateBool( "player_clip_low", ( inclip <= lowAmmo ) );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Model and muzzleflash
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateFlashPosition
|
|
================
|
|
*/
|
|
void idWeapon::UpdateFlashPosition( void ) {
|
|
// the flash has an explicit joint for locating it
|
|
GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
|
|
|
|
if( isPlayerFlashlight )
|
|
{
|
|
static float pscale = 2.0f;
|
|
static float yscale = 0.25f;
|
|
|
|
// static idVec3 baseAdjustPos = vec3_zero; //idVec3( 0.0f, 10.0f, 0.0f );
|
|
// idVec3 adjustPos = baseAdjustPos;
|
|
// muzzleFlash.origin += adjustPos.x * muzzleFlash.axis[1] + adjustPos.y * muzzleFlash.axis[0] + adjustPos.z * muzzleFlash.axis[2];
|
|
muzzleFlash.origin += owner->GetViewBob();
|
|
|
|
// static idAngles baseAdjustAng = ang_zero; //idAngles( 0.0f, 10.0f, 0.0f );
|
|
idAngles adjustAng = /*baseAdjustAng +*/ idAngles( fraccos * yscale, 0.0f, fraccos2 * pscale );
|
|
idAngles bobAngles = owner->GetViewBobAngles();
|
|
SwapValues( bobAngles.pitch, bobAngles.roll );
|
|
adjustAng += bobAngles * 3.0f;
|
|
muzzleFlash.axis = adjustAng.ToMat3() * muzzleFlash.axis /** adjustAng.ToMat3()*/;
|
|
}
|
|
|
|
// if the desired point is inside or very close to a wall, back it up until it is clear
|
|
idVec3 start = muzzleFlash.origin - playerViewAxis[0] * 16;
|
|
idVec3 end = muzzleFlash.origin + playerViewAxis[0] * 8;
|
|
trace_t tr;
|
|
gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
|
|
// be at least 8 units away from a solid
|
|
muzzleFlash.origin = tr.endpos - playerViewAxis[0] * 8;
|
|
|
|
muzzleFlash.noShadows = !g_weaponShadows.GetBool();
|
|
|
|
// put the world muzzle flash on the end of the joint, no matter what
|
|
GetGlobalJointTransform( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::MuzzleFlashLight
|
|
================
|
|
*/
|
|
void idWeapon::MuzzleFlashLight( void ) {
|
|
|
|
if ( !lightOn && ( !g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0] ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( flashJointView == INVALID_JOINT ) {
|
|
return;
|
|
}
|
|
|
|
UpdateFlashPosition();
|
|
|
|
// these will be different each fire
|
|
muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
|
|
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
|
|
|
|
// the light will be removed at this time
|
|
muzzleFlashEnd = gameLocal.time + flashTime;
|
|
|
|
if ( muzzleFlashHandle != -1 ) {
|
|
gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
|
|
gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
|
|
} else {
|
|
muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
|
|
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateVRGUI
|
|
================
|
|
*/
|
|
void idWeapon::UpdateVRGUI()
|
|
{
|
|
|
|
float healthv = 0.0f;
|
|
float armorv = 0.0f;
|
|
float armorb = 0.0f;
|
|
float healthb = 0.0f;
|
|
|
|
idPlayer* player;
|
|
player = gameLocal.GetLocalPlayer();
|
|
|
|
if ( !player ) return;
|
|
|
|
if ( rvrStatGui == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( status == WP_HOLSTERED )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( isPlayerFlashlight || isPlayerLeftHand ) return;
|
|
|
|
if ( owner->weaponGone )
|
|
{
|
|
// dropping weapons was implemented weird, so we have to not update the gui when it happens or we'll get a negative ammo count
|
|
return;
|
|
}
|
|
|
|
int inclip[2];
|
|
int ammoamount[2];
|
|
int clipsize[2];
|
|
bool harvester[2];
|
|
bool ammoEmpty[2];
|
|
bool clipEmpty[2];
|
|
bool clipLow[2];
|
|
|
|
for ( int h = 0; h < 2; h++ )
|
|
{
|
|
inclip[h] = player->hands[h].weapon.GetEntity()->AmmoInClip();
|
|
ammoamount[h] = player->hands[h].weapon.GetEntity()->AmmoAvailable();
|
|
harvester[h] = player->hands[h].weapon.GetEntity()->IdentifyWeapon() == WEAPON_ARTIFACT || player->hands[h].weapon.GetEntity()->IdentifyWeapon() == WEAPON_SOULCUBE;
|
|
clipsize[h] = player->hands[h].weapon.GetEntity()->ClipSize();
|
|
|
|
|
|
ammoEmpty[h] = ( ammoamount[h] == 0 ) ;
|
|
clipEmpty[h] = ( player->hands[h].weapon.GetEntity()->ClipSize() && inclip[h] == 0);
|
|
clipLow[h] = ( player->hands[h].weapon.GetEntity()->ClipSize() && player->hands[h].weapon.GetEntity()->LowAmmo());
|
|
}
|
|
|
|
//int inclip = AmmoInClip();
|
|
//int ammoamount = AmmoAvailable();
|
|
|
|
// update the right hand statwatch
|
|
|
|
if ( ammoamount[HAND_RIGHT] < 0 )
|
|
{
|
|
// show infinite ammo
|
|
rvrStatGui->SetStateString( "player_ammo", "" );
|
|
}
|
|
else
|
|
{
|
|
// show remaining ammo
|
|
rvrStatGui->SetStateString("player_totalammo", va("%i", ammoamount[HAND_RIGHT]));
|
|
rvrStatGui->SetStateString("player_ammo", clipsize[HAND_RIGHT] ? va("%i", inclip[HAND_RIGHT]) : "--");
|
|
rvrStatGui->SetStateString("player_clips", clipsize[HAND_RIGHT] ? va("%i", ammoamount[HAND_RIGHT] / clipsize[HAND_RIGHT]) : "--");
|
|
rvrStatGui->SetStateString("player_allammo", va("%i/%i", inclip[HAND_RIGHT], ammoamount[HAND_RIGHT]));
|
|
}
|
|
rvrStatGui->SetStateBool("player_ammo_empty", ( ammoEmpty[HAND_RIGHT] ));
|
|
rvrStatGui->SetStateBool("player_clip_empty", ( clipEmpty[HAND_RIGHT] ));
|
|
rvrStatGui->SetStateBool("player_clip_low", ( clipLow[HAND_RIGHT] ));
|
|
|
|
// koz todo
|
|
// need to get the ammocount from the hand structures, not sure right now, in the interim just use the ammo amount.
|
|
|
|
//Let the GUI know the total amount of ammo regardless of the ammo required value
|
|
//rvrStatGui->SetStateString( "player_ammo_count", va( "%i", AmmoCount() ) );
|
|
|
|
rvrStatGui->SetStateString("player_ammo_count", va("%i", ammoamount[HAND_RIGHT]));
|
|
// koz end
|
|
|
|
//health and armor
|
|
|
|
if ( player )
|
|
{
|
|
healthv = (float)player->health;
|
|
if ( player->inventory.armor )
|
|
{
|
|
armorv = (float)player->inventory.armor;
|
|
}
|
|
|
|
//healthv = vr_guiH.GetInteger();
|
|
//armorv = vr_guiA.GetInteger();
|
|
|
|
healthv = idMath::ClampFloat( 0.0, 100.0, healthv );
|
|
armorv = idMath::ClampFloat( 0.0, 100.0, armorv );
|
|
|
|
if ( healthv >= 75.0 ) healthb = ( healthv / 100.0f ) ;
|
|
if ( armorv >= 75.0 ) armorb = ( armorv / 100.0f ) ;
|
|
|
|
healthb = idMath::ClampFloat( 0.0, 100.0, healthb );
|
|
armorb = idMath::ClampFloat( 0.0, 100.0, armorb );
|
|
|
|
}
|
|
|
|
rvrStatGui->SetStateString( "player_health", va( "%f", healthv ) );
|
|
//vrStatGui->SetStateString( "player_armor", va( "%i%%", armorv ) );
|
|
rvrStatGui->SetStateString( "player_armor", va( "%f", armorv ) );
|
|
rvrStatGui->SetStateString( "player_healthb", va( "%f", healthb ) );
|
|
rvrStatGui->SetStateString( "player_armorb", va( "%f", armorb ) );
|
|
|
|
|
|
// update the left hand stat watch gui.
|
|
|
|
if (lvrStatGui == NULL)
|
|
{
|
|
common->Printf("Left Hand stat gui not found.\n");
|
|
return;
|
|
}
|
|
|
|
lvrStatGui->SetStateString("player_health", va("%f", healthv));
|
|
//vrStatGui->SetStateString( "player_armor", va( "%i%%", armorv ) );
|
|
lvrStatGui->SetStateString("player_armor", va("%f", armorv));
|
|
lvrStatGui->SetStateString("player_healthb", va("%f", healthb));
|
|
lvrStatGui->SetStateString("player_armorb", va("%f", armorb));
|
|
|
|
if (ammoamount[HAND_LEFT] < 0)
|
|
{
|
|
// show infinite ammo
|
|
lvrStatGui->SetStateString("player_ammo", "");
|
|
}
|
|
else
|
|
{
|
|
// show remaining ammo
|
|
lvrStatGui->SetStateString("player_totalammo", va("%i", ammoamount[HAND_LEFT]));
|
|
lvrStatGui->SetStateString("player_ammo", clipsize[HAND_LEFT] ? va("%i", inclip[HAND_LEFT]) : "--");
|
|
lvrStatGui->SetStateString("player_clips", clipsize[HAND_LEFT] ? va("%i", ammoamount[HAND_LEFT] / clipsize[HAND_LEFT]) : "--");
|
|
lvrStatGui->SetStateString("player_allammo", va("%i/%i", inclip[HAND_LEFT], ammoamount[HAND_LEFT]));
|
|
}
|
|
lvrStatGui->SetStateBool("player_ammo_empty", (ammoEmpty[HAND_LEFT]));
|
|
lvrStatGui->SetStateBool("player_clip_empty", (clipEmpty[HAND_LEFT]));
|
|
lvrStatGui->SetStateBool("player_clip_low", (clipLow[HAND_LEFT]));
|
|
|
|
// koz todo
|
|
|
|
// need to get the ammocount from the hand structures, not sure right now, in the interim just use the ammo amount.
|
|
//Let the GUI know the total amount of ammo regardless of the ammo required value
|
|
//rvrStatGui->SetStateString( "player_ammo_count", va( "%i", AmmoCount() ) );
|
|
|
|
lvrStatGui->SetStateString("player_ammo_count", va("%i", ammoamount[HAND_LEFT]));
|
|
// koz end
|
|
|
|
// koz todo also need to figure this out for dual guis
|
|
}
|
|
|
|
/*
|
|
================
|
|
Koz
|
|
idWeapon::UpdateWeaponClipPosition
|
|
================
|
|
*/
|
|
void idWeapon::UpdateWeaponClipPosition( idVec3 &origin, idMat3 &axis )
|
|
{
|
|
|
|
/*PDAclipModel->SetPosition( origin, axis );
|
|
PDAclipModel->Link( gameLocal.clip );
|
|
*/
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateSkin
|
|
================
|
|
*/
|
|
bool idWeapon::UpdateSkin( void ) {
|
|
const function_t *func;
|
|
|
|
if ( !isLinked ) {
|
|
return false;
|
|
}
|
|
|
|
func = scriptObject.GetFunction( "UpdateSkin" );
|
|
if ( !func ) {
|
|
common->Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
|
|
return false;
|
|
}
|
|
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetModel
|
|
================
|
|
*/
|
|
void idWeapon::SetModel( const char *modelname ) {
|
|
assert( modelname );
|
|
|
|
if ( modelDefHandle >= 0 ) {
|
|
gameRenderWorld->RemoveDecals( modelDefHandle );
|
|
}
|
|
|
|
renderEntity.hModel = animator.SetModel( modelname );
|
|
if ( renderEntity.hModel ) {
|
|
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
|
|
animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
|
|
} else {
|
|
renderEntity.customSkin = NULL;
|
|
renderEntity.callback = NULL;
|
|
renderEntity.numJoints = 0;
|
|
renderEntity.joints = NULL;
|
|
}
|
|
|
|
// hide the model until an animation is played
|
|
Hide();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetGlobalJointTransform
|
|
|
|
This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
|
|
================
|
|
*/
|
|
bool idWeapon::GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) {
|
|
if ( viewModel ) {
|
|
// view model
|
|
if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
|
|
offset = offset * viewWeaponAxis + viewWeaponOrigin;
|
|
axis = axis * viewWeaponAxis;
|
|
return true;
|
|
}
|
|
} else {
|
|
// world model
|
|
if ( worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
|
|
offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis();
|
|
axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis();
|
|
return true;
|
|
}
|
|
}
|
|
offset = viewWeaponOrigin;
|
|
axis = viewWeaponAxis;
|
|
return false;
|
|
}
|
|
|
|
// Carl: dual wielding, check which hand, if any, the weapon is in
|
|
int idWeapon::GetHand()
|
|
{
|
|
if ( !owner )
|
|
return -1;
|
|
for( int h = 0; h < 2; h++ )
|
|
{
|
|
if( owner->hands[ h ].weapon.GetEntity() == this )
|
|
return h;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetMuzzlePositionWithHacks
|
|
|
|
Some weapons that have a barrel joint either have it pointing in the wrong
|
|
direction (rocket launcher), or don't animate it properly (pistol).
|
|
|
|
For good 3D TV / head mounted display work, we need to display a laser sight
|
|
in the world.
|
|
|
|
Fixing the animated meshes would be ideal, but hacking it in code is
|
|
the pragmatic move right now.
|
|
|
|
Returns false for hands, grenades, and chainsaw.
|
|
================
|
|
*/
|
|
bool idWeapon::GetMuzzlePositionWithHacks( idVec3& origin, idMat3& axis )
|
|
{
|
|
|
|
static weapon_t currentWeap = WEAPON_NONE;
|
|
idVec3 discardedOrigin;
|
|
|
|
origin = playerViewOrigin;
|
|
axis = playerViewAxis;
|
|
|
|
currentWeap = IdentifyWeapon();
|
|
|
|
switch ( currentWeap )
|
|
{
|
|
case WEAPON_NONE:
|
|
case WEAPON_FISTS:
|
|
case WEAPON_SOULCUBE:
|
|
case WEAPON_PDA:
|
|
origin = commonVr->lastViewOrigin; // Koz fixme set the origin and axis to the players view
|
|
axis = commonVr->lastViewAxis;
|
|
return false;
|
|
break;
|
|
|
|
case WEAPON_HANDGRENADE:
|
|
origin = viewWeaponOrigin; // Koz fixme set the origin and axis to the weapon default
|
|
axis = viewWeaponAxis;
|
|
return false;
|
|
break;
|
|
|
|
case WEAPON_CHAINSAW:
|
|
{
|
|
if ( barrelJointView == INVALID_JOINT )
|
|
{
|
|
origin = viewWeaponOrigin; // Koz fixme set the origin and axis to the weapon default
|
|
axis = viewWeaponAxis;
|
|
return false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
GetGlobalJointTransform( true, barrelJointView, origin, axis );
|
|
std::swap( axis[0], axis[2] ); // barrel joint points right, rotate &
|
|
axis[0] = -axis[0]; // make it point forward.
|
|
|
|
// the barrel joint is not actually aligned with the blade of the chainsaw.
|
|
// Move the origin to align with the tip of the blade.
|
|
// fixme -34 forward is a hack to compensate for an overly long
|
|
// melee range value in VR. Otherwise you can saw something 3 feet away
|
|
// from the tip of the blade.
|
|
// should probably change the weapon def instead of using this mess.
|
|
idVec3 move( -34.0f, 2.0f, -6.0f ); // fwd,up,right
|
|
origin += move * axis;
|
|
|
|
}
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( barrelJointView != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, barrelJointView, origin, axis );
|
|
}
|
|
else if ( guiLightJointView != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, guiLightJointView, origin, axis );
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch ( currentWeap )
|
|
{
|
|
case WEAPON_PISTOL:
|
|
{
|
|
// muzzle doesn't animate during firing, Bod does
|
|
const jointHandle_t bodJoint = animator.GetJointHandle( "Bod" );
|
|
GetGlobalJointTransform( true, bodJoint, discardedOrigin, axis );
|
|
break;
|
|
}
|
|
|
|
case WEAPON_ROCKETLAUNCHER:
|
|
// joint doesn't point straight, so rotate it
|
|
std::swap( axis[0], axis[2] );
|
|
break;
|
|
|
|
case WEAPON_SHOTGUN:
|
|
{
|
|
// joint doesn't point straight, so rotate it
|
|
//const jointHandle_t bodJoint = animator.GetJointHandle( "trigger" );
|
|
const jointHandle_t bodJoint = animator.GetJointHandle( "body" );
|
|
GetGlobalJointTransform( true, bodJoint, discardedOrigin, axis );
|
|
std::swap( axis[0], axis[2] );
|
|
axis[0] = -axis[0];
|
|
break;
|
|
}
|
|
|
|
case WEAPON_PLASMAGUN:
|
|
{
|
|
// the barrel of the plasma rifle is angled down by default, bring it up a little so it shoots straight.
|
|
const idMat3 adj = idAngles( 0.0f, 4.0f, 0.0f ).ToMat3();
|
|
axis = adj * axis;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetProjectileLaunchOriginAndAxis
|
|
================
|
|
*/
|
|
void idWeapon::GetProjectileLaunchOriginAndAxis( idVec3& origin, idMat3& axis )
|
|
{
|
|
assert( owner != NULL );
|
|
if ( game->isVR )
|
|
{
|
|
static weapon_t curWeap = WEAPON_NONE;
|
|
|
|
// Koz debug common->Printf( "GPLOAA getting muzzle position w/ hacks\n" ); // Koz delete me
|
|
GetMuzzlePositionWithHacks( origin, axis );
|
|
|
|
curWeap = IdentifyWeapon();
|
|
|
|
switch ( curWeap )
|
|
{
|
|
case WEAPON_BFG:
|
|
{
|
|
// BFG pitches up when fired before the projectile is launched.
|
|
// Hack to correct pitch so projectile doesn't shoot high.
|
|
static idMat3 bfgCorrection = idAngles( 0.0, -15.0, 0.0 ).ToMat3();
|
|
axis = bfgCorrection * axis;
|
|
|
|
}
|
|
break;
|
|
|
|
case WEAPON_HANDGRENADE:
|
|
{
|
|
// if using motion controls, the muzzle axis should be the tracked direction of
|
|
// hand movement, not the barrel axis. (unless the controller is mounted on something like a topshot, then you have a grenade launcher.)
|
|
if ( commonVr->VR_USE_MOTION_CONTROLS && !vr_mountedWeaponController.GetBool() )
|
|
{
|
|
axis = owner->hands[GetHand()].throwDirection.ToMat3();
|
|
break;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
// calculate the muzzle position
|
|
if( barrelJointView != INVALID_JOINT && projectileDict.GetBool( "launchFromBarrel" ) )
|
|
{
|
|
// there is an explicit joint for the muzzle
|
|
// GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
|
|
GetMuzzlePositionWithHacks( origin, axis );
|
|
}
|
|
else
|
|
{
|
|
// go straight out of the view
|
|
origin = playerViewOrigin;
|
|
axis = playerViewAxis;
|
|
}
|
|
|
|
axis = playerViewAxis; // Fix for plasma rifle not firing correctly on initial shot of a burst fire
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::SetPushVelocity
|
|
================
|
|
*/
|
|
void idWeapon::SetPushVelocity( const idVec3 &pushVelocity ) {
|
|
this->pushVelocity = pushVelocity;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
State control/player interface
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::Think
|
|
================
|
|
*/
|
|
void idWeapon::Think( void ) {
|
|
// do nothing because the present is called from the player through PresentWeapon
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Raise
|
|
================
|
|
*/
|
|
void idWeapon::Raise( void ) {
|
|
if ( isLinked ) {
|
|
WEAPON_RAISEWEAPON = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::PutAway
|
|
================
|
|
*/
|
|
void idWeapon::PutAway( void ) {
|
|
hasBloodSplat = false;
|
|
if ( isLinked ) {
|
|
WEAPON_LOWERWEAPON = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Reload
|
|
NOTE: this is only for impulse-triggered reload, auto reload is scripted
|
|
================
|
|
*/
|
|
void idWeapon::Reload( void ) {
|
|
if ( isLinked ) {
|
|
WEAPON_RELOAD = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::LowerWeapon
|
|
================
|
|
*/
|
|
void idWeapon::LowerWeapon( void ) {
|
|
|
|
if ( game->isVR && commonVr->handInGui ) return;// Koz never lower weapon if hand is in gui
|
|
if ( !owner->GuiActive() && !gameLocal.inCinematic ) return;
|
|
|
|
if ( !hide ) {
|
|
hideStart = 0.0f;
|
|
hideEnd = hideDistance;
|
|
if ( gameLocal.time - hideStartTime < hideTime ) {
|
|
hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
|
|
} else {
|
|
hideStartTime = gameLocal.time;
|
|
}
|
|
hide = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::RaiseWeapon
|
|
================
|
|
*/
|
|
void idWeapon::RaiseWeapon( void ) {
|
|
Show();
|
|
|
|
if ( hide ) {
|
|
hideStart = hideDistance;
|
|
hideEnd = 0.0f;
|
|
if ( gameLocal.time - hideStartTime < hideTime ) {
|
|
hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
|
|
} else {
|
|
hideStartTime = gameLocal.time;
|
|
}
|
|
hide = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::HideWeapon
|
|
================
|
|
*/
|
|
void idWeapon::HideWeapon( void ) {
|
|
Hide();
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
muzzleFlashEnd = 0;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowWeapon
|
|
================
|
|
*/
|
|
void idWeapon::ShowWeapon( void ) {
|
|
Show();
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->Show();
|
|
}
|
|
if ( lightOn ) {
|
|
MuzzleFlashLight();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::HideWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::HideWorldModel( void ) {
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowWorldModel
|
|
================
|
|
*/
|
|
void idWeapon::ShowWorldModel( void ) {
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->Show();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::OwnerDied
|
|
================
|
|
*/
|
|
void idWeapon::OwnerDied( void ) {
|
|
if ( isLinked ) {
|
|
SetState( "OwnerDied", 0 );
|
|
thread->Execute();
|
|
}
|
|
|
|
Hide();
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->Hide();
|
|
}
|
|
|
|
// don't clear the weapon immediately since the owner might have killed himself by firing the weapon
|
|
// within the current stack frame
|
|
PostEventMS( &EV_Weapon_Clear, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::BeginAttack
|
|
================
|
|
*/
|
|
void idWeapon::BeginAttack( void ) {
|
|
if ( status != WP_OUTOFAMMO ) {
|
|
lastAttack = gameLocal.time;
|
|
}
|
|
|
|
if ( !isLinked ) {
|
|
return;
|
|
}
|
|
|
|
if ( !WEAPON_ATTACK ) {
|
|
if ( sndHum ) {
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
}
|
|
}
|
|
WEAPON_ATTACK = true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::EndAttack
|
|
================
|
|
*/
|
|
void idWeapon::EndAttack( void ) {
|
|
if ( !WEAPON_ATTACK.IsLinked() ) {
|
|
return;
|
|
}
|
|
if ( WEAPON_ATTACK ) {
|
|
WEAPON_ATTACK = false;
|
|
if ( sndHum ) {
|
|
StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::isReady
|
|
================
|
|
*/
|
|
bool idWeapon::IsReady( void ) const {
|
|
return !hide && !IsHidden() && ( ( status == WP_RELOAD ) || ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::IsReloading
|
|
================
|
|
*/
|
|
bool idWeapon::IsReloading( void ) const {
|
|
return ( status == WP_RELOAD );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::IsHolstered
|
|
================
|
|
*/
|
|
bool idWeapon::IsHolstered( void ) const {
|
|
return ( status == WP_HOLSTERED );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ShowCrosshair
|
|
================
|
|
*/
|
|
bool idWeapon::ShowCrosshair( void ) const {
|
|
return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::CanDrop
|
|
=====================
|
|
*/
|
|
bool idWeapon::CanDrop( void ) const {
|
|
if ( !weaponDef || !worldModel.GetEntity() ) {
|
|
return false;
|
|
}
|
|
const char *classname = weaponDef->dict.GetString( "def_dropItem" );
|
|
if ( !classname[ 0 ] ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::WeaponStolen
|
|
================
|
|
*/
|
|
void idWeapon::WeaponStolen( void ) {
|
|
assert( !gameLocal.isClient );
|
|
if ( projectileEnt ) {
|
|
if ( isLinked ) {
|
|
SetState( "WeaponStolen", 0 );
|
|
thread->Execute();
|
|
}
|
|
projectileEnt = NULL;
|
|
}
|
|
|
|
// set to holstered so we can switch weapons right away
|
|
status = WP_HOLSTERED;
|
|
|
|
HideWeapon();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::DropItem
|
|
=====================
|
|
*/
|
|
idEntity * idWeapon::DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died ) {
|
|
if ( !weaponDef || !worldModel.GetEntity() ) {
|
|
return NULL;
|
|
}
|
|
if ( !allowDrop ) {
|
|
return NULL;
|
|
}
|
|
const char *classname = weaponDef->dict.GetString( "def_dropItem" );
|
|
if ( !classname[0] ) {
|
|
return NULL;
|
|
}
|
|
StopSound( SND_CHANNEL_BODY, true );
|
|
StopSound( SND_CHANNEL_BODY3, true );
|
|
|
|
return idMoveableItem::DropItem( classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Script state management
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::SetState
|
|
=====================
|
|
*/
|
|
void idWeapon::SetState( const char *statename, int blendFrames ) {
|
|
const function_t *func;
|
|
|
|
if ( !isLinked ) {
|
|
return;
|
|
}
|
|
|
|
func = scriptObject.GetFunction( statename );
|
|
if ( !func ) {
|
|
assert( 0 );
|
|
gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
|
|
}
|
|
|
|
thread->CallFunction( this, func, true );
|
|
state = statename;
|
|
|
|
animBlendFrames = blendFrames;
|
|
if ( g_debugWeapon.GetBool() ) {
|
|
gameLocal.Printf( "%d: weapon state : %s\n", gameLocal.time, statename );
|
|
}
|
|
|
|
idealState = "";
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Particles/Effects
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateNozzelFx
|
|
================
|
|
*/
|
|
void idWeapon::UpdateNozzleFx( void ) {
|
|
if ( !nozzleFx ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// shader parms
|
|
//
|
|
int la = gameLocal.time - lastAttack + 1;
|
|
float s = 1.0f;
|
|
float l = 0.0f;
|
|
if ( la < nozzleFxFade ) {
|
|
s = ((float)la / nozzleFxFade);
|
|
l = 1.0f - s;
|
|
}
|
|
renderEntity.shaderParms[5] = s;
|
|
renderEntity.shaderParms[6] = l;
|
|
|
|
if ( ventLightJointView == INVALID_JOINT ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// vent light
|
|
//
|
|
if ( nozzleGlowHandle == -1 ) {
|
|
memset(&nozzleGlow, 0, sizeof(nozzleGlow));
|
|
if ( owner ) {
|
|
nozzleGlow.allowLightInViewID = owner->entityNumber+1;
|
|
}
|
|
nozzleGlow.pointLight = true;
|
|
nozzleGlow.noShadows = true;
|
|
nozzleGlow.lightRadius.x = nozzleGlowRadius;
|
|
nozzleGlow.lightRadius.y = nozzleGlowRadius;
|
|
nozzleGlow.lightRadius.z = nozzleGlowRadius;
|
|
nozzleGlow.shader = nozzleGlowShader;
|
|
nozzleGlow.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
|
|
nozzleGlow.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
|
|
GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
|
|
nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
|
|
}
|
|
|
|
GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
|
|
|
|
nozzleGlow.shaderParms[ SHADERPARM_RED ] = nozzleGlowColor.x * s;
|
|
nozzleGlow.shaderParms[ SHADERPARM_GREEN ] = nozzleGlowColor.y * s;
|
|
nozzleGlow.shaderParms[ SHADERPARM_BLUE ] = nozzleGlowColor.z * s;
|
|
gameRenderWorld->UpdateLightDef(nozzleGlowHandle, &nozzleGlow);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::BloodSplat
|
|
================
|
|
*/
|
|
bool idWeapon::BloodSplat( float size ) {
|
|
float s, c;
|
|
idMat3 localAxis, axistemp;
|
|
idVec3 localOrigin, normal;
|
|
|
|
if ( hasBloodSplat ) {
|
|
return true;
|
|
}
|
|
|
|
hasBloodSplat = true;
|
|
|
|
if ( modelDefHandle < 0 ) {
|
|
return false;
|
|
}
|
|
|
|
if ( !GetGlobalJointTransform( true, ejectJointView, localOrigin, localAxis ) ) {
|
|
return false;
|
|
}
|
|
|
|
localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f;
|
|
localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f;
|
|
localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f;
|
|
|
|
normal = idVec3( gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1 );
|
|
normal.Normalize();
|
|
|
|
idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
|
|
|
|
localAxis[2] = -normal;
|
|
localAxis[2].NormalVectors( axistemp[0], axistemp[1] );
|
|
localAxis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
|
|
localAxis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
|
|
|
|
localAxis[0] *= 1.0f / size;
|
|
localAxis[1] *= 1.0f / size;
|
|
|
|
idPlane localPlane[2];
|
|
|
|
localPlane[0] = localAxis[0];
|
|
localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f;
|
|
|
|
localPlane[1] = localAxis[1];
|
|
localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f;
|
|
|
|
const idMaterial *mtr = declManager->FindMaterial( "textures/decals/duffysplatgun" );
|
|
|
|
gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Visual presentation
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::MuzzleRise
|
|
|
|
The machinegun and chaingun will incrementally back up as they are being fired
|
|
================
|
|
*/
|
|
void idWeapon::MuzzleRise( idVec3 &origin, idMat3 &axis ) {
|
|
int time;
|
|
float amount;
|
|
idAngles ang;
|
|
idVec3 offset;
|
|
|
|
time = kick_endtime - gameLocal.time;
|
|
if ( time <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
if ( muzzle_kick_maxtime <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
if ( time > muzzle_kick_maxtime ) {
|
|
time = muzzle_kick_maxtime;
|
|
}
|
|
|
|
amount = ( float )time / ( float )muzzle_kick_maxtime;
|
|
ang = muzzle_kick_angles * amount;
|
|
offset = muzzle_kick_offset * amount;
|
|
|
|
origin = origin - axis * offset;
|
|
axis = ang.ToMat3() * axis;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ConstructScriptObject
|
|
|
|
Called during idEntity::Spawn. Calls the constructor on the script object.
|
|
Can be overridden by subclasses when a thread doesn't need to be allocated.
|
|
================
|
|
*/
|
|
idThread *idWeapon::ConstructScriptObject( void ) {
|
|
const function_t *constructor;
|
|
|
|
thread->EndThread();
|
|
|
|
// call script object's constructor
|
|
constructor = scriptObject.GetConstructor();
|
|
if ( !constructor ) {
|
|
gameLocal.Error( "Missing constructor on '%s' for weapon", scriptObject.GetTypeName() );
|
|
}
|
|
|
|
// init the script object's data
|
|
scriptObject.ClearObject();
|
|
thread->CallFunction( this, constructor, true );
|
|
thread->Execute();
|
|
|
|
return thread;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::DeconstructScriptObject
|
|
|
|
Called during idEntity::~idEntity. Calls the destructor on the script object.
|
|
Can be overridden by subclasses when a thread doesn't need to be allocated.
|
|
Not called during idGameLocal::MapShutdown.
|
|
================
|
|
*/
|
|
void idWeapon::DeconstructScriptObject( void ) {
|
|
const function_t *destructor;
|
|
|
|
if ( !thread ) {
|
|
return;
|
|
}
|
|
|
|
// don't bother calling the script object's destructor on map shutdown
|
|
if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
|
|
return;
|
|
}
|
|
|
|
thread->EndThread();
|
|
|
|
// call script object's destructor
|
|
destructor = scriptObject.GetDestructor();
|
|
if ( destructor ) {
|
|
// start a thread that will run immediately and end
|
|
thread->CallFunction( this, destructor, true );
|
|
thread->Execute();
|
|
thread->EndThread();
|
|
}
|
|
|
|
// clear out the object's memory
|
|
scriptObject.ClearObject();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::UpdateScript
|
|
================
|
|
*/
|
|
void idWeapon::UpdateScript( void ) {
|
|
int count;
|
|
|
|
if ( !isLinked ) {
|
|
return;
|
|
}
|
|
|
|
// only update the script on new frames
|
|
if ( !gameLocal.isNewFrame ) {
|
|
return;
|
|
}
|
|
|
|
if ( idealState.Length() ) {
|
|
SetState( idealState, animBlendFrames );
|
|
}
|
|
|
|
// update script state, which may call Event_LaunchProjectiles, among other things
|
|
count = 10;
|
|
while( ( thread->Execute() || idealState.Length() ) && count-- ) {
|
|
// happens for weapons with no clip (like grenades)
|
|
if ( idealState.Length() ) {
|
|
SetState( idealState, animBlendFrames );
|
|
}
|
|
}
|
|
|
|
WEAPON_RELOAD = false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AlertMonsters
|
|
================
|
|
*/
|
|
void idWeapon::AlertMonsters( void ) {
|
|
trace_t tr;
|
|
idEntity *ent;
|
|
idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
|
|
|
|
gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
|
|
if ( g_debugWeapon.GetBool() ) {
|
|
gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
|
|
gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
|
|
}
|
|
|
|
if ( tr.fraction < 1.0f ) {
|
|
ent = gameLocal.GetTraceEntity( tr );
|
|
if ( ent->IsType( idAI::Type ) ) {
|
|
static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
|
|
} else if ( ent->IsType( idTrigger::Type ) ) {
|
|
ent->Signal( SIG_TOUCH );
|
|
ent->ProcessEvent( &EV_Touch, owner, &tr );
|
|
}
|
|
}
|
|
|
|
// jitter the trace to try to catch cases where a trace down the center doesn't hit the monster
|
|
end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f );
|
|
end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f );
|
|
gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
|
|
if ( g_debugWeapon.GetBool() ) {
|
|
gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
|
|
gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
|
|
}
|
|
|
|
if ( tr.fraction < 1.0f ) {
|
|
ent = gameLocal.GetTraceEntity( tr );
|
|
if ( ent->IsType( idAI::Type ) ) {
|
|
static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
|
|
} else if ( ent->IsType( idTrigger::Type ) ) {
|
|
ent->Signal( SIG_TOUCH );
|
|
ent->ProcessEvent( &EV_Touch, owner, &tr );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::CalculateHideRise
|
|
================
|
|
*/
|
|
void idWeapon::CalculateHideRise( idVec3& origin, idMat3& axis )
|
|
{
|
|
// hide offset is for dropping the gun when approaching a GUI or NPC
|
|
// This is simpler to manage than doing the weapon put-away animation
|
|
if ( gameLocal.time - hideStartTime < hideTime )
|
|
{
|
|
float frac = (float)(gameLocal.time - hideStartTime) / (float)hideTime;
|
|
if ( hideStart < hideEnd )
|
|
{
|
|
frac = 1.0f - frac;
|
|
frac = 1.0f - frac * frac;
|
|
}
|
|
else
|
|
{
|
|
frac = frac * frac;
|
|
}
|
|
hideOffset = hideStart + (hideEnd - hideStart) * frac;
|
|
}
|
|
else
|
|
{
|
|
hideOffset = hideEnd;
|
|
if ( hide && disabled )
|
|
{
|
|
Hide();
|
|
}
|
|
}
|
|
//viewWeaponOrigin += hideOffset * viewWeaponAxis[2]; // Koz adjust the gui pointer by this offset later
|
|
//viewWeaponOrigin.z += hideOffset;
|
|
origin.z += hideOffset;
|
|
// kick up based on repeat firing
|
|
MuzzleRise( origin, axis );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::PresentWeapon
|
|
================
|
|
*/
|
|
void idWeapon::PresentWeapon( bool showViewModel, int hand ) {
|
|
|
|
worldModel.GetEntity()->GetRenderEntity()->allowSurfaceInViewID = -1;
|
|
|
|
playerViewOrigin = owner->firstPersonViewOrigin;
|
|
playerViewAxis = owner->firstPersonViewAxis;
|
|
weapon_t currentWeapon = WEAPON_NONE;
|
|
currentWeapon = IdentifyWeapon();
|
|
|
|
if ( isPlayerFlashlight )
|
|
{
|
|
viewWeaponOrigin = playerViewOrigin;
|
|
viewWeaponAxis = playerViewAxis;
|
|
owner->CalculateViewFlashlightPos( viewWeaponOrigin, viewWeaponAxis, flashlightOffsets[int( currentWeapon )] );
|
|
|
|
}
|
|
else if ( isPlayerLeftHand ) // Koz left hand
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
// calculate weapon position based on player movement bobbing
|
|
owner->CalculateViewWeaponPos( hand, viewWeaponOrigin, viewWeaponAxis );
|
|
// Koz hide weapon and muzzlerise was here, now called as weapon::CalculateHideRise in player->CalculateViewWeaponPosition to allow hand animations
|
|
}
|
|
|
|
// set the physics position and orientation
|
|
GetPhysics()->SetOrigin( viewWeaponOrigin );
|
|
GetPhysics()->SetAxis( viewWeaponAxis );
|
|
|
|
UpdateVisuals();
|
|
|
|
// update the weapon script
|
|
UpdateScript();
|
|
|
|
UpdateGUI();
|
|
|
|
// update animation
|
|
UpdateAnimation();
|
|
|
|
// in VR don't suppress drawing the player's body
|
|
// also show the viewmodel
|
|
if ( (hide && disabled) ) // hide the weapon if in a cinematic
|
|
{
|
|
renderEntity.allowSurfaceInViewID = -1;
|
|
showViewModel = false;
|
|
} //show the viewmodel in all views - flashlight visibilty set in calcViewFlashPosition
|
|
else if (!isPlayerFlashlight && !commonVr->handInGui ) renderEntity.allowSurfaceInViewID = 0; // Koz fixme
|
|
owner->GetRenderEntity()->suppressSurfaceInViewID = 0;
|
|
|
|
// crunch the depth range so it never pokes into walls this breaks the machine gun gui
|
|
renderEntity.weaponDepthHack = g_useWeaponDepthHack.GetBool();
|
|
|
|
// present the model
|
|
if ( showViewModel )
|
|
{
|
|
Present();
|
|
}
|
|
else
|
|
{
|
|
FreeModelDef();
|
|
}
|
|
|
|
if ( worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity() )
|
|
{
|
|
// deal with the third-person visible world model
|
|
// don't show shadows of the world model in first person
|
|
if ( g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() || game->isVR ) // Koz fixme only in vr
|
|
{
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0;
|
|
}
|
|
else
|
|
{
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber + 1;
|
|
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
|
|
}
|
|
}
|
|
|
|
if ( nozzleFx )
|
|
{
|
|
UpdateNozzleFx();
|
|
}
|
|
|
|
// muzzle smoke
|
|
if ( showViewModel && !disabled && weaponSmoke && (weaponSmokeStartTime != 0) )
|
|
{
|
|
// use the barrel joint if available
|
|
|
|
if ( smokeJointView != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, smokeJointView, muzzleOrigin, muzzleAxis );
|
|
}
|
|
else if ( barrelJointView != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
|
|
}
|
|
else
|
|
{
|
|
// default to going straight out the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
// spit out a particle
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis ) )
|
|
{
|
|
weaponSmokeStartTime = (continuousSmoke) ? gameLocal.time : 0;
|
|
}
|
|
}
|
|
|
|
if ( showViewModel && strikeSmoke && strikeSmokeStartTime != 0 )
|
|
{
|
|
// spit out a particle
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis ) )
|
|
{
|
|
strikeSmokeStartTime = 0;
|
|
}
|
|
}
|
|
|
|
if ( showViewModel && !hide )
|
|
{
|
|
for ( int i = 0; i < weaponParticles.Num(); i++ )
|
|
{
|
|
WeaponParticle_t* part = weaponParticles.GetIndex( i );
|
|
|
|
if ( part->active )
|
|
{
|
|
if ( part->smoke )
|
|
{
|
|
if ( part->joint != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, part->joint, muzzleOrigin, muzzleAxis );
|
|
}
|
|
else
|
|
{
|
|
// default to going straight out the view
|
|
muzzleOrigin = playerViewOrigin;
|
|
muzzleAxis = playerViewAxis;
|
|
}
|
|
if ( !gameLocal.smokeParticles->EmitSmoke( part->particle, part->startTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis) )
|
|
{
|
|
part->active = false; // all done
|
|
part->startTime = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( part->emitter != NULL )
|
|
{
|
|
//Manually update the position of the emitter so it follows the weapon
|
|
renderEntity_t* rendEnt = part->emitter->GetRenderEntity();
|
|
GetGlobalJointTransform( true, part->joint, rendEnt->origin, rendEnt->axis );
|
|
|
|
if ( part->emitter->GetModelDefHandle() != -1 )
|
|
{
|
|
gameRenderWorld->UpdateEntityDef( part->emitter->GetModelDefHandle(), rendEnt );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < weaponLights.Num(); i++ )
|
|
{
|
|
WeaponLight_t* light = weaponLights.GetIndex( i );
|
|
|
|
if ( light->active )
|
|
{
|
|
|
|
GetGlobalJointTransform( true, light->joint, light->light.origin, light->light.axis );
|
|
if ( (light->lightHandle != -1) )
|
|
{
|
|
gameRenderWorld->UpdateLightDef( light->lightHandle, &light->light );
|
|
}
|
|
else
|
|
{
|
|
light->lightHandle = gameRenderWorld->AddLightDef( &light->light );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove the muzzle flash light when it's done
|
|
if ( (!lightOn && (gameLocal.time >= muzzleFlashEnd)) || IsHidden() )
|
|
{
|
|
if ( muzzleFlashHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeLightDef( muzzleFlashHandle );
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if ( worldMuzzleFlashHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
}
|
|
|
|
// update the muzzle flash light, so it moves with the gun
|
|
if ( muzzleFlashHandle != -1 )
|
|
{
|
|
UpdateFlashPosition();
|
|
gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
|
|
gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
|
|
|
|
// wake up monsters with the flashlight
|
|
if ( lightOn && !owner->fl.notarget )
|
|
{
|
|
AlertMonsters();
|
|
}
|
|
}
|
|
|
|
// update the gui light
|
|
if ( guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT )
|
|
{
|
|
GetGlobalJointTransform( true, guiLightJointView, guiLight.origin, guiLight.axis );
|
|
|
|
if ( (guiLightHandle != -1) )
|
|
{
|
|
gameRenderWorld->UpdateLightDef( guiLightHandle, &guiLight );
|
|
}
|
|
else
|
|
{
|
|
guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
|
|
}
|
|
}
|
|
|
|
if ( status != WP_READY && sndHum )
|
|
{
|
|
StopSound( SND_CHANNEL_BODY, false );
|
|
}
|
|
|
|
UpdateSound();
|
|
|
|
// constant rumble...
|
|
float highMagnitude = weaponDef->dict.GetFloat( "controllerConstantShakeHighMag" );
|
|
int highDuration = weaponDef->dict.GetInt( "controllerConstantShakeHighTime" );
|
|
float lowMagnitude = weaponDef->dict.GetFloat( "controllerConstantShakeLowMag" );
|
|
int lowDuration = weaponDef->dict.GetInt( "controllerConstantShakeLowTime" );
|
|
|
|
if ( vr_rumbleChainsaw.GetBool() || !commonVr->VR_USE_MOTION_CONTROLS )
|
|
owner->hands[hand].SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::EnterCinematic
|
|
================
|
|
*/
|
|
void idWeapon::EnterCinematic( void ) {
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
|
|
if ( isLinked ) {
|
|
SetState( "EnterCinematic", 0 );
|
|
thread->Execute();
|
|
|
|
WEAPON_ATTACK = false;
|
|
WEAPON_RELOAD = false;
|
|
WEAPON_NETRELOAD = false;
|
|
WEAPON_NETENDRELOAD = false;
|
|
if(WEAPON_NETFIRING.IsLinked())
|
|
WEAPON_NETFIRING = false;
|
|
WEAPON_RAISEWEAPON = false;
|
|
WEAPON_LOWERWEAPON = false;
|
|
}
|
|
|
|
disabled = true;
|
|
|
|
LowerWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ExitCinematic
|
|
================
|
|
*/
|
|
void idWeapon::ExitCinematic( void ) {
|
|
disabled = false;
|
|
|
|
if ( isLinked ) {
|
|
SetState( "ExitCinematic", 0 );
|
|
thread->Execute();
|
|
}
|
|
|
|
RaiseWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::NetCatchup
|
|
================
|
|
*/
|
|
void idWeapon::NetCatchup( void ) {
|
|
if ( isLinked ) {
|
|
SetState( "NetCatchup", 0 );
|
|
thread->Execute();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetZoomFov
|
|
================
|
|
*/
|
|
int idWeapon::GetZoomFov( void ) {
|
|
return zoomFov;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponAngleOffsets
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponAngleOffsets( int *average, float *scale, float *max ) {
|
|
*average = weaponAngleOffsetAverages;
|
|
*scale = weaponAngleOffsetScale;
|
|
*max = weaponAngleOffsetMax;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetWeaponTimeOffsets
|
|
================
|
|
*/
|
|
void idWeapon::GetWeaponTimeOffsets( float *time, float *scale ) {
|
|
*time = weaponOffsetTime;
|
|
*scale = weaponOffsetScale;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Ammo
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoNumForName
|
|
================
|
|
*/
|
|
ammo_t idWeapon::GetAmmoNumForName( const char *ammoname ) {
|
|
int num;
|
|
const idDict *ammoDict;
|
|
|
|
assert( ammoname );
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
|
|
if ( !ammoDict ) {
|
|
gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
|
|
}
|
|
|
|
if ( !ammoname[ 0 ] ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( !ammoDict->GetInt( ammoname, "-1", num ) ) {
|
|
gameLocal.Error( "Unknown ammo type '%s'", ammoname );
|
|
}
|
|
|
|
if ( ( num < 0 ) || ( num >= AMMO_NUMTYPES ) ) {
|
|
gameLocal.Error( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES );
|
|
}
|
|
|
|
return ( ammo_t )num;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoNameForNum
|
|
================
|
|
*/
|
|
const char *idWeapon::GetAmmoNameForNum( ammo_t ammonum ) {
|
|
int i;
|
|
int num;
|
|
const idDict *ammoDict;
|
|
const idKeyValue *kv;
|
|
char text[ 32 ];
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
|
|
if ( !ammoDict ) {
|
|
gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
|
|
}
|
|
|
|
sprintf( text, "%d", ammonum );
|
|
|
|
num = ammoDict->GetNumKeyVals();
|
|
for( i = 0; i < num; i++ ) {
|
|
kv = ammoDict->GetKeyVal( i );
|
|
if ( kv->GetValue() == text ) {
|
|
return kv->GetKey();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoPickupNameForNum
|
|
================
|
|
*/
|
|
const char *idWeapon::GetAmmoPickupNameForNum( ammo_t ammonum ) {
|
|
int i;
|
|
int num;
|
|
const idDict *ammoDict;
|
|
const idKeyValue *kv;
|
|
|
|
ammoDict = gameLocal.FindEntityDefDict( "ammo_names", false );
|
|
if ( !ammoDict ) {
|
|
gameLocal.Error( "Could not find entity definition for 'ammo_names'\n" );
|
|
}
|
|
|
|
const char *name = GetAmmoNameForNum( ammonum );
|
|
|
|
if ( name && *name ) {
|
|
num = ammoDict->GetNumKeyVals();
|
|
for( i = 0; i < num; i++ ) {
|
|
kv = ammoDict->GetKeyVal( i );
|
|
if ( idStr::Icmp( kv->GetKey(), name) == 0 ) {
|
|
return kv->GetValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoAvailable
|
|
================
|
|
*/
|
|
int idWeapon::AmmoAvailable( void ) const {
|
|
if ( owner ) {
|
|
return owner->inventory.HasAmmo( ammoType, ammoRequired );
|
|
} else {
|
|
if( g_infiniteAmmo.GetBool() )
|
|
{
|
|
return 10; // arbitrary number, just so whatever's calling thinks there's sufficient ammo...
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
================
|
|
idWeapon::AmmoCount
|
|
|
|
Returns the total number of rounds regardless of the required ammo
|
|
================
|
|
*/
|
|
int idWeapon::AmmoCount() const
|
|
{
|
|
|
|
if( owner )
|
|
{
|
|
return owner->inventory.HasAmmo( ammoType, 1 );
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoInClip
|
|
================
|
|
*/
|
|
int idWeapon::AmmoInClip( void ) const {
|
|
return ammoClip;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ResetAmmoClip
|
|
================
|
|
*/
|
|
void idWeapon::ResetAmmoClip( void ) {
|
|
ammoClip = -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::GetAmmoType
|
|
================
|
|
*/
|
|
ammo_t idWeapon::GetAmmoType( void ) const {
|
|
return ammoType;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ClipSize
|
|
================
|
|
*/
|
|
int idWeapon::ClipSize( void ) const {
|
|
return clipSize;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::LowAmmo
|
|
================
|
|
*/
|
|
int idWeapon::LowAmmo() const {
|
|
return lowAmmo;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::AmmoRequired
|
|
================
|
|
*/
|
|
int idWeapon::AmmoRequired( void ) const {
|
|
return ammoRequired;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::WriteToSnapshot
|
|
================
|
|
*/
|
|
void idWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const {
|
|
msg.WriteBits( ammoClip, ASYNC_PLAYER_INV_CLIP_BITS );
|
|
msg.WriteBits( worldModel.GetSpawnId(), 32 );
|
|
msg.WriteBits( lightOn, 1 );
|
|
msg.WriteBits( isFiring ? 1 : 0, 1 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ReadFromSnapshot
|
|
================
|
|
*/
|
|
void idWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) {
|
|
ammoClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
|
|
worldModel.SetSpawnId( msg.ReadBits( 32 ) );
|
|
bool snapLight = msg.ReadBits( 1 ) != 0;
|
|
isFiring = msg.ReadBits( 1 ) != 0;
|
|
|
|
// WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client
|
|
if ( owner && gameLocal.localClientNum != owner->entityNumber && WEAPON_NETFIRING.IsLinked() ) {
|
|
|
|
// immediately go to the firing state so we don't skip fire animations
|
|
if ( !WEAPON_NETFIRING && isFiring ) {
|
|
idealState = "Fire";
|
|
}
|
|
|
|
// immediately switch back to idle
|
|
if ( WEAPON_NETFIRING && !isFiring ) {
|
|
idealState = "Idle";
|
|
}
|
|
|
|
WEAPON_NETFIRING = isFiring;
|
|
}
|
|
|
|
if ( snapLight != lightOn ) {
|
|
Reload();
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::RemoveMuzzleFlashlight
|
|
================
|
|
*/
|
|
void idWeapon::RemoveMuzzleFlashlight()
|
|
{
|
|
if( muzzleFlashHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeLightDef( muzzleFlashHandle );
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
if( worldMuzzleFlashHandle != -1 )
|
|
{
|
|
gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
|
|
worldMuzzleFlashHandle = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::ClientReceiveEvent
|
|
================
|
|
*/
|
|
bool idWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
|
|
|
|
switch( event ) {
|
|
case EVENT_RELOAD: {
|
|
if ( gameLocal.time - time < 1000 ) {
|
|
if ( WEAPON_NETRELOAD.IsLinked() ) {
|
|
WEAPON_NETRELOAD = true;
|
|
WEAPON_NETENDRELOAD = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
case EVENT_ENDRELOAD: {
|
|
if ( WEAPON_NETENDRELOAD.IsLinked() ) {
|
|
WEAPON_NETENDRELOAD = true;
|
|
}
|
|
return true;
|
|
}
|
|
case EVENT_CHANGESKIN: {
|
|
int index = gameLocal.ClientRemapDecl( DECL_SKIN, msg.ReadInt() );
|
|
renderEntity.customSkin = ( index != -1 ) ? static_cast<const idDeclSkin *>( declManager->DeclByIndex( DECL_SKIN, index ) ) : NULL;
|
|
UpdateVisuals();
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->SetSkin( renderEntity.customSkin );
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return idEntity::ClientReceiveEvent( event, time, msg );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Script events
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_Clear
|
|
===============
|
|
*/
|
|
void idWeapon::Event_Clear( void ) {
|
|
Clear();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_GetOwner
|
|
===============
|
|
*/
|
|
void idWeapon::Event_GetOwner( void ) {
|
|
idThread::ReturnEntity( owner );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponState
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponState( const char *statename, int blendFrames ) {
|
|
const function_t *func;
|
|
|
|
func = scriptObject.GetFunction( statename );
|
|
if ( !func ) {
|
|
assert( 0 );
|
|
gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
|
|
}
|
|
|
|
idealState = statename;
|
|
|
|
if ( !idealState.Icmp( "Fire" ) ) {
|
|
isFiring = true;
|
|
} else {
|
|
isFiring = false;
|
|
}
|
|
|
|
animBlendFrames = blendFrames;
|
|
thread->DoneProcessing();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::FlashlightOn
|
|
================
|
|
*/
|
|
void idWeapon::FlashlightOn()
|
|
{
|
|
const function_t* func;
|
|
|
|
if( !isLinked )
|
|
{
|
|
return;
|
|
}
|
|
|
|
func = scriptObject.GetFunction( "TurnOn" );
|
|
if( !func )
|
|
{
|
|
common->Warning( "Can't find function 'TurnOn' in object '%s'", scriptObject.GetTypeName() );
|
|
return;
|
|
}
|
|
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
idWeapon::FlashlightOff
|
|
================
|
|
*/
|
|
void idWeapon::FlashlightOff()
|
|
{
|
|
const function_t* func;
|
|
|
|
if( !isLinked )
|
|
{
|
|
return;
|
|
}
|
|
|
|
func = scriptObject.GetFunction( "TurnOff" );
|
|
if( !func )
|
|
{
|
|
common->Warning( "Can't find function 'TurnOff' in object '%s'", scriptObject.GetTypeName() );
|
|
return;
|
|
}
|
|
|
|
// use the frameCommandThread since it's safe to use outside of framecommands
|
|
gameLocal.frameCommandThread->CallFunction( this, func, true );
|
|
gameLocal.frameCommandThread->Execute();
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponReady
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponReady( void ) {
|
|
status = WP_READY;
|
|
if ( isLinked ) {
|
|
WEAPON_RAISEWEAPON = false;
|
|
}
|
|
if ( sndHum ) {
|
|
StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponOutOfAmmo
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponOutOfAmmo( void ) {
|
|
status = WP_OUTOFAMMO;
|
|
if ( isLinked ) {
|
|
WEAPON_RAISEWEAPON = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponReloading
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponReloading( void ) {
|
|
status = WP_RELOAD;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponHolstered
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponHolstered( void ) {
|
|
status = WP_HOLSTERED;
|
|
if ( isLinked ) {
|
|
WEAPON_LOWERWEAPON = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponRising
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponRising( void ) {
|
|
status = WP_RISING;
|
|
if ( isLinked ) {
|
|
WEAPON_LOWERWEAPON = false;
|
|
}
|
|
owner->WeaponRisingCallback();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_WeaponLowering
|
|
===============
|
|
*/
|
|
void idWeapon::Event_WeaponLowering( void ) {
|
|
status = WP_LOWERING;
|
|
if ( isLinked ) {
|
|
WEAPON_RAISEWEAPON = false;
|
|
}
|
|
owner->WeaponLoweringCallback();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_UseAmmo
|
|
===============
|
|
*/
|
|
void idWeapon::Event_UseAmmo( int amount ) {
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? amount : ( amount * ammoRequired ) );
|
|
if ( clipSize && ammoRequired ) {
|
|
ammoClip -= powerAmmo ? amount : ( amount * ammoRequired );
|
|
if ( ammoClip < 0 ) {
|
|
ammoClip = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AddToClip
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AddToClip( int amount ) {
|
|
int ammoAvail;
|
|
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
ammoClip += amount;
|
|
if ( ammoClip > clipSize ) {
|
|
ammoClip = clipSize;
|
|
}
|
|
|
|
ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
|
|
if ( ammoClip > ammoAvail ) {
|
|
ammoClip = ammoAvail;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AmmoInClip
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AmmoInClip( void ) {
|
|
int ammo = AmmoInClip();
|
|
idThread::ReturnFloat( ammo );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AmmoAvailable
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AmmoAvailable( void ) {
|
|
int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
|
|
idThread::ReturnFloat( ammoAvail );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_TotalAmmoCount
|
|
===============
|
|
*/
|
|
void idWeapon::Event_TotalAmmoCount( void ) {
|
|
int ammoAvail = owner->inventory.HasAmmo( ammoType, 1 );
|
|
idThread::ReturnFloat( ammoAvail );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_ClipSize
|
|
===============
|
|
*/
|
|
void idWeapon::Event_ClipSize( void ) {
|
|
idThread::ReturnFloat( clipSize );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AutoReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AutoReload( void ) {
|
|
assert( owner );
|
|
if ( gameLocal.isClient ) {
|
|
idThread::ReturnFloat( 0.0f );
|
|
return;
|
|
}
|
|
idThread::ReturnFloat( gameLocal.userInfo[ owner->entityNumber ].GetBool( "ui_autoReload" ) );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_NetReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_NetReload( void ) {
|
|
assert( owner );
|
|
if ( gameLocal.isServer ) {
|
|
ServerSendEvent( EVENT_RELOAD, NULL, false, -1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_NetEndReload
|
|
===============
|
|
*/
|
|
void idWeapon::Event_NetEndReload( void ) {
|
|
assert( owner );
|
|
if ( gameLocal.isServer ) {
|
|
ServerSendEvent( EVENT_ENDRELOAD, NULL, false, -1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_PlayAnim
|
|
===============
|
|
*/
|
|
void idWeapon::Event_PlayAnim( int channel, const char *animname ) {
|
|
int anim;
|
|
|
|
anim = animator.GetAnim( animname );
|
|
if ( !anim ) {
|
|
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
|
|
animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
animDoneTime = 0;
|
|
} else {
|
|
if ( !( owner && owner->GetInfluenceLevel() ) ) {
|
|
Show();
|
|
}
|
|
animator.PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
|
|
if ( worldModel.GetEntity() ) {
|
|
anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
|
|
if ( anim ) {
|
|
worldModel.GetEntity()->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
}
|
|
}
|
|
}
|
|
animBlendFrames = 0;
|
|
idThread::ReturnInt( 0 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_PlayCycle
|
|
===============
|
|
*/
|
|
void idWeapon::Event_PlayCycle( int channel, const char *animname ) {
|
|
int anim;
|
|
|
|
anim = animator.GetAnim( animname );
|
|
if ( !anim ) {
|
|
gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
|
|
animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
animDoneTime = 0;
|
|
} else {
|
|
if ( !( owner && owner->GetInfluenceLevel() ) ) {
|
|
Show();
|
|
}
|
|
animator.CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
|
|
if ( worldModel.GetEntity() ) {
|
|
anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
|
|
worldModel.GetEntity()->GetAnimator()->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
|
|
}
|
|
}
|
|
animBlendFrames = 0;
|
|
idThread::ReturnInt( 0 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_AnimDone
|
|
===============
|
|
*/
|
|
void idWeapon::Event_AnimDone( int channel, int blendFrames ) {
|
|
if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
|
|
idThread::ReturnInt( true );
|
|
} else {
|
|
idThread::ReturnInt( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_SetBlendFrames
|
|
===============
|
|
*/
|
|
void idWeapon::Event_SetBlendFrames( int channel, int blendFrames ) {
|
|
animBlendFrames = blendFrames;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_GetBlendFrames
|
|
===============
|
|
*/
|
|
void idWeapon::Event_GetBlendFrames( int channel ) {
|
|
idThread::ReturnInt( animBlendFrames );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_Next
|
|
================
|
|
*/
|
|
void idWeapon::Event_Next( void ) {
|
|
// change to another weapon if possible
|
|
owner->NextBestWeapon();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetSkin
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetSkin( const char *skinname ) {
|
|
const idDeclSkin *skinDecl;
|
|
|
|
if ( !skinname || !skinname[ 0 ] ) {
|
|
skinDecl = NULL;
|
|
} else {
|
|
skinDecl = declManager->FindSkin( skinname );
|
|
}
|
|
|
|
// Don't update if the skin hasn't changed.
|
|
if( renderEntity.customSkin == skinDecl && worldModel.GetEntity() != NULL && worldModel.GetEntity()->GetSkin() == skinDecl )
|
|
{
|
|
return;
|
|
}
|
|
|
|
renderEntity.customSkin = skinDecl;
|
|
UpdateVisuals();
|
|
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->SetSkin( skinDecl );
|
|
}
|
|
|
|
if ( gameLocal.isServer && !isPlayerFlashlight) {
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
|
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
|
msg.WriteInt( ( skinDecl != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_SKIN, skinDecl->Index() ) : -1 );
|
|
ServerSendEvent( EVENT_CHANGESKIN, &msg, false, -1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_Flashlight
|
|
================
|
|
*/
|
|
void idWeapon::Event_Flashlight( int enable ) {
|
|
if ( enable ) {
|
|
lightOn = true;
|
|
MuzzleFlashLight();
|
|
} else {
|
|
lightOn = false;
|
|
muzzleFlashEnd = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_GetLightParm
|
|
================
|
|
*/
|
|
void idWeapon::Event_GetLightParm( int parmnum ) {
|
|
if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
|
|
gameLocal.Error( "shader parm index (%d) out of range", parmnum );
|
|
}
|
|
|
|
idThread::ReturnFloat( muzzleFlash.shaderParms[ parmnum ] );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetLightParm
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetLightParm( int parmnum, float value ) {
|
|
if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
|
|
gameLocal.Error( "shader parm index (%d) out of range", parmnum );
|
|
}
|
|
|
|
muzzleFlash.shaderParms[ parmnum ] = value;
|
|
worldMuzzleFlash.shaderParms[ parmnum ] = value;
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_SetLightParms
|
|
================
|
|
*/
|
|
void idWeapon::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
|
|
muzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
|
|
muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
|
|
muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
|
|
muzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
|
|
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
|
|
worldMuzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
|
|
|
|
UpdateVisuals();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_CreateProjectile
|
|
================
|
|
*/
|
|
void idWeapon::Event_CreateProjectile( void ) {
|
|
if ( !gameLocal.isClient ) {
|
|
projectileEnt = NULL;
|
|
gameLocal.SpawnEntityDef( projectileDict, &projectileEnt, false );
|
|
if ( projectileEnt ) {
|
|
projectileEnt->SetOrigin( GetPhysics()->GetOrigin() );
|
|
projectileEnt->Bind( owner, false );
|
|
projectileEnt->Hide();
|
|
}
|
|
idThread::ReturnEntity( projectileEnt );
|
|
} else {
|
|
idThread::ReturnEntity( NULL );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_LaunchProjectiles
|
|
================
|
|
*/
|
|
void idWeapon::Event_LaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ) {
|
|
idProjectile *proj;
|
|
idEntity *ent;
|
|
int i;
|
|
idVec3 dir;
|
|
float ang;
|
|
float spin;
|
|
float distance;
|
|
trace_t tr;
|
|
idVec3 start;
|
|
idVec3 muzzle_pos;
|
|
idBounds ownerBounds, projBounds;
|
|
|
|
// Koz the shotgun is supposed to be a closeup weapon in the game, but the depth provided in VR really changes
|
|
// how it feels. IMO, it seems underpowered unless you are on top of an enemy, due to the extreme pellet spread.
|
|
// This enables an optional 'choke' for the shotgun, by reducing the spread.
|
|
weapon_t currentWeap;
|
|
|
|
currentWeap = IdentifyWeapon();
|
|
if ( currentWeap == WEAPON_SHOTGUN)
|
|
{
|
|
//idPlayer *player;
|
|
//player = gameLocal.GetLocalPlayer();
|
|
//if ( 1 || player == owner )
|
|
//{
|
|
common->Printf( "Event_LaunchProjectiles spread = %f , ", spread );
|
|
spread -= 14 * vr_shotgunChoke.GetFloat() / 100.0f;
|
|
spread = idMath::ClampFloat( 8.0f, 22.0f, spread ); // not too low, or the shotgun gets WAY too powerful at range.
|
|
common->Printf( "choke spread = %f\n", spread );
|
|
//}
|
|
}
|
|
|
|
assert( owner != NULL );
|
|
|
|
if ( IsHidden() ) {
|
|
return;
|
|
}
|
|
|
|
if ( !projectileDict.GetNumKeyVals() ) {
|
|
const char *classname = weaponDef->dict.GetString( "classname" );
|
|
gameLocal.Warning( "No projectile defined on '%s'", classname );
|
|
return;
|
|
}
|
|
|
|
// avoid all ammo considerations on an MP client
|
|
if ( !gameLocal.isClient ) {
|
|
|
|
// check if we're out of ammo or the clip is empty
|
|
int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
|
|
if ( !ammoAvail || ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) ) {
|
|
return;
|
|
}
|
|
|
|
// if this is a power ammo weapon ( currently only the bfg ) then make sure
|
|
// we only fire as much power as available in each clip
|
|
if ( powerAmmo ) {
|
|
// power comes in as a float from zero to max
|
|
// if we use this on more than the bfg will need to define the max
|
|
// in the .def as opposed to just in the script so proper calcs
|
|
// can be done here.
|
|
dmgPower = ( int )dmgPower + 1;
|
|
if ( dmgPower > ammoClip ) {
|
|
dmgPower = ammoClip;
|
|
}
|
|
}
|
|
|
|
owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired );
|
|
if ( clipSize && ammoRequired ) {
|
|
ammoClip -= powerAmmo ? dmgPower : 1;
|
|
}
|
|
|
|
}
|
|
|
|
if ( !silent_fire ) {
|
|
// wake up nearby monsters
|
|
gameLocal.AlertAI( owner );
|
|
}
|
|
|
|
// set the shader parm to the time of last projectile firing,
|
|
// which the gun material shaders can reference for single shot barrel glows, etc
|
|
renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
|
|
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
|
|
|
|
if ( worldModel.GetEntity() ) {
|
|
worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
|
|
worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
|
|
}
|
|
|
|
// calculate the muzzle position
|
|
GetProjectileLaunchOriginAndAxis( muzzleOrigin, muzzleAxis );
|
|
|
|
// calculate the muzzle position
|
|
/*
|
|
if ( barrelJointView != INVALID_JOINT && projectileDict.GetBool( "launchFromBarrel" ) ) {
|
|
// there is an explicit joint for the muzzle
|
|
GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
|
|
} else {
|
|
muzzleOrigin = viewWeaponOrigin;
|
|
muzzleAxis = viewWeaponAxis;
|
|
}*/
|
|
|
|
// add some to the kick time, incrementally moving repeat firing weapons back
|
|
if ( kick_endtime < gameLocal.realClientTime ) {
|
|
kick_endtime = gameLocal.realClientTime;
|
|
}
|
|
kick_endtime += muzzle_kick_time;
|
|
if ( kick_endtime > gameLocal.realClientTime + muzzle_kick_maxtime ) {
|
|
kick_endtime = gameLocal.realClientTime + muzzle_kick_maxtime;
|
|
}
|
|
|
|
// "Predict" damage effects on clients by just spawning a local projectile that deals no damage. Used only
|
|
// for sound & visual effects. Damage will be handled through reliable messages to the host.
|
|
const bool isHitscan = projectileDict.GetBool( "net_instanthit" );
|
|
const bool attackerIsLocal = owner->IsLocallyControlled();
|
|
const bool actuallySpawnProjectile = gameLocal.isServer || attackerIsLocal || isHitscan;
|
|
|
|
if( actuallySpawnProjectile )
|
|
{
|
|
ownerBounds = owner->GetPhysics()->GetAbsBounds();
|
|
|
|
owner->AddProjectilesFired( num_projectiles );
|
|
|
|
float spreadRad = DEG2RAD( spread );
|
|
for( i = 0; i < num_projectiles; i++ )
|
|
{
|
|
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
|
|
spin = ( float )DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
|
|
dir = muzzleAxis[ 0 ] + muzzleAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - muzzleAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
|
|
dir.Normalize();
|
|
|
|
if( projectileEnt )
|
|
{
|
|
ent = projectileEnt;
|
|
ent->Show();
|
|
ent->Unbind();
|
|
projectileEnt = NULL;
|
|
}
|
|
else
|
|
{
|
|
if( gameLocal.isClient )
|
|
{
|
|
// This is predicted on a client, don't replicate.
|
|
// Must be set before spawn, so that the entity can be spawned into the correct area of the entities array.
|
|
projectileDict.SetBool( "net_skip_replication", true );
|
|
}
|
|
else
|
|
{
|
|
projectileDict.SetBool( "net_skip_replication", false );
|
|
}
|
|
gameLocal.SpawnEntityDef( projectileDict, &ent, false );
|
|
}
|
|
|
|
if( ent == NULL || !ent->IsType( idProjectile::Type ) )
|
|
{
|
|
const char* projectileName = weaponDef->dict.GetString( "def_projectile" );
|
|
gameLocal.Error( "'%s' is not an idProjectile", projectileName );
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
if( projectileDict.GetBool( "net_instanthit" ) )
|
|
{
|
|
// don't synchronize this on top of the already predicted effect
|
|
ent->fl.networkSync = false;
|
|
}
|
|
else if( owner != NULL )
|
|
{
|
|
/*
|
|
int predictedKey = idEntity::INVALID_PREDICTION_KEY;
|
|
// Set the prediction key only for non-instanthit projectiles.
|
|
if( gameLocal.isClient )
|
|
{
|
|
owner->IncrementFireCount();
|
|
}
|
|
predictedKey = gameLocal.GeneratePredictionKey( this, owner, -1 );
|
|
ent->SetPredictedKey( predictedKey );*/
|
|
}
|
|
|
|
proj = static_cast<idProjectile*>( ent );
|
|
proj->Create( owner, muzzleOrigin, dir );
|
|
|
|
projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
|
|
|
|
// make sure the projectile starts inside the bounding box of the owner
|
|
// Carl: unless it's a grenade, in which case we want to be able to drop it over a railing without inexplicably killing ourselves
|
|
if( IdentifyWeapon() == WEAPON_HANDGRENADE && game->isVR && commonVr->VR_USE_MOTION_CONTROLS && i == 0 )
|
|
{
|
|
muzzle_pos = muzzleOrigin + muzzleAxis[ 0 ] * 2.0f;
|
|
// Carl: check that there's a grenade-sized gap at hand height from the center of our chest to our grenade,
|
|
// so we can drop grenades over a railing, but not through a solid wall or glass window.
|
|
// Ideally start should be our right shoulder at grenade height, but I'm not sure how to get that.
|
|
start = ownerBounds.GetCenter();
|
|
start.z = muzzleOrigin.z;
|
|
// MASK_SHOT_RENDERMODEL goes through chainlink fences, but a grenade shouldn't.
|
|
gameLocal.clip.Translation(tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL | CONTENTS_PLAYERCLIP, owner);
|
|
// Try reaching down from neck height if sticking our hand out straight doesn't work.
|
|
// Ideally this should be our right shoulder at shoulder height, but I'm not sure how to get that.
|
|
if ( tr.fraction < 1.0f )
|
|
{
|
|
start.z = commonVr->lastHMDViewOrigin.z - 6;
|
|
gameLocal.clip.Translation(tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL | CONTENTS_PLAYERCLIP, owner);
|
|
}
|
|
muzzle_pos = tr.endpos;
|
|
}
|
|
else if( i == 0 )
|
|
{
|
|
muzzle_pos = muzzleOrigin + muzzleAxis[ 0 ] * 2.0f;
|
|
if( ( ownerBounds - projBounds ).RayIntersection( muzzle_pos, muzzleAxis[0], distance ) )
|
|
{
|
|
start = muzzle_pos + distance * muzzleAxis[0];
|
|
}
|
|
else
|
|
{
|
|
start = ownerBounds.GetCenter();
|
|
}
|
|
gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
|
|
muzzle_pos = tr.endpos;
|
|
}
|
|
|
|
// Koz if throwing a grenade use the tracked hand velocity when using motion controls if the controller is not mounted
|
|
// Carl: Dual Wielding
|
|
float speed = 0;
|
|
if( IdentifyWeapon() == WEAPON_HANDGRENADE && game->isVR && commonVr->VR_USE_MOTION_CONTROLS && !vr_mountedWeaponController.GetBool() )
|
|
{
|
|
int h = GetHand();
|
|
if( h >= 0 )
|
|
speed = owner->hands[ h ].throwVelocity * vr_throwPower.GetFloat();
|
|
}
|
|
// Koz end
|
|
|
|
// Normal launch
|
|
proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower, speed );
|
|
proj->Launch(muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower);
|
|
}
|
|
|
|
// toss the brass
|
|
if( brassDelay >= 0 )
|
|
{
|
|
PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
|
|
}
|
|
}
|
|
|
|
/*if ( gameLocal.isClient ) {
|
|
|
|
// predict instant hit projectiles
|
|
if ( projectileDict.GetBool( "net_instanthit" ) ) {
|
|
float spreadRad = DEG2RAD( spread );
|
|
//muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
|
|
muzzle_pos = muzzleOrigin + viewWeaponAxis[ 0 ] * 2.0f;
|
|
for( i = 0; i < num_projectiles; i++ ) {
|
|
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
|
|
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
|
|
|
|
dir = viewWeaponAxis[ 0 ] + viewWeaponAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - viewWeaponAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
|
|
dir.Normalize();
|
|
gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
|
|
if ( tr.fraction < 1.0f ) {
|
|
idProjectile::ClientPredictionCollide( this, projectileDict, tr, vec3_origin, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ownerBounds = owner->GetPhysics()->GetAbsBounds();
|
|
|
|
owner->AddProjectilesFired( num_projectiles );
|
|
|
|
float spreadRad = DEG2RAD( spread );
|
|
for( i = 0; i < num_projectiles; i++ ) {
|
|
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
|
|
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
|
|
dir = muzzleAxis[ 0 ] + muzzleAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - muzzleAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
|
|
dir.Normalize();
|
|
|
|
|
|
/* vrClientInfo *pVRClientInfo = owner->GetVRClientInfo();
|
|
* if (owner->GetCurrentWeaponId() == WEAPON_HANDGRENADE &&
|
|
pVRClientInfo != nullptr &&
|
|
vr_throwables.GetBool())
|
|
{
|
|
idVec3 releaseOffset( -pVRClientInfo->throw_origin[2],
|
|
-pVRClientInfo->throw_origin[0],
|
|
pVRClientInfo->throw_origin[1]);
|
|
idAngles a(0, owner->viewAngles.yaw - pVRClientInfo->hmdorientation[YAW], 0);
|
|
releaseOffset *= a.ToMat3();
|
|
releaseOffset *= cvarSystem->GetCVarFloat( "vr_worldscale" );
|
|
muzzle_pos = owner->firstPersonViewOrigin + releaseOffset;
|
|
|
|
idVec3 throw_direction( -pVRClientInfo->throw_trajectory[2],
|
|
-pVRClientInfo->throw_trajectory[0],
|
|
pVRClientInfo->throw_trajectory[1]);
|
|
throw_direction *= a.ToMat3();
|
|
throw_direction.Normalize();
|
|
dir = throw_direction;
|
|
|
|
launchPower = pVRClientInfo->throw_power * 0.5f;
|
|
} else {
|
|
dir = viewWeaponAxis[0] + viewWeaponAxis[2] * (ang * idMath::Sin(spin)) -
|
|
viewWeaponAxis[1] * (ang * idMath::Cos(spin));
|
|
|
|
dir.Normalize();
|
|
}***
|
|
|
|
if ( projectileEnt ) {
|
|
ent = projectileEnt;
|
|
ent->Show();
|
|
ent->Unbind();
|
|
projectileEnt = NULL;
|
|
} else {
|
|
gameLocal.SpawnEntityDef( projectileDict, &ent, false );
|
|
}
|
|
|
|
if ( !ent || !ent->IsType( idProjectile::Type ) ) {
|
|
const char *projectileName = weaponDef->dict.GetString( "def_projectile" );
|
|
gameLocal.Error( "'%s' is not an idProjectile", projectileName );
|
|
}
|
|
|
|
if ( projectileDict.GetBool( "net_instanthit" ) ) {
|
|
// don't synchronize this on top of the already predicted effect
|
|
ent->fl.networkSync = false;
|
|
}
|
|
|
|
proj = static_cast<idProjectile *>(ent);
|
|
proj->Create( owner, muzzleOrigin, dir );
|
|
|
|
projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
|
|
|
|
if (owner->GetCurrentWeaponId() != WEAPON_HANDGRENADE ||
|
|
!vr_throwables.GetBool()) {
|
|
// make sure the projectile starts inside the bounding box of the owner
|
|
if (i == 0) {
|
|
//muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
|
|
muzzle_pos = muzzleOrigin + viewWeaponAxis[0] * 2.0f;
|
|
// DG: sometimes the assertion in idBounds::operator-(const idBounds&) triggers
|
|
// (would get bounding box with negative volume)
|
|
// => check that before doing ownerBounds - projBounds (equivalent to the check in the assertion)
|
|
idVec3 obDiff = ownerBounds[1] - ownerBounds[0];
|
|
idVec3 pbDiff = projBounds[1] - projBounds[0];
|
|
bool boundsSubLegal =
|
|
obDiff.x > pbDiff.x && obDiff.y > pbDiff.y && obDiff.z > pbDiff.z;
|
|
if (boundsSubLegal &&
|
|
(ownerBounds - projBounds).RayIntersection(muzzle_pos, viewWeaponAxis[0],
|
|
distance)) {
|
|
//start = muzzle_pos + distance * playerViewAxis[0];
|
|
start = muzzle_pos + distance * viewWeaponAxis[0];
|
|
} else {
|
|
start = ownerBounds.GetCenter();
|
|
}
|
|
gameLocal.clip.Translation(tr, start, muzzle_pos,
|
|
proj->GetPhysics()->GetClipModel(),
|
|
proj->GetPhysics()->GetClipModel()->GetAxis(),
|
|
MASK_SHOT_RENDERMODEL, owner);
|
|
muzzle_pos = tr.endpos;
|
|
}
|
|
}
|
|
|
|
proj->Launch(muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower);
|
|
}
|
|
|
|
// toss the brass
|
|
PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
|
|
}*/
|
|
|
|
// add the light for the muzzleflash
|
|
if ( !lightOn ) {
|
|
MuzzleFlashLight();
|
|
}
|
|
|
|
owner->WeaponFireFeedback( GetHand(), &weaponDef->dict );
|
|
|
|
// reset muzzle smoke
|
|
weaponSmokeStartTime = gameLocal.realClientTime;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::Event_Melee
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_Melee( void ) {
|
|
idEntity *ent;
|
|
trace_t tr;
|
|
|
|
if ( !meleeDef ) {
|
|
gameLocal.Error( "No meleeDef on '%s'", weaponDef->dict.GetString( "classname" ) );
|
|
}
|
|
|
|
if ( !gameLocal.isClient ) {
|
|
idVec3 start = viewWeaponOrigin;
|
|
//idVec3 end = start + playerViewAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
|
|
idVec3 end = start + viewWeaponAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
|
|
gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
|
|
if ( tr.fraction < 1.0f ) {
|
|
ent = gameLocal.GetTraceEntity( tr );
|
|
} else {
|
|
ent = NULL;
|
|
}
|
|
|
|
if ( g_debugWeapon.GetBool() ) {
|
|
gameRenderWorld->DebugLine( colorYellow, start, end, 100 );
|
|
if ( ent ) {
|
|
gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100 );
|
|
}
|
|
}
|
|
|
|
bool hit = false;
|
|
const char *hitSound = meleeDef->dict.GetString( "snd_miss" );
|
|
|
|
if ( ent ) {
|
|
|
|
float push = meleeDef->dict.GetFloat( "push" );
|
|
idVec3 impulse = -push * owner->PowerUpModifier( SPEED ) * tr.c.normal;
|
|
|
|
if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && ( ent->IsType( idActor::Type ) || ent->IsType( idAFAttachment::Type) ) ) {
|
|
idThread::ReturnInt( 0 );
|
|
return;
|
|
}
|
|
|
|
ent->ApplyImpulse( this, tr.c.id, tr.c.point, impulse );
|
|
|
|
// weapon stealing - do this before damaging so weapons are not dropped twice
|
|
if ( gameLocal.isMultiplayer
|
|
&& weaponDef && weaponDef->dict.GetBool( "stealing" )
|
|
&& ent->IsType( idPlayer::Type )
|
|
&& !owner->PowerUpActive( BERSERK )
|
|
&& ( gameLocal.gameType != GAME_TDM || gameLocal.serverInfo.GetBool( "si_teamDamage" ) || ( owner->team != static_cast< idPlayer * >( ent )->team ) )
|
|
) {
|
|
owner->StealWeapon( static_cast< idPlayer * >( ent ) );
|
|
}
|
|
|
|
if ( ent->fl.takedamage ) {
|
|
idVec3 kickDir, globalKickDir;
|
|
meleeDef->dict.GetVector( "kickDir", "0 0 0", kickDir );
|
|
globalKickDir = muzzleAxis * kickDir;
|
|
ent->Damage( owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier( MELEE_DAMAGE ), tr.c.id );
|
|
hit = true;
|
|
}
|
|
|
|
if ( weaponDef->dict.GetBool( "impact_damage_effect" ) ) {
|
|
|
|
if ( ent->spawnArgs.GetBool( "bleed" ) ) {
|
|
|
|
hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
|
|
|
|
ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ) );
|
|
|
|
} else {
|
|
|
|
int type = tr.c.material->GetSurfaceType();
|
|
if ( type == SURFTYPE_NONE ) {
|
|
type = GetDefaultSurfaceType();
|
|
}
|
|
|
|
const char *materialType = gameLocal.sufaceTypeNames[ type ];
|
|
|
|
// start impact sound based on material type
|
|
hitSound = meleeDef->dict.GetString( va( "snd_%s", materialType ) );
|
|
if ( *hitSound == '\0' ) {
|
|
hitSound = meleeDef->dict.GetString( "snd_metal" );
|
|
}
|
|
|
|
if ( gameLocal.time > nextStrikeFx ) {
|
|
const char *decal;
|
|
// project decal
|
|
decal = weaponDef->dict.GetString( "mtr_strike" );
|
|
if ( decal && *decal ) {
|
|
gameLocal.ProjectDecal( tr.c.point, -tr.c.normal, 8.0f, true, 6.0, decal );
|
|
}
|
|
nextStrikeFx = gameLocal.time + 200;
|
|
} else {
|
|
hitSound = "";
|
|
}
|
|
|
|
strikeSmokeStartTime = gameLocal.time;
|
|
strikePos = tr.c.point;
|
|
strikeAxis = -tr.endAxis;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( *hitSound != '\0' ) {
|
|
const idSoundShader *snd = declManager->FindSound( hitSound );
|
|
StartSoundShader( snd, SND_CHANNEL_BODY2, 0, true, NULL );
|
|
}
|
|
|
|
idThread::ReturnInt( hit );
|
|
owner->WeaponFireFeedback( GetHand(), &weaponDef->dict );
|
|
return;
|
|
}
|
|
|
|
idThread::ReturnInt( 0 );
|
|
owner->WeaponFireFeedback( GetHand(), &weaponDef->dict );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::Event_GetWorldModel
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_GetWorldModel( void ) {
|
|
idThread::ReturnEntity( worldModel.GetEntity() );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idWeapon::Event_AllowDrop
|
|
=====================
|
|
*/
|
|
void idWeapon::Event_AllowDrop( int allow ) {
|
|
if ( allow ) {
|
|
allowDrop = true;
|
|
} else {
|
|
allowDrop = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idWeapon::Event_EjectBrass
|
|
|
|
Toss a shell model out from the breach if the bone is present
|
|
================
|
|
*/
|
|
void idWeapon::Event_EjectBrass( void ) {
|
|
if ( !g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel() ) {
|
|
return;
|
|
}
|
|
|
|
if ( ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals() ) {
|
|
return;
|
|
}
|
|
|
|
if ( gameLocal.isClient ) {
|
|
return;
|
|
}
|
|
|
|
idMat3 axis;
|
|
idVec3 origin, linear_velocity, angular_velocity;
|
|
idEntity *ent;
|
|
|
|
if ( !GetGlobalJointTransform( true, ejectJointView, origin, axis ) ) {
|
|
return;
|
|
}
|
|
|
|
gameLocal.SpawnEntityDef( brassDict, &ent, false );
|
|
if ( !ent || !ent->IsType( idDebris::Type ) ) {
|
|
gameLocal.Error( "'%s' is not an idDebris", weaponDef ? weaponDef->dict.GetString( "def_ejectBrass" ) : "def_ejectBrass" );
|
|
}
|
|
idDebris *debris = static_cast<idDebris *>(ent);
|
|
debris->Create( owner, origin, axis );
|
|
debris->Launch();
|
|
|
|
// Koz begin
|
|
idAngles vwa = viewWeaponAxis.ToAngles();
|
|
vwa.yaw += 30.0f;
|
|
idMat3 ba = vwa.Normalize180().ToMat3();
|
|
|
|
//linear_velocity = 50 * (viewWeaponAxis[0] + viewWeaponAxis[1] + viewWeaponAxis[2]);
|
|
linear_velocity = 50 * ( ba[0] + ba[1] + ba[2] );
|
|
|
|
// Koz end
|
|
//linear_velocity = 40 * ( playerViewAxis[0] + playerViewAxis[1] + playerViewAxis[2] );
|
|
//linear_velocity = 40 * ( viewWeaponAxis[0] + viewWeaponAxis[1] + viewWeaponAxis[2] );
|
|
angular_velocity.Set( 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat() );
|
|
|
|
debris->GetPhysics()->SetLinearVelocity( linear_velocity );
|
|
debris->GetPhysics()->SetAngularVelocity( angular_velocity );
|
|
}
|
|
|
|
/*Koz - what a mess
|
|
===============
|
|
idWeapon::Event_GetWeaponSkin
|
|
===============
|
|
*/
|
|
void idWeapon::Event_GetWeaponSkin()
|
|
{
|
|
if ( !owner || !game->isVR )
|
|
{
|
|
idThread::ReturnString( "" );
|
|
return;
|
|
}
|
|
|
|
// game is vr, return a string name for the skin matching the hand/statwatch config.
|
|
|
|
static idStr vrSkinName;
|
|
|
|
// Koz fixme - need to go through all the weapon models and delete the meshes for the hands and statwatch. Also need to delete all the skin definitions, no longer necessary.
|
|
|
|
// this has all changed. Originally, hands were part of the weapon models, and hands and statwatch were turned on and off
|
|
// by changing the skin for model. Now the hands are always drawn as part of the player body, so all the skins
|
|
// are no longer needed. The only skin used now is for the mini flashlight when mounted to the weapon.
|
|
|
|
|
|
if ( isPlayerFlashlight )
|
|
{
|
|
vrSkinName = ( commonVr->GetCurrentFlashlightMode() == FLASHLIGHT_GUN || commonVr->GetCurrentFlashlightMode() == FLASHLIGHT_PISTOL ) ? "minivr/flashhands/0h" : "vr/flashhands/0h"; // mini flashlight skin for gun mount : normal flashlight skin
|
|
}
|
|
else
|
|
{
|
|
//vrSkinName = "vr/weaponhands/0h";
|
|
vrSkinName = "";
|
|
}
|
|
|
|
//common->Printf( "idWeapon::Event_GetWeaponSkin() returning %s\n", vrSkinName.c_str() );
|
|
idThread::ReturnString( vrSkinName.c_str() );
|
|
|
|
}
|
|
|
|
/*
|
|
==================
|
|
idPlayer::Event_IsMotionControlled
|
|
==================
|
|
*/
|
|
void idWeapon::Event_IsMotionControlled()
|
|
{
|
|
static int isMC;
|
|
|
|
//isMC = (game->isVR && commonVr->VR_USE_MOTION_CONTROLS) ? 1 : 0;
|
|
isMC = 1;
|
|
|
|
// Koz debug common->Printf( "Event_IsMotionControlled returning %d\n", isMC );
|
|
// idThread::ReturnInt( ( game->isVR && commonVr->VR_USE_MOTION_CONTROLS ) ? 1 : 0 );
|
|
|
|
idThread::ReturnInt( isMC );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::Event_IsInvisible
|
|
===============
|
|
*/
|
|
void idWeapon::Event_IsInvisible( void ) {
|
|
if ( !owner ) {
|
|
idThread::ReturnFloat( 0 );
|
|
return;
|
|
}
|
|
idThread::ReturnFloat( owner->PowerUpActive( INVISIBILITY ) ? 1 : 0 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idWeapon::ClientPredictionThink
|
|
===============
|
|
*/
|
|
void idWeapon::ClientPredictionThink( void ) {
|
|
UpdateAnimation();
|
|
}
|