dhewm3-sdk/game/Weapon.cpp
Daniel Gibson 35840cfafb Fix renderlights loaded from savegames aliasing other lights
Some entities wrote the handle from gameRenderWorld->AddLightDef()
into savegames and reused it after restoring it.
That's a bad idea, because at that point the handle most likely belongs
to something else (likely some idLight). The most visible issue this
can create is that the flashlight may not work correctly after loading
a savegame with flashlight on, when it happens to alias a light that's
updated each frame to (mostly) being off..

The correct way to handle this (THAT FOR SOME REASON WAS ALREADY
IMPLEMENTED IN D3XP BUT NOT THE BASE GAME - WHY?!) is to get a fresh
handle with AddLightDef() when restoring a savegame - unless the handle
was -1, which means that the light didn't exist when saving.

fixes #495

# Conflicts:
#	game/Weapon.cpp
2024-03-18 23:47:04 +01:00

4556 lines
130 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "framework/DeclEntityDef.h"
#include "framework/DeclSkin.h"
#include "gamesys/SysCvar.h"
#include "ai/AI.h"
#include "Player.h"
#include "Trigger.h"
#include "SmokeParticles.h"
#include "WorldSpawn.h"
#include "Moveable.h" //rev 2019
#include "BrittleFracture.h" //rev 2019
#include "renderer/ModelManager.h"
#include "Weapon.h"
/***********************************************************************
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" );
// New------------------
#ifdef _DENTONMOD
const idEventDef EV_Weapon_ChangeProjectileDef( "changeProjectileDef" , "d", 'f' ); // New
const idEventDef EV_Weapon_GetProjectileType( "getProjectileType", NULL, 'f' );
const idEventDef EV_Weapon_SetZoom( "setZoom" , "d"); // New
const idEventDef EV_Weapon_StartWeaponParticle( "startWeaponParticle", "s" );
const idEventDef EV_Weapon_StopWeaponParticle( "stopWeaponParticle", "s" );
const idEventDef EV_Weapon_StartWeaponLight( "startWeaponLight", "s" );
const idEventDef EV_Weapon_StopWeaponLight( "stopWeaponLight", "s" );
#endif //_DENTONMOD
//
const idEventDef EV_Weapon_StartAutoMelee( "startAutoMelee", "fd" );
const idEventDef EV_Weapon_StopAutoMelee( "stopAutoMelee" );
const idEventDef EV_Weapon_StartMeleeBeam( "startMeleeBeam", "d" );
const idEventDef EV_Weapon_StopMeleeBeam( "stopMeleeBeam" );
//const idEventDef EV_Weapon_SetWeaponMode( "setWeaponMode", "d" );
//const idEventDef EV_Weapon_GetWeaponMode( "getWeaponMode", NULL, 'd' );
//ivan end
//
// class def
//
CLASS_DECLARATION( idAnimatedEntity, idWeapon )
#ifdef _DENTONMOD
EVENT( EV_Weapon_SetZoom, idWeapon::Event_SetZoom ) // New
#endif //_DENTONMOD
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 )
#ifdef _DENTONMOD
EVENT( EV_Weapon_ChangeProjectileDef, idWeapon::Event_ChangeProjectileDef ) // New
EVENT( EV_Weapon_GetProjectileType, idWeapon::Event_GetProjectileType ) // New
EVENT( EV_Weapon_StartWeaponParticle, idWeapon::Event_StartWeaponParticle ) // All Four New
EVENT( EV_Weapon_StopWeaponParticle, idWeapon::Event_StopWeaponParticle )
EVENT( EV_Weapon_StartWeaponLight, idWeapon::Event_StartWeaponLight )
EVENT( EV_Weapon_StopWeaponLight, idWeapon::Event_StopWeaponLight )
#endif // _DENTONMOD
//ivan start
EVENT( EV_Weapon_StartAutoMelee, idWeapon::Event_StartAutoMelee )
EVENT( EV_Weapon_StopAutoMelee, idWeapon::Event_StopAutoMelee )
//EVENT( EV_Weapon_StartMeleeBeam, idWeapon::Event_StartMeleeBeam )
//EVENT( EV_Weapon_StopMeleeBeam, idWeapon::Event_StopMeleeBeam )
//EVENT( EV_Weapon_SetWeaponMode, idWeapon::Event_SetWeaponMode )
//EVENT( EV_Weapon_GetWeaponMode, idWeapon::Event_GetWeaponMode )
//ivan end
END_CLASS
/***********************************************************************
init
***********************************************************************/
/*
================
idWeapon::idWeapon()
================
*/
idWeapon::idWeapon() {
owner = NULL;
weaponDef = NULL; //un noted code change from original sdk
thread = NULL;
memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) ); //un noted code change from original sdk
memset( &nozzleGlow, 0, sizeof( nozzleGlow ) );
muzzleFlashEnd = 0;
flashColor = vec3_origin;
worldMuzzleFlashHandle = -1; //un noted code change from original sdk
nozzleGlowHandle = -1;
modelDefHandle = -1;
berserk = 2;
brassDelay = 0;
allowDrop = true;
//ivan start
#ifdef TRAIL_FX_CHAIN
//done in Clear()
#else
trailGen = NULL; //rev 2019
#endif
//ivan end
Clear();
fl.networkSync = true;
}
/*
================
idWeapon::~idWeapon()
================
*/
idWeapon::~idWeapon() {
Clear();
//un noted code change from original sdk
//ivan start
#ifdef TRAIL_FX_CHAIN
//no need to delete lastBeamInChain
#else
//this is not strictly necessary because weapon is only deallocated at map end... and that's when trailsManager would do this for me.
gameLocal.trailsManager->RemoveTrailGen( trailGen ); //rev 2019
#endif
//ivan start
}
/*
================
idWeapon::Spawn
================
*/
void idWeapon::Spawn( void ) {
//un noted code change from original sdk
thread = new idThread();
thread->ManualDelete();
thread->ManualControl();
//ivan start //rev 2019
#ifdef TRAIL_FX_CHAIN
//nothing to spawn for beams. lastBeamInChain is already null.
#else
//this is spawned once at player spawn and then it is always there.
if(!trailGen){
trailGen = gameLocal.trailsManager->NewTrailGen();
}
#endif
//ivan end
}
/*
================
idWeapon::SetOwner
Only called at player spawn time, not each weapon switch
================
*/
void idWeapon::SetOwner( idPlayer *_owner ) {
assert( !owner );
owner = _owner;
SetName( va( "%s_weapon", owner->name.c_str() ) );
//un noted code change from original sdk
}
/*
================
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 );
//un noted code change from original sdk
savefile->WriteInt( hideTime );
savefile->WriteFloat( hideDistance );
savefile->WriteInt( hideStartTime );
savefile->WriteFloat( hideStart );
savefile->WriteFloat( hideEnd );
//un noted code change from original sdk
savefile->WriteBool( hide );
savefile->WriteBool( disabled );
savefile->WriteInt( berserk );
savefile->WriteVec3( playerViewOrigin );
savefile->WriteMat3( playerViewAxis );
//un noted code change from original sdk
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 );
//un noted code change from original sdk
savefile->WriteInt( worldMuzzleFlashHandle );
savefile->WriteRenderLight( worldMuzzleFlash );
savefile->WriteVec3( flashColor );
savefile->WriteInt( muzzleFlashEnd );
savefile->WriteInt( flashTime );
savefile->WriteBool( lightOn );
savefile->WriteBool( silent_fire );
//un noted code change from original sdk
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 );
//un noted code change from original sdk
savefile->WriteJoint( flashJointWorld );
savefile->WriteJoint( barrelJointWorld );
savefile->WriteJoint( ejectJointWorld );
savefile->WriteJoint( meleeJointWorld ); //ivan
//ivan start
savefile->WriteBool( autoMeleeEnabled );
savefile->WriteBool( useMeleeBox );
savefile->WriteFloat( comboMultiplier );
savefile->WriteBounds( meleebox );
savefile->WriteInt( trailNumType );
#ifdef TRAIL_FX_CHAIN
lastBeamInChain.Save( savefile );
#else
//trailGen is saved elsewhere. Here we save only the id to retrieve it later
savefile->WriteInt( gameLocal.trailsManager->GetSafeUniqueId( trailGen ) );
#endif
savefile->WriteInt( trailLowOffset );
savefile->WriteInt( trailHighOffset );
//ivan end
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->WriteInt( nextMeleeSnd ); //ivan
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->WriteObject( lastMeleeEnt ); //ivan //rev 2020 code fixes
// New----------------------------------------------
#ifdef _DENTONMOD
savefile->WriteInt(weaponParticles.Num());
for(int i = 0; i < weaponParticles.Num(); i++) {
WeaponParticle_t* part = weaponParticles.GetIndex(i);
particleFlags_s flags = part->particleFlags;
LittleBitField( &flags, sizeof( flags ) );
savefile->Write( &flags, sizeof( flags ) );
savefile->WriteString( part->name );
savefile->WriteString( part->particlename );
savefile->WriteInt( part->startTime );
savefile->WriteJoint( part->joint );
if( !part->particleFlags.isSmoke ) {
savefile->WriteRenderEntity( part->renderEntity );
}
if( part->particleFlags.isDir ) {
savefile->WriteVec3(part->dir);
}
if( part->particleFlags.isOffset ) {
savefile->WriteVec3(part->offset);
}
}
savefile->WriteInt(weaponLights.Num());
for(int i = 0; i < weaponLights.Num(); i++) {
WeaponLight_t* light = weaponLights.GetIndex(i);
lightFlags_s flags = light->lightFlags;
LittleBitField( &flags, sizeof( flags ) );
savefile->Write( &flags, sizeof( flags ) );
savefile->WriteString( light->name );
savefile->WriteInt( light->startTime );
savefile->WriteJoint( light->joint );
savefile->WriteInt( light->lightHandle );
savefile->WriteRenderLight( light->light );
if( !light->lightFlags.isAlwaysOn ) {
savefile->WriteInt( light->endTime );
}
if( light->lightFlags.isDir ) {
savefile->WriteVec3(light->dir);
}
if( light->lightFlags.isOffset ) {
savefile->WriteVec3(light->offset);
}
}
#endif// _DENTONMOD
//ivan start
savefile->WriteInt( maxAmmo );
savefile->WriteInt( fireMode );
savefile->WriteFloat( mouseAimOffsetY );
//ivan end
}
/*
================
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_SPECIAL.LinkTo( scriptObject, "WEAPON_SPECIAL" ); // New //un noted code change from original sdk
WEAPON_SPECIAL_HOLD.LinkTo( scriptObject, "WEAPON_SPECIAL_HOLD" ); //
WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
//un noted code change from original sdk
savefile->ReadInt( hideTime );
savefile->ReadFloat( hideDistance );
savefile->ReadInt( hideStartTime );
savefile->ReadFloat( hideStart );
savefile->ReadFloat( hideEnd );
//un noted code change from original sdk
savefile->ReadBool( hide );
savefile->ReadBool( disabled );
savefile->ReadInt( berserk );
savefile->ReadVec3( playerViewOrigin );
savefile->ReadMat3( playerViewAxis );
//un noted code change from original sdk
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();
}*/ //un noted code change from original sdk
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 );
//un noted code change from original sdk
savefile->ReadInt( worldMuzzleFlashHandle );
savefile->ReadRenderLight( worldMuzzleFlash );
if ( worldMuzzleFlashHandle != -1 ) { // DG: enforce getting fresh handle
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
}
savefile->ReadVec3( flashColor );
savefile->ReadInt( muzzleFlashEnd );
savefile->ReadInt( flashTime );
savefile->ReadBool( lightOn );
savefile->ReadBool( silent_fire );
//un noted code change from original sdk
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 );
//un noted code change from original sdk
savefile->ReadJoint( flashJointWorld );
savefile->ReadJoint( barrelJointWorld );
savefile->ReadJoint( ejectJointWorld );
savefile->ReadJoint( meleeJointWorld ); //ivan
//ivan start
savefile->ReadBool( autoMeleeEnabled );
savefile->ReadBool( useMeleeBox );
savefile->ReadFloat( comboMultiplier );
savefile->ReadBounds( meleebox );
savefile->ReadInt( trailNumType );
#ifdef TRAIL_FX_CHAIN
lastBeamInChain.Restore( savefile );
#else
int trailId; //trailGen was saved elsewhere. Here we read its id to retrieve it.
savefile->ReadInt( trailId );
trailGen = gameLocal.trailsManager->FindTrailByUniqueId( trailId );
#ifdef TEST_TRAIL
//the test trail was not saved. Create a new one.
//NOTE: the trail object has been correctly restored by the manager, so it's sill there. We just don't know the id.
testGen = NULL;
#endif
#endif
savefile->ReadInt( trailLowOffset );
savefile->ReadInt( trailHighOffset );
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->ReadInt( nextMeleeSnd ); //ivan
savefile->ReadBool( nozzleFx );
savefile->ReadInt( nozzleFxFade );
savefile->ReadInt( lastAttack );
savefile->ReadInt( nozzleGlowHandle );
savefile->ReadRenderLight( nozzleGlow );
if ( nozzleGlowHandle != -1 ) { // DG: enforce getting fresh handle
nozzleGlowHandle = gameRenderWorld->AddLightDef( &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->ReadObject( reinterpret_cast<idClass *&>( lastMeleeEnt ) ); //ivan //rev 2020 code fixes
#ifdef _DENTONMOD
if( !ChangeProjectileDef( owner->GetProjectileType() ) ) { // if restore fails we must clear the dict
projectileDict.Clear();
}
//All rest New
int particleCount;
savefile->ReadInt( particleCount );
for(int i = 0; i < particleCount; i++) {
WeaponParticle_t newParticle;
memset(&newParticle, 0, sizeof(newParticle));
savefile->Read( &newParticle.particleFlags, sizeof( newParticle.particleFlags ) );
LittleBitField( &newParticle.particleFlags, sizeof( newParticle.particleFlags ) );
idStr prtName, particlename;
savefile->ReadString( prtName );
savefile->ReadString( particlename );
strcpy( newParticle.name, prtName.c_str() );
strcpy( newParticle.particlename, particlename.c_str() );
savefile->ReadInt( newParticle.startTime );
savefile->ReadJoint( newParticle.joint );
if(newParticle.particleFlags.isSmoke) {
newParticle.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particlename, false ) );
}
else {
savefile->ReadRenderEntity( newParticle.renderEntity );
newParticle.modelDefHandle = -1;
}
if( newParticle.particleFlags.isDir ) {
savefile->ReadVec3( newParticle.dir );
}
if( newParticle.particleFlags.isOffset ) {
savefile->ReadVec3( newParticle.offset );
}
weaponParticles.Set(newParticle.name, newParticle);
}
int lightCount;
savefile->ReadInt( lightCount );
for(int i = 0; i < lightCount; i++) {
WeaponLight_t newLight;
memset(&newLight, 0, sizeof(newLight));
savefile->Read( &newLight.lightFlags, sizeof( newLight.lightFlags ) );
LittleBitField( &newLight.lightFlags, sizeof( newLight.lightFlags ) );
idStr lightName;
savefile->ReadString( lightName );
strcpy( newLight.name, lightName.c_str() );
savefile->ReadInt( newLight.startTime );
savefile->ReadJoint( newLight.joint );
savefile->ReadInt( newLight.lightHandle );
savefile->ReadRenderLight( newLight.light );
if ( newLight.lightHandle >= 0 ) {
newLight.lightHandle = gameRenderWorld->AddLightDef( &newLight.light );
}
if( !newLight.lightFlags.isAlwaysOn ) {
savefile->ReadInt( newLight.endTime );
}
if( newLight.lightFlags.isDir ) {
savefile->ReadVec3(newLight.dir);
}
if( newLight.lightFlags.isOffset ) {
savefile->ReadVec3(newLight.offset);
}
weaponLights.Set(newLight.name, newLight);
}
#endif //_DENTONMOD
//ivan start
savefile->ReadInt( maxAmmo );
savefile->ReadInt( fireMode );
savefile->ReadFloat( mouseAimOffsetY );
//ivan end
}
/***********************************************************************
Weapon definition management
***********************************************************************/
/*
================
idWeapon::Clear
================
*/
void idWeapon::Clear( void ) {
CancelEvents( &EV_Weapon_Clear );
DeconstructScriptObject();
scriptObject.Free();
WEAPON_ATTACK.Unlink();
WEAPON_SPECIAL.Unlink(); //new //un noted code change from original sdk
WEAPON_SPECIAL_HOLD.Unlink(); //new //un noted code change from original sdk
WEAPON_RELOAD.Unlink();
WEAPON_NETRELOAD.Unlink();
WEAPON_NETENDRELOAD.Unlink();
WEAPON_NETFIRING.Unlink();
WEAPON_RAISEWEAPON.Unlink();
WEAPON_LOWERWEAPON.Unlink();
//un noted code change from original sdk
if ( worldMuzzleFlashHandle != -1 ) {
gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
worldMuzzleFlashHandle = -1;
}
//un noted code change from original sdk
if ( nozzleGlowHandle != -1 ) {
gameRenderWorld->FreeLightDef( nozzleGlowHandle );
nozzleGlowHandle = -1;
}
memset( &renderEntity, 0, sizeof( renderEntity ) );
renderEntity.entityNum = entityNumber;
//rev 2020 allow weapons to cast shadows
renderEntity.noShadow = false; //true;
renderEntity.noSelfShadow = false; //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; //un noted code change from original sdk
hide = false;
disabled = false;
weaponSmoke = NULL;
weaponSmokeStartTime = 0;
continuousSmoke = false;
strikeSmoke = NULL;
strikeSmokeStartTime = 0;
strikePos.Zero();
strikeAxis = mat3_identity;
nextStrikeFx = 0;
nextMeleeSnd = 0; //ivan
icon = "";
playerViewAxis.Identity();
playerViewOrigin.Zero();
//un noted code change from original sdk
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;
zoomFov = 90;//un noted code change from original sdk
barrelJointWorld = INVALID_JOINT;
flashJointWorld = INVALID_JOINT;
ejectJointWorld = INVALID_JOINT;
meleeJointWorld = INVALID_JOINT; //ivan
// ------------------- New Start
#ifdef _DENTONMOD
//Clean up the weapon particles
for(int i = 0; i < weaponParticles.Num(); i++) {
WeaponParticle_t* part = weaponParticles.GetIndex(i);
if(!part->particleFlags.isSmoke) {
if ( part->modelDefHandle >= 0 )
gameRenderWorld->FreeEntityDef( part->modelDefHandle );
}
}
weaponParticles.Clear();
//Clean up the weapon lights
for(int i = 0; i < weaponLights.Num(); i++) {
WeaponLight_t* light = weaponLights.GetIndex(i);
if ( light->lightHandle != -1 ) {
gameRenderWorld->FreeLightDef( light->lightHandle );
}
}
weaponLights.Clear();
//------------------------------------ New End
#endif //_DENTONMOD
hasBloodSplat = false;
nozzleFx = false;
nozzleFxFade = 1500;
lastAttack = 0;
// lastSpecialFunc = 0; // new //un noted code change from original sdk
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;
//ivan start
lastMeleeEnt = NULL;
isFiring = false;
//isSecFiring = false;
autoMeleeEnabled = false;
useMeleeBox = false;
comboMultiplier = 1.0f;
meleebox.Zero();
trailNumType = TRAIL_NONE;
#ifdef TRAIL_FX_CHAIN
lastBeamInChain = NULL;
#else
//check if trailGen is != null because this is also called by the constructor!
if( trailGen && trailGen->IsEnabled() ){ //this also happens when weapon change. Make sure no trail is going.
trailGen->StopTrail();
}
trailLowOffset = 0;
trailHighOffset = 0;
#endif
//ivan start
mouseAimOffsetY = 0.0f;
//ivan end
}
/*
================
idWeapon::InitWorldModel
================
*/
void idWeapon::InitWorldModel( const idDeclEntityDef *def ) {
//un noted code change from original sdk
assert( def );
const char *model = def->dict.GetString( "model_world" );
const char *attach = def->dict.GetString( "joint_attach" );
//ivan start
if ( model[0] && attach[0] ) {
SetModel( model );
GetPhysics()->SetContents( 0 );
GetPhysics()->SetClipModel( NULL, 1.0f );
BindToJoint( owner, attach, true );
GetPhysics()->SetOrigin( vec3_origin );
GetPhysics()->SetAxis( mat3_identity );
// supress model in player views, but allow it in mirrors and remote views
renderEntity.suppressSurfaceInViewID = owner->entityNumber+1;
renderEntity.suppressShadowInViewID = owner->entityNumber+1;
renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
} else {
SetModel( "" );
}
// find some joints in the model for locating effects
barrelJointWorld = animator.GetJointHandle( def->dict.GetString( "world_barrel_joint", "barrel" ) );
flashJointWorld = animator.GetJointHandle( "flash" );
ejectJointWorld = animator.GetJointHandle( "eject" );
meleeJointWorld = animator.GetJointHandle( "melee" ); //ivan
//ivan end
//ivan - initialize the tracer bbox
meleebox.Zero();
float expansion = def->dict.GetFloat("melee_tracerWidth","0");
if(expansion > 0){
useMeleeBox = true;
meleebox.ExpandSelf( expansion );
}
}
/*
================
idWeapon::GetWeaponDef
================
*/
void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) {
const char *shader;
const char *objectType;
//un noted code change from original sdk
const char *guiName;
//un noted code change from original sdk
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" );
//ivan start
maxAmmo = owner->inventory.MaxAmmoForAmmoClass( owner, weaponDef->dict.GetString( "ammoType" ) );
fireMode = weaponDef->dict.GetInt( "fireMode", "0" );
//ivan end
icon = weaponDef->dict.GetString( "icon" );
silent_fire = weaponDef->dict.GetBool( "silent_fire" );
powerAmmo = weaponDef->dict.GetBool( "powerAmmo" );
//un noted code change from original sdk
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;
//ivan start
nextMeleeSnd = 0;
trailLowOffset = weaponDef->dict.GetInt( "trailLowOffset", "0" );
trailHighOffset = weaponDef->dict.GetInt( "trailHighOffset", "0" );
//spread
//UpdSpreadSettings();
//ivan end
// setup the world model
InitWorldModel( weaponDef );
// copy the sounds from the weapon model def into our spawnargs //un noted code change from original sdk
const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" );
while( kv ) {
spawnArgs.Set( kv->GetKey(), kv->GetValue() );
kv = weaponDef->dict.MatchPrefix( "snd_", kv );
}
#ifdef _DENTONMOD
#else
// get the projectile
projectileDict.Clear();
const char *projectileName = weaponDef->dict.GetString( "def_projectile" ); //un noted code change from original sdk
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;
}
}
}
#endif
// 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" );
//ivan start
memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) );
//nozzleGlow will only be in first person //TODO: use nozzle stuff in 3th person?
nozzleGlow.allowLightInViewID = owner->entityNumber+1;
worldMuzzleFlash.suppressLightInViewID = owner->entityNumber+1;
worldMuzzleFlash.allowLightInViewID = 0;
worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber;
worldMuzzleFlash.pointLight = flashPointLight;
worldMuzzleFlash.shader = flashShader;
worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
worldMuzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
worldMuzzleFlash.lightRadius[0] = flashRadius;
worldMuzzleFlash.lightRadius[1] = flashRadius;
worldMuzzleFlash.lightRadius[2] = flashRadius;
if ( !flashPointLight ) {
worldMuzzleFlash.target = flashTarget;
worldMuzzleFlash.up = flashUp;
worldMuzzleFlash.right = flashRight;
worldMuzzleFlash.end = flashTarget;
}
//ivan end
//-----------------------------------
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;
}
//In D3XP we use ammo as soon as it is moved into the clip. This allows for weapons that share ammo
//owner->inventory.UseAmmo(ammoType, ammoClip); //ivan - commented out!
}
renderEntity.gui[ 0 ] = NULL;
guiName = weaponDef->dict.GetString( "gui" );
if ( guiName[0] ) {
renderEntity.gui[ 0 ] = uiManager->FindGui( guiName, true, false, true );
}
zoomFov = weaponDef->dict.GetInt( "zoomFov", "70" );
berserk = weaponDef->dict.GetInt( "berserk", "2" );
//ivan start
mouseAimOffsetY = weaponDef->dict.GetFloat( "mouseAimOffsetY", "0" );
//ivan end
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_SPECIAL.LinkTo( scriptObject, "WEAPON_SPECIAL" ); //new //un noted code change from original sdk
WEAPON_SPECIAL_HOLD.LinkTo( scriptObject, "WEAPON_SPECIAL_HOLD" ); //
WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
spawnArgs = weaponDef->dict;
#ifdef _DENTONMOD
projectileDict.Clear();
ChangeProjectileDef( owner->GetProjectileType() );
#endif
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();
#ifdef _DENTONMOD
InitWeaponFx();
#endif
}
/*********************
idWeapon::initWeaponFx
**********************/
#ifdef _DENTONMOD
void idWeapon::InitWeaponFx( void )
{
const idKeyValue *pkv;
/*
// Create new joints
const idKeyValue *pkv = weaponDef->dict.MatchPrefix( "weapon_createjoint", NULL );
while( pkv ) {
WeaponJoint_t newJoint;
jointHandle_t joint;
idStr jointName = pkv->GetValue();
bool isOnWorldModel = weaponDef->dict.GetBool( va( "%s_onWorldModel", jointName.c_str() ), "0");
idStr referenceJoint = weaponDef->dict.GetString(va("%s_referencejoint", jointName.c_str()));
idAnimator *weapAnimator;
if ( !isOnWorldModel ) {
joint = animator.GetJointHandle( referenceJoint.c_str() );
weapAnimator = &animator;
}
else {
idEntity *ent = worldModel.GetEntity();
weapAnimator = ent->GetAnimator();
joint = weapAnimator->GetJointHandle( referenceJoint.c_str() );
}
if( joint != INVALID_JOINT ) { // To create a new joint we need a reference to an existing joint
idVec3 offset, origin, dir;
idMat3 axis;
GetGlobalJointTransform( !isOnWorldModel, joint, origin, axis );
if( weaponDef->dict.GetVector( va( "%s_offset", jointName.c_str() ), "0 0 0", offset) ) {
origin += axis * offset;
}
if ( weaponDef->dict.GetVector( va( "%s_dir", jointName.c_str() ), "1 0 0", dir) ) {
idVec3 &xAxis = axis[0];
xAxis = axis * dir;
xAxis.Normalize();
axis = xAxis.ToMat3 ();
}
strcpy( newJoint.name, jointName.c_str() );
newJoint.joint = (jointHandle_t)weapAnimator->NumJoints();
weapAnimator->SetJointPos( newJoint.joint, JOINTMOD_WORLD_OVERRIDE, origin );
weapAnimator->SetJointAxis( newJoint.joint, JOINTMOD_WORLD_OVERRIDE, axis );
weaponJoints.Set( jointName.c_str(), newJoint );
}
pkv = weaponDef->dict.MatchPrefix( "weapon_createjoint", pkv );
}
// Update existing joints
pkv = weaponDef->dict.MatchPrefix( "weapon_updatejoint", NULL );
while( pkv ) {
jointHandle_t refJoint, joint;
idStr jointName = pkv->GetValue();
bool isOnWorldModel = weaponDef->dict.GetBool( va( "%s_onWorldModel", jointName.c_str() ), "0");
idStr refJointName = weaponDef->dict.GetString(va("%s_referencejoint", jointName.c_str()));
idAnimator *weapAnimator;
if ( !isOnWorldModel ) {
refJoint = animator.GetJointHandle( refJointName.c_str() );
joint = animator.GetJointHandle( jointName.c_str() );
weapAnimator = &animator;
}
else {
idEntity *ent = worldModel.GetEntity();
weapAnimator = ent->GetAnimator();
refJoint = weapAnimator->GetJointHandle( refJointName.c_str() );
joint = weapAnimator->GetJointHandle( jointName.c_str() );
}
if( joint != INVALID_JOINT && refJoint != INVALID_JOINT ) {
idVec3 offset, origin, dir;
idMat3 axis;
GetGlobalJointTransform( !isOnWorldModel, refJoint, origin, axis );
if( weaponDef->dict.GetVector( va( "%s_offset", jointName.c_str() ), "0 0 0", offset) ) {
origin += axis * offset;
}
if ( weaponDef->dict.GetVector( va( "%s_dir", jointName.c_str() ), "1 0 0", dir) ) {
idVec3 &xAxis = axis[0];
xAxis = axis * dir;
xAxis.Normalize();
axis = xAxis.ToMat3 ();
}
weapAnimator->SetJointAxis( joint, JOINTMOD_WORLD_OVERRIDE, axis );
weapAnimator->SetJointPos( joint, JOINTMOD_WORLD_OVERRIDE, origin );
}
pkv = weaponDef->dict.MatchPrefix( "weapon_updatejoint", pkv );
}
*/
//Initialize the particles
pkv = weaponDef->dict.MatchPrefix( "weapon_particle", NULL );
while( pkv ) {
WeaponParticle_t newParticle;
memset( &newParticle, 0, sizeof( newParticle ) );
idStr prtName = pkv->GetValue();
strcpy(newParticle.name, prtName.c_str());
newParticle.particleFlags.isOffset = weaponDef->dict.GetVector( va( "%s_offset", prtName.c_str() ), "0 0 0", newParticle.offset ); // this offset will be added to the origin
newParticle.particleFlags.isDir = weaponDef->dict.GetVector( va( "%s_dir", prtName.c_str() ), "1 0 0", newParticle.dir ); // adjust the dir
newParticle.particleFlags.isViewDir = weaponDef->dict.GetBool( va( "%s_useViewDir", prtName.c_str() ), "0"); //ivan
newParticle.particleFlags.isOnWorldModel = weaponDef->dict.GetBool( va( "%s_onWorldModel", prtName.c_str() ), "0");
idStr jointName = weaponDef->dict.GetString(va("%s_joint", prtName.c_str()));
// WeaponJoint_t *customJoint;
//ivan start
newParticle.particleFlags.isSmoke = weaponDef->dict.GetBool(va("%s_smoke", prtName.c_str()), "0");
if ( newParticle.particleFlags.isOnWorldModel ) { //now worldmodel is the weapon model itself
newParticle.joint = animator.GetJointHandle( jointName.c_str() );
}
else { //not supported
newParticle.joint = INVALID_JOINT;
}
/*
if( newParticle.joint == INVALID_JOINT ){
gameLocal.Printf( "joint %s for %s is INVALID\n", jointName.c_str(), prtName.c_str());
}else{
gameLocal.Printf( "joint %s for %s is OK\n", jointName.c_str(), prtName.c_str());
}
*/
//ivan end
newParticle.particleFlags.isActive = false;
newParticle.startTime = 0;
idStr particle = weaponDef->dict.GetString(va("%s_particle", prtName.c_str()));
strcpy(newParticle.particlename, particle.c_str());
newParticle.particleFlags.isContinuous = weaponDef->dict.GetBool(va("%s_continuous", prtName.c_str()), "1");
if(newParticle.particleFlags.isSmoke) {
// newParticle.particleFlags.isContinuous = weaponDef->dict.GetBool(va("%s_continuous", prtName.c_str()), "1");
newParticle.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particle.c_str(), false ) );
}
else {
newParticle.renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0;
newParticle.renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0;
newParticle.renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0;
newParticle.renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0;
if ( newParticle.particleFlags.isOnWorldModel ) {
newParticle.renderEntity.suppressSurfaceInViewID = owner->entityNumber+1; // Make sure this is third person effect only.
}
else {
newParticle.renderEntity.weaponDepthHack = weaponDef->dict.GetBool(va("%s_weaponDepthHack", prtName.c_str()), "0");
newParticle.renderEntity.allowSurfaceInViewID = owner->entityNumber+1; // Make sure this is first person effect only.
}
const idDeclModelDef *modelDef = static_cast<const idDeclModelDef *>( declManager->FindType( DECL_MODELDEF, newParticle.particlename ) );
if ( modelDef ) {
newParticle.renderEntity.hModel = renderModelManager->FindModel( newParticle.particlename );
}
newParticle.modelDefHandle = -1;
}
weaponParticles.Set(prtName.c_str(), newParticle);
pkv = weaponDef->dict.MatchPrefix( "weapon_particle", pkv );
}
const idKeyValue *lkv = weaponDef->dict.MatchPrefix( "weapon_light", NULL );
while( lkv ) {
WeaponLight_t newLight;
memset( &newLight, 0, sizeof( newLight ) );
newLight.lightHandle = -1;
newLight.lightFlags.isActive = false;
newLight.startTime = 0;
idStr lightName = lkv->GetValue(), debug;
strcpy(newLight.name, lightName.c_str());
newLight.lightFlags.isOffset = weaponDef->dict.GetVector( va( "%s_offset", lightName.c_str() ), "0 0 0", newLight.offset ); // this offset will be added to the origin
newLight.lightFlags.isDir = weaponDef->dict.GetVector( va( "%s_dir", lightName.c_str() ), "1 0 0", newLight.dir ); // Direction can be adjusted with this
idStr lightShader = weaponDef->dict.GetString(va("%s_shader", lightName.c_str()));
newLight.light.shader = declManager->FindMaterial( lightShader.c_str(), false );
float radius = weaponDef->dict.GetFloat(va("%s_radius", lightName.c_str()));
newLight.light.lightRadius[0] = radius;
newLight.light.lightRadius[1] = radius;
newLight.light.lightRadius[2] = radius;
newLight.lightFlags.isAlwaysOn = weaponDef->dict.GetBool( va( "%s_alwaysOn", lightName.c_str () ), "1" );
newLight.light.pointLight = weaponDef->dict.GetBool( va( "%s_pointLight", lightName.c_str() ), "0" );
newLight.light.noShadows = weaponDef->dict.GetBool( va( "%s_noShadows", lightName.c_str() ), "0" );
if (!newLight.light.pointLight) {
newLight.light.target = weaponDef->dict.GetVector( va( "%s_target", lightName.c_str() ) );
newLight.light.up = weaponDef->dict.GetVector( va( "%s_up", lightName.c_str() ) );
newLight.light.right = weaponDef->dict.GetVector( va( "%s_right", lightName.c_str() ) );
newLight.light.end = newLight.light.target;
}
if (!newLight.lightFlags.isAlwaysOn) {
newLight.endTime = SEC2MS( weaponDef->dict.GetFloat( va( "%s_endTime", lightName.c_str() ), "0.25" ) );
}
idVec3 lightColor = weaponDef->dict.GetVector( va( "%s_color", lightName.c_str() ), "1 1 1");
newLight.light.shaderParms[ SHADERPARM_RED ] = lightColor[0];
newLight.light.shaderParms[ SHADERPARM_GREEN ] = lightColor[1];
newLight.light.shaderParms[ SHADERPARM_BLUE ] = lightColor[2];
newLight.light.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
newLight.lightFlags.isOnWorldModel = weaponDef->dict.GetBool( va( "%s_onWorldModel", lightName.c_str() ), "0");
idStr jointName = weaponDef->dict.GetString(va("%s_joint", lightName.c_str()));
if ( newLight.lightFlags.isOnWorldModel ) { // third person only light
newLight.joint = animator.GetJointHandle( jointName.c_str() ); //ivan
newLight.light.suppressLightInViewID = owner->entityNumber+1;
}
else { //not supported
newLight.joint = INVALID_JOINT; //ivan
newLight.light.allowLightInViewID = owner->entityNumber+1;
}
weaponLights.Set(lightName.c_str(), newLight);
lkv = weaponDef->dict.MatchPrefix( "weapon_light", lkv );
}
}
#endif //_DENTONMOD
/***********************************************************************
GUIs
***********************************************************************/
/*
================
idWeapon::Icon
================
*/
const char *idWeapon::Icon( void ) const {
return icon;
}
/*
================
idWeapon::UpdateGUI
================
*/
void idWeapon::UpdateGUI( void ) {
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) );//new //un noted code change from original sdk
// 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 ) ); //un noted code change from original sdk
renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount ) );//new
}
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 ) );
//Let the HUD know the total amount of ammo regardless of the ammo required value //un noted code change from original sdk
renderEntity.gui[ 0 ]->SetStateString( "player_ammo_count", va("%i", AmmoCount())); //new
}
/***********************************************************************
Model and muzzleflash
***********************************************************************/
/********************************************************************
idWeapon::UpdateWeaponFx( void )
Updates custom weapon particles and lights- By Clone JC Denton
********************************************************************/
#ifdef _DENTONMOD
void idWeapon::UpdateWeaponFx( void )
{
for( int i = 0; i < weaponParticles.Num(); i++ ) {
WeaponParticle_t* part = weaponParticles.GetIndex(i);
//ivan - skip view particles!
if( !part->particleFlags.isOnWorldModel ) continue;
if( part->particleFlags.isActive ) {
if(part->particleFlags.isSmoke) {
if(part->joint != INVALID_JOINT) {
//ivan start
/*
//was
GetGlobalJointTransform( true, part->joint, muzzleOrigin, muzzleAxis );
*/
GetGlobalJointTransform( part->joint, muzzleOrigin, muzzleAxis );
if( part->particleFlags.isViewDir ){ //use view dir awyway
muzzleAxis = playerViewAxis;
}else{
//direction fix!
idVec3 tmp = muzzleAxis[2];
muzzleAxis[2] = muzzleAxis[0];
muzzleAxis[0] = -tmp;
}
//ivan end
} else {
// default to going straight out the view
muzzleOrigin = playerViewOrigin;
muzzleAxis = playerViewAxis;
}
if ( part->particleFlags.isOffset ) {
muzzleOrigin += muzzleAxis * part->offset;
}
if ( part->particleFlags.isDir ) {
idVec3 &dir = muzzleAxis[ 0 ];
dir = muzzleAxis * part->dir;
dir.Normalize();
muzzleAxis = dir.ToMat3 ();
}
if ( !gameLocal.smokeParticles->EmitSmoke( part->particle, part->startTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis ) ) {
if ( !part->particleFlags.isContinuous ) {
part->particleFlags.isActive = false; // all done
part->startTime = 0;
}
else {
part->startTime = gameLocal.time; // for continuous effect
}
}
}
else {
if( part->renderEntity.hModel ) {
//ivan start
/*
//was:
GetGlobalJointTransform( !part->particleFlags.isOnWorldModel, part->joint, part->renderEntity.origin, part->renderEntity.axis );
if ( part->particleFlags.isOffset ) {
part->renderEntity.origin += part->renderEntity.axis * part->offset;
}
if ( part->particleFlags.isDir ) {
idVec3 &dir = part->renderEntity.axis[ 0 ];
dir = part->renderEntity.axis * part->dir;
dir.Normalize();
part->renderEntity.axis = dir.ToMat3();
}
*/
GetGlobalJointTransform( part->joint, muzzleOrigin, muzzleAxis );
if( part->particleFlags.isViewDir ){ //use view dir awyway
muzzleAxis = playerViewAxis;
}
//direction fix!
//do this also is isViewDir, so the "up" of the particle means "forward" also in that case
idVec3 tmp = muzzleAxis[2];
muzzleAxis[2] = muzzleAxis[0];
muzzleAxis[0] = -tmp;
if ( part->particleFlags.isDir ) {
idVec3 &dir = muzzleAxis[ 0 ];
dir = muzzleAxis * part->dir;
dir.Normalize();
muzzleAxis = dir.ToMat3();
}
if ( part->particleFlags.isOffset ) {
muzzleOrigin += muzzleAxis * part->offset;
}
part->renderEntity.origin = muzzleOrigin;
part->renderEntity.axis = muzzleAxis;
//ivan end
if ( part->modelDefHandle != -1 ) {
gameRenderWorld->UpdateEntityDef( part->modelDefHandle, &part->renderEntity );
}
else {
part->renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
part->renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.RandomFloat(); // For effects like muzzleflashes etc.
part->modelDefHandle = gameRenderWorld->AddEntityDef( &part->renderEntity );
}
}
}
}
}
for(int i = 0; i < weaponLights.Num(); i++) {
WeaponLight_t* light = weaponLights.GetIndex(i);
//ivan - skip view lights!
if( !light->lightFlags.isOnWorldModel ) continue;
if(light->lightFlags.isActive) {
if (GetGlobalJointTransform( light->joint, light->light.origin, light->light.axis )) {
if ( light->lightFlags.isOffset ) {
light->light.origin += light->light.axis[ 0 ] * light->offset[ 0 ] + light->light.axis[ 1 ] * light->offset[ 1 ] + light->light.axis[ 2 ] * light->offset[ 2 ];
}
if ( light->lightFlags.isDir ) {
idVec3 &dir = light->light.axis[ 0 ];
dir = light->light.axis * light->dir;
dir.Normalize();
light->light.axis = dir.ToMat3();
}
if ( ( light->lightHandle != -1 ) ) {
gameRenderWorld->UpdateLightDef( light->lightHandle, &light->light );
}
else {
light->light.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
light->light.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ]; // For muzzleflashes.
light->lightHandle = gameRenderWorld->AddLightDef( &light->light );
}
if ( !light->lightFlags.isAlwaysOn && ( gameLocal.time >= light->endTime ) ) {
light->endTime -= light->startTime; // readjust the value.
Event_StopWeaponLight( light->name );
}
}
}
}
}
/********************************************************************
idWeapon::StopWeaponFx( void )
Stops custom weapon particles and lights temporarily- By Clone JC Denton
********************************************************************/
void idWeapon::StopWeaponFx( void )
{
for( int i = 0; i < weaponParticles.Num(); i++ ) {
WeaponParticle_t* part = weaponParticles.GetIndex(i);
if(part->particleFlags.isActive) {
//Free the particles
if( !part->particleFlags.isSmoke && part->modelDefHandle > -1 ) {
gameRenderWorld->FreeEntityDef( part->modelDefHandle );
part->modelDefHandle = -1;
}
}
}
for(int i = 0; i < weaponLights.Num(); i++) {
WeaponLight_t* light = weaponLights.GetIndex(i);
if( light->lightFlags.isActive ) {
if(light->lightHandle != -1) {
gameRenderWorld->FreeLightDef( light->lightHandle );
light->lightHandle = -1;
}
}
}
}
#endif
/*
================
idWeapon::UpdateFlashPosition
================
*/
void idWeapon::UpdateFlashPosition( void ) {
/* REMOVED BY REVILITY FOR IVAN'S LAUNCH FROM BARREL FIX WHICH IS THE CODE BELOW IT
// the flash has an explicit joint for locating it
GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
// 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;
// put the world muzzle flash on the end of the joint, no matter what
GetGlobalJointTransform( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
}
*/
/*
if ( flashJointView != INVALID_JOINT ) { //ivan
// the flash has an explicit joint for locating it
GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
}
*/
/* ivan - commented out - don't waste time for useless things
// 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;
*/
// put the world muzzle flash on the end of the joint, no matter what
GetGlobalJointTransform( flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
}
/*
================
idWeapon::MuzzleFlashLight
================
*/
void idWeapon::MuzzleFlashLight( void ) {
if ( !lightOn && ( !g_muzzleFlash.GetBool() || !worldMuzzleFlash.lightRadius[0] ) ) {
return;
}
//REVILITY START LAUNCH FROM BARREL FIX
if ( flashJointWorld == INVALID_JOINT ) {
return;
}
//REVILITY END LAUNCH FROM BARREL FIX
UpdateFlashPosition();
// these will be different each fire
//un noted code change from original sdk
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 ( worldMuzzleFlashHandle != -1 ) {
gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
} else {
//un noted code change from original sdk
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
}
}
/*
================
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( const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) {
//ivan - world model is now the model itself
if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis(); //un noted code change from original sdk
axis = axis * GetPhysics()->GetAxis();
return true;
}
offset = GetPhysics()->GetOrigin(); //viewWeaponOrigin; //un noted code change from original sdk
axis = GetPhysics()->GetAxis(); //viewWeaponAxis;
return false;
//ivan end
}
/*
================
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 ( !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();
//un noted code change from original sdk
muzzleFlashEnd = 0;
}
/*
================
idWeapon::ShowWeapon
================
*/
void idWeapon::ShowWeapon( void ) {
Show();
//un noted code change from original sdk
if ( lightOn ) {
MuzzleFlashLight();
}
}
/*
================
idWeapon::HideWorldModel
================
*/
void idWeapon::HideWorldModel( void ) {
Hide(); //un noted code change from original sdk
}
/*
================
idWeapon::ShowWorldModel
================
*/
void idWeapon::ShowWorldModel( void ) {
Show(); //un noted code change from original sdk
}
/*
================
idWeapon::OwnerDied
================
*/
void idWeapon::OwnerDied( void ) {
if ( isLinked ) {
SetState( "OwnerDied", 0 );
thread->Execute();
}
Hide();
//un noted code change from original sdk
// 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::BeginSpecialFunction
================
*/
void idWeapon::BeginSpecialFunction( bool keyTapped ) { // new
if ( isLinked ) {
if (keyTapped){
WEAPON_SPECIAL = true;
}
WEAPON_SPECIAL_HOLD = true;
}
}
/*
================
idWeapon::EndSpecialFunc
================
*/
void idWeapon::EndSpecialFunction( void ) { //New
if ( !WEAPON_SPECIAL_HOLD.IsLinked() ) {
return;
}
if ( WEAPON_SPECIAL_HOLD ) {
WEAPON_SPECIAL_HOLD = false;
}
}
/*
================
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 ) { //un noted code change from original sdk
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 ) { //un noted code change from original sdk
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 );
//ivan start
//was:
//idMoveableItem::DropItem( classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
return idMoveableItem::DropItem( classname, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
//ivan end
}
/***********************************************************************
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;
/* //un noted code change from original sdk
if ( ventLightJointView == INVALID_JOINT ) {
return;
}
//
// vent light
//
if ( nozzleGlowHandle == -1 ) { //un noted code change from original sdk
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 ); //un noted code change from original sdk
nozzleGlow.shaderParms[ SHADERPARM_RED ] = nozzleGlowColor.x * s; //un noted code change from original sdk
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( ejectJointWorld, localOrigin, localAxis ) ) { //ivan
//was: 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 ) { //un noted code change from original sdk
int time;
float amount;
idAngles ang;
idVec3 offset;
time = kick_endtime - gameLocal.time; //un noted code change from original sdk
if ( time <= 0 ) {
return;
}
if ( muzzle_kick_maxtime <= 0 ) { //un noted code change from original sdk
return;
}
if ( time > muzzle_kick_maxtime ) { //un noted code change from original sdk
time = muzzle_kick_maxtime;
}
amount = ( float )time / ( float )muzzle_kick_maxtime; //un noted code change from original sdk
ang = muzzle_kick_angles * amount;
offset = muzzle_kick_offset * amount;
origin = origin - axis * offset; //un noted code change from original sdk
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;
WEAPON_SPECIAL = false;
}
/*
================
idWeapon::AlertMonsters
================
*/
void idWeapon::AlertMonsters( void ) {
trace_t tr;
idEntity *ent;
idVec3 end = worldMuzzleFlash.origin + worldMuzzleFlash.axis * worldMuzzleFlash.target; //un noted code change from original sdk
gameLocal.clip.TracePoint( tr, worldMuzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner ); //un noted code change from original sdk
if ( g_debugWeapon.GetBool() ) {
gameRenderWorld->DebugLine( colorYellow, worldMuzzleFlash.origin, end, 0 ); //un noted code change from original sdk
gameRenderWorld->DebugArrow( colorGreen, worldMuzzleFlash.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 += worldMuzzleFlash.axis * worldMuzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f ); //un noted code change from original sdk
end += worldMuzzleFlash.axis * worldMuzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f );
gameLocal.clip.TracePoint( tr, worldMuzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
if ( g_debugWeapon.GetBool() ) {
gameRenderWorld->DebugLine( colorYellow, worldMuzzleFlash.origin, end, 0 ); //un noted code change from original sdk
gameRenderWorld->DebugArrow( colorGreen, worldMuzzleFlash.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::PresentWeapon
================
*/
void idWeapon::PresentWeapon( bool showViewModel ) { //showViewModel is "ui_showGun". We want to make everything to work in 3th person even if this is false //un noted code change from original sdk
playerViewOrigin = owner->firstPersonViewOrigin;
playerViewAxis = owner->firstPersonViewAxis;
/*
// calculate weapon position based on player movement bobbing
owner->CalculateViewWeaponPos( viewWeaponOrigin, viewWeaponAxis ); //no: now weapon is bound to joint //un noted code change from original sdk
// hide offset is for dropping the gun when approaching a GUI or NPC //no: now weapon is bound to joint //un noted code change from original sdk
// This is simpler to manage than doing the weapon put-away animation
if ( gameLocal.time - hideStartTime < hideTime ) { //un noted code change from original sdk
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 ];
*/
// kick up based on repeat firing //no: now weapon is bound to joint //un noted code change from original sdk
//MuzzleRise( viewWeaponOrigin, viewWeaponAxis );
/* //un noted code change from original sdk
// set the physics position and orientation //no: now weapon is bound to joint
GetPhysics()->SetOrigin( viewWeaponOrigin );
GetPhysics()->SetAxis( viewWeaponAxis );
*/
//UpdateVisuals(); //not needed now?
// update the weapon script
UpdateScript();
UpdateGUI();
// update animation
UpdateAnimation();
Present(); //always show the model //un noted code change from original sdk
//ivan - allow shadows
renderEntity.suppressShadowInViewID = 0;
if ( nozzleFx ) {
UpdateNozzleFx();
}
// muzzle smoke
if ( !disabled && weaponSmoke && ( weaponSmokeStartTime != 0 ) ) { //ivan - showViewModel commented out
// use the barrel joint if available
if ( barrelJointWorld != INVALID_JOINT ) { //ivan - compare with INVALID_JOINT !
GetGlobalJointTransform( barrelJointWorld, 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 ( strikeSmoke && strikeSmokeStartTime != 0 ) { //ivan - showViewModel commented out
// spit out a particle
if ( !gameLocal.smokeParticles->EmitSmoke( strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis ) ) {
strikeSmokeStartTime = 0;
}
}
#ifdef _DENTONMOD
//ivan start
//disabled flag ensures that fx wont be played when entering cinematic
if ( !disabled ) {
UpdateWeaponFx();
}
//ivan end
#endif //_DENTONMOD
// remove the muzzle flash light when it's done
if ( ( !lightOn && ( gameLocal.time >= muzzleFlashEnd ) ) || IsHidden() ) {
//un noted code change from original sdk
if ( worldMuzzleFlashHandle != -1 ) {
gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
worldMuzzleFlashHandle = -1;
}
}
// update the muzzle flash light, so it moves with the gun
if ( worldMuzzleFlashHandle != -1 ) { //un noted code change from original sdk
UpdateFlashPosition();
//un noted code change from original sdk
gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
// wake up monsters with the flashlight
if ( !gameLocal.isMultiplayer && lightOn && !owner->fl.notarget ) {
AlertMonsters();
}
}
//un noted code change from original sdk
if ( status != WP_READY && sndHum ) {
StopSound( SND_CHANNEL_BODY, false );
}
UpdateSound();
//ivan start
if ( autoMeleeEnabled && !disabled ) { //no in cinematic
EvaluateMelee();
}
#ifdef TRAIL_FX_CHAIN
//the beams'chain is updated inside EvaluateMelee();
#else
if( trailGen->IsEnabled() ){
UpdateTrailVerts();
}
#endif
//ivan end
}
//Ivan start
/*
=====================
idWeapon::StartAutoMelee
=====================
*/
void idWeapon::StartAutoMelee( float dmgMult, int trailNum ) {
if ( g_debugWeapon.GetBool() ) {
gameLocal.Printf("idWeapon::StartAutoMelee - dmgMult: %f,trailNum: %d - time:%d\n", dmgMult, trailNum, gameLocal.time);
}
comboMultiplier = dmgMult;
lastMeleeEnt = NULL; //reset it so that can be hit again
autoMeleeEnabled = true;
nextStrikeFx = gameLocal.time + 100; //delay snd+prt for LOW priority entities after the beginning of the attack
nextMeleeSnd = gameLocal.time + 100; //don't play snd on world too early - this could not be used
//-- trail --
//make sure no trail is started if there is ever started if there is no melee joint
if ( meleeJointWorld == INVALID_JOINT ) {
return;
}
#ifdef TRAIL_FX_CHAIN
lastBeamInChain = NULL; //next node will be the first of the chain. This also allows the beam type to change.
trailNumType = ( trailNum >= 0 ) ? trailNum : TRAIL_NONE;
#else
if ( trailNum < 0 ){ //no trail. stop the current one, if any.
if( trailGen->IsEnabled() ){
trailGen->FadeTrail();
}
trailNumType = TRAIL_NONE;
}else{
if( trailNumType == trailNum ){ //last trail type used was exactly this one
trailGen->RestartTrail(); //this is faster
}else{
trailGen->StartTrail( weaponDef->dict.GetString( va( "def_trail%d", trailNum ) ) );
}
trailNumType = trailNum;
#ifdef TEST_TRAIL
if( trailNumType != 0){
//random remove test
int randPos = 1 + gameLocal.random.RandomInt( 8 ); //never remove the first one... weapon is using it
idTrailGenerator* toRemove = gameLocal.trailsManager->FindTrailByLocalPos( randPos );
gameLocal.trailsManager->RemoveTrailGen( toRemove );
toRemove = NULL;
}else{
//create new test
testGen = gameLocal.trailsManager->NewTrailGen();
testGen->StartTrail( weaponDef->dict.GetString( va( "def_trail%d", trailNum ) ) );
}
#endif
}
#endif
}
/*
=====================
idWeapon::StopAutoMelee
=====================
*/
void idWeapon::StopAutoMelee( void ) {
comboMultiplier = 1.0f;
lastMeleeEnt = NULL; //don't remember it in the future
autoMeleeEnabled = false;
//beam
#ifdef TRAIL_FX_CHAIN
trailNumType = TRAIL_NONE; //turn off the trail
lastBeamInChain = NULL; //next node will be the first of the chain. This also allows the beam type to change.
#else
//note: don't set trailNumType = TRAIL_NONE in this case.
//The trail will start fading and could be restored wihout changing the type.
if( trailGen->IsEnabled() ){
trailGen->FadeTrail();
}
#endif
}
/*
=====================
idWeapon::Event_StartAutoMelee
=====================
*/
void idWeapon::Event_StartAutoMelee( float dmgMult, int trailNum ) {
StartAutoMelee( dmgMult, trailNum );
}
/*
=====================
idWeapon::Event_StopAutoMelee
=====================
*/
void idWeapon::Event_StopAutoMelee( void ) {
StopAutoMelee();
}
#ifdef TRAIL_FX_CHAIN
/*
=====================
idWeapon::SpawnMeleeBeam
NOTE: this only spawns the first elem of the chain. The other will be just a copy of this one.
=====================
*/
idBeam * idWeapon::SpawnMeleeBeam( const idVec3 &pos ) {
idEntity *ent;
const char *classname = weaponDef->dict.GetString( va( "def_meleeBeam%d", trailNumType ) );
if ( !classname[0] ) {
return NULL;
}
const idDict *beam_args = gameLocal.FindEntityDefDict( classname, false );
if ( !beam_args ) {
gameLocal.Error( "Unknown classname '%s'", classname );
}
gameLocal.SpawnEntityDef( *beam_args, &ent, false );
if ( !ent || !ent->IsType( idBeam::Type ) ) {
gameLocal.Error( "Beam entity is not an idBeam" );
}
ent->SetOrigin(pos);
//gameLocal.Printf("Created first node at '%s'\n", pos.ToString() );
return ( static_cast< idBeam *>( ent ) );
}
#else
//custom geometry trail only
void idWeapon::UpdateTrailVerts( void ) {
//meleeJointWorld should be a valid joint. It is checked when we try to start a trail.
GetGlobalJointTransform( meleeJointWorld, meleeJointOrigin, meleeJointAxis ); //to do: upd this somewhere else?
trailGen->AddNewPoints( meleeJointOrigin + meleeJointAxis[0] * trailLowOffset, meleeJointOrigin + meleeJointAxis[0] * trailHighOffset );
#ifdef TEST_TRAIL
if( testGen && testGen->IsEnabled() ){
testGen->AddNewPoints( meleeJointOrigin + meleeJointAxis[0] * 20, meleeJointOrigin + meleeJointAxis[0] * 50);
}
#endif
}
#endif
/*
=====================
idWeapon::EvaluateMelee
=====================
*/
bool idWeapon::EvaluateMelee( void ) {
idEntity *ent;
trace_t tr;
if ( !meleeDef ) {
gameLocal.Error( "No meleeDef on '%s'", weaponDef->dict.GetString( "classname" ) );
}
if ( !gameLocal.isClient ) {
idVec3 start;
idVec3 end;
//get origin end axis of the joint "melee" if available
if ( meleeJointWorld == INVALID_JOINT ) {
//gameLocal.Printf( "idWeapon::EvaluateMelee - Invalid joint 'melee' \n" );
start = playerViewOrigin;
end = start + playerViewAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
}else{
GetGlobalJointTransform( meleeJointWorld, meleeJointOrigin, meleeJointAxis ); //to do: upd this somewhere else?
start = meleeJointOrigin;
end = start + meleeJointAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
//gameLocal.Printf( "idWeapon::EvaluateMelee - start %f %f %f \n",start[0],start[1],start[2] );
}
#ifdef TRAIL_FX_CHAIN
//fx test start
if( trailNumType != TRAIL_NONE ){
idVec3 beamPos;
if ( meleeJointWorld == INVALID_JOINT ) { //this should not happen...
beamPos = end;
}else{
beamPos = start + meleeJointAxis[0] * ( meleeDistance/2 );
}
if( lastBeamInChain.GetEntity() ){
lastBeamInChain = lastBeamInChain.GetEntity()->AddChainNodeAtPos( beamPos );
}else{ //first one!
lastBeamInChain = SpawnMeleeBeam( beamPos );
}
if( lastBeamInChain.GetEntity() ){
//NOTE: make sure they auto-remove themself!
lastBeamInChain.GetEntity()->PostEventMS( &EV_FadeBeamColor, 1 ); //wait 1 frame so it has time to check its targets!
}
}
//fx test end
#endif
if(useMeleeBox){
gameLocal.clip.TraceBounds( tr, start, end, meleebox, MASK_SHOT_RENDERMODEL, owner ); //ignore player
}else{
gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner ); //ignore player
}
if ( tr.fraction < 1.0f ) {
ent = gameLocal.entities[ tr.c.entityNum ]; //fix the headshot bug with melee attacks
if(( ent ) && !(ent->IsType( idAFAttachment::Type))){ //only if it's not an idAFAttachment
ent = gameLocal.GetTraceEntity( tr );
}
} else {
ent = NULL;
}
if ( g_debugWeapon.GetBool() ) {
gameRenderWorld->DebugLine( colorYellow, start, end, 100 );
if(useMeleeBox){
gameRenderWorld->DebugBounds( colorBlue,meleebox, start, 100 );
gameRenderWorld->DebugBounds( colorBlue,meleebox, 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 ) { //something hit
//gameLocal.Printf( "idWeapon::EvaluateMelee - ent = %s \n",ent->GetName());
if(autoMeleeEnabled &&( ent == lastMeleeEnt)){ //ignore the last entity hit
//gameLocal.Printf( "idWeapon::EvaluateMelee - entity ignored\n" );
return true; //we hit the same thing again... do nothing now.
}
//gameLocal.Printf( "idWeapon::EvaluateMelee - ent = %s \n",ent->GetName());
if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && ( ent->IsType( idActor::Type ) || ent->IsType( idAFAttachment::Type) ) ) { //no melee if noweapons = 1?
autoMeleeEnabled = false; //make sure
return false;
}
// weapon stealing - do this before damaging so weapons are not dropped twice - disabled if autoMeleeEnabled
if ( !autoMeleeEnabled
&& 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;
//Ivan fix - transform clipmodel to joint handle to correctly get the damage zone in idActor::Damage
//was: ent->Damage( owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier( MELEE_DAMAGE ), tr.c.id );
ent->Damage( owner, owner, globalKickDir, meleeDefName, (comboMultiplier * owner->PowerUpModifier( MELEE_DAMAGE )) , CLIPMODEL_ID_TO_JOINT_HANDLE( tr.c.id ) );
lastMeleeEnt = ent; //remember this to avoid hitting it consecutively
hit = true;
}
//push
float push = meleeDef->dict.GetFloat( "push" );
idVec3 impulse = -push * owner->PowerUpModifier( SPEED ) * tr.c.normal;
//extra push for AFs
if( (ent->health <= 0) && (ent->IsType(idAFEntity_Base::Type)) ){
idAFEntity_Base *p = static_cast< idAFEntity_Base * >( ent );
if ( p->IsActiveAF() ){
//gameLocal.Printf( "p->IsActiveAF()\n" );
impulse *= meleeDef->dict.GetInt( "pushAFMult","1" );
//quinak and dirty fix for flying ragdolls
/*
if(impulse.z > 70000 ){
impulse.z = 70000,
}else if( impulse.z < -70000 ){
impulse.z = -70000,
}
*/
//gameLocal.Printf( "idWeapon::EvaluateMelee - impulse: %s, lenght: %f\n", impulse.ToString(), impulse.Length() );
}
}
ent->ApplyImpulse( this, tr.c.id, tr.c.point, impulse );
if ( weaponDef->dict.GetBool( "impact_damage_effect" ) ) {
/* ivan - was:
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" ) );
} */
//case 1/3: HIGH priority entities: ALWAYS play the snd and the prt on them, unless 'bleed' key is set to '0'. (sword or chainsaw on HIGH priority entities)
if ( (ent->IsType(idBrittleFracture::Type) || ent->IsType(idAnimatedEntity::Type) || ent->IsType(idMoveable::Type) || ent->IsType(idMoveableItem::Type)) && ent->spawnArgs.GetBool( "bleed", "1" ) ) {
nextStrikeFx = gameLocal.time + 500; ///delay snd+prt for LOW priority entities after an hit on HIGH priority entity
hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ) ); //play the sound from the entity hit!
hitSound = ""; //don't play hitsound because AddDamageEffect already plays its own sound
}
//case 2/3: (LOW priority entities + we don't have our own .prt to show) AND (can bleed) -> play the snd and the prt less frequently - (example: sword on LOW priority entities)
else if (strikeSmoke == NULL && ent->spawnArgs.GetBool( "bleed", "1" )){ // Again, this is not done if 'bleed' key is set to '0'.
if (( gameLocal.time > nextStrikeFx ) ){ //this is usually the worldspawn... don't play too much snd and prt on it!
nextStrikeFx = gameLocal.time + 300; //delay snd+prt for LOW priority entities after an hit on LOW priority entity
hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ), this ); //play the sound from the weapon itself!
hitSound = ""; //don't play hitsound because AddDamageEffect already plays its own sound from the weapon
}
}
//case 3/3: (LOW priority entities + we have our own .prt to show) OR (cannot bleed) -> play our snd and our prt less frequently (example: chainsaw on LOW priority entities)
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;
}
}
}
//always play sound if autoMelee is disabled, otherwise only if (we damaged something ) or (hit something not damaged, as world, and we are beyond the min time)
if( (hit) || (ent && gameLocal.time > nextMeleeSnd ) || (!autoMeleeEnabled )) {
if ( *hitSound != '\0' ) {
const idSoundShader *snd = declManager->FindSound( hitSound );
StartSoundShader( snd, SND_CHANNEL_BODY2, 0, true, NULL );
nextMeleeSnd = gameLocal.time + 1000;
}
}
if(!autoMeleeEnabled){ owner->WeaponFireFeedback( &weaponDef->dict ); } //autoMeleeEnabled --> no need for feedback
return hit;
}
if(!autoMeleeEnabled){ owner->WeaponFireFeedback( &weaponDef->dict ); } //autoMeleeEnabled --> no need for feedback
return false;
}
//Ivan end
/*
================
idWeapon::EnterCinematic
================
*/
void idWeapon::EnterCinematic( void ) {
StopSound( SND_CHANNEL_ANY, false );
#ifdef _DENTONMOD
StopWeaponFx();
#endif
if ( isLinked ) {
SetState( "EnterCinematic", 0 );
thread->Execute();
WEAPON_ATTACK = false;
WEAPON_SPECIAL = false; // new //un noted code change from original sdk
WEAPON_SPECIAL_HOLD = false; // new
WEAPON_RELOAD = false;
WEAPON_NETRELOAD = false;
WEAPON_NETENDRELOAD = false;
WEAPON_NETFIRING = false;
WEAPON_RAISEWEAPON = false;
WEAPON_LOWERWEAPON = false;
}
disabled = true;
autoMeleeEnabled = false; //ivan - disable in cinematic
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 ) {
//ivan - this was wrong! everyone expects this being the real amount of ammo avalable, including clip!
return owner->inventory.HasAmmo( ammoType, ammoRequired );
//return owner->inventory.HasAmmo( ammoType, ammoRequired ) + ( ammoClip > 0 ? ammoClip : 0 );
} else {
return 0;
}
}
/*
================
idWeapon::AmmoInClip
================
*/
int idWeapon::AmmoInClip( void ) const {
return ammoClip;
}
//ivan start
/*
================
idWeapon::GetMaxAmmo
================
*/
int idWeapon::GetMaxAmmo( void ) const {
return maxAmmo;
}
//ivan end
/*
================
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 ); //un noted code change from original sdk
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 ) ); //un noted code change from original sdk
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::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();
//un noted code change from original sdk
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::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;
}
/*
//ivan - commented out
int oldAmmo = ammoClip;
ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
ammoClip += amount;
if ( ammoClip > clipSize ) {
ammoClip = clipSize;
}
if ( ammoClip > ammoAvail ) {
ammoClip = ammoAvail;
}
// for shared ammo we need to use the ammo when it is moved into the clip
int usedAmmo = ammoClip - oldAmmo;
owner->inventory.UseAmmo(ammoType, usedAmmo);
*/
ammoClip += amount;
if ( ammoClip > clipSize ) {
ammoClip = clipSize;
}
ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
if ( ammoClip > ammoAvail ) {
ammoClip = ammoAvail;
}
//ivan end
}
/*
===============
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 );
//ivan - out: ammoAvail += AmmoInClip();
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();
//un noted code change from original sdk
}
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();
//un noted code change from original sdk
}
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 );
}
renderEntity.customSkin = skinDecl;
UpdateVisuals();
//un noted code change from original sdk
if ( gameLocal.isServer ) {
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( worldMuzzleFlash.shaderParms[ parmnum ] ); //un noted code change from original sdk
}
/*
================
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 );
}
//un noted code change from original sdk
worldMuzzleFlash.shaderParms[ parmnum ] = value;
UpdateVisuals();
}
/*
================
idWeapon::Event_SetLightParms
================
*/
void idWeapon::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
//un noted code change from original sdk
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
Modified & rearranged by Clone JCD for barrel launched Projectiles to hit the target accurately.
================
*/
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;
//un noted code change from original sdk
trace_t tr;
bool barrelLaunch; //un noted code change from original sdk
bool tracer, beam;
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 ) {
//ivan start
/*
int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
if ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) {
return;
}
*/
// 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;
}
//ivan end
// 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;
}
}
if(clipSize == 0) { //un noted code change from original sdk
//Weapons with a clip size of 0 launch straight from inventory without moving to a clip
//Now we used the ammo when the ammo was moved into the clip so we don't want to
//use it now.
owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired );
} //un noted code change from original sdk
if ( clipSize && ammoRequired ) {
ammoClip -= powerAmmo ? dmgPower : ammoRequired; //un noted code change from original sdk
}
}
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
/*
//was:
renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
if( weaponDef->dict.GetBool( "resetShaderParms", "1" ) ){ //ivan - new "resetShaderParms" key
if ( worldModel.GetEntity() ) {
worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] ); //an offset
worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] ); //restart from zero
}
}
*/
//ivan start
if( weaponDef->dict.GetBool( "resetShaderParms", "1" ) ){ //ivan - new "resetShaderParms" key
renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
}
//ivan end
/*
// 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;
}
*/
//ownerBounds = owner->GetPhysics()->GetAbsBounds();
// predict instant hit projectiles //un noted code change from original sdk
const bool isPrediction = gameLocal.isClient && projectileDict.GetBool( "net_instanthit" );
const float spreadRad = DEG2RAD( spread );
idVec3 view_pos = playerViewOrigin + playerViewAxis[ 0 ] * 2.0f; // Muzzle pos for translation clip model only-- For barrel Launched projectiles
idVec3 muzzle_pos;
float muzzleDistFromView;
float traceDist = 0.0f, muzzleToTargetDist = 0.0f;
idVec3 muzzleDir;
beam = projectileDict.GetFloat( "fuse" ) <= 0 || projectileDict.GetBool( "rail_beam");
tracer = !beam && projectileDict.GetBool( "tracers" ) && (projectileDict.GetFloat("tracer_probability", "1.0") > gameLocal.random.RandomFloat());
barrelLaunch = projectileDict.GetBool( "launchFromBarrel" );
if ( barrelLaunch || tracer || beam ) { //un noted code change from original sdk
// calculate the muzzle position
if ( barrelJointWorld != INVALID_JOINT ) { //ivan
// there is an explicit joint for the muzzle
GetGlobalJointTransform( barrelJointWorld, muzzleOrigin, muzzleAxis ); //un noted code change from original sdk
if ( barrelLaunch ) {
tracer = false;
}
muzzle_pos = muzzleOrigin; // + playerViewAxis[ 0 ] * 2.0f; // muzzle_pos for multiplayer prediction //un noted code change from original sdk
// as well as for launching the projectiles
// muzzleDistFromView = (muzzle_pos - view_pos).Length( ) * 3.5f;
muzzleDistFromView = (muzzle_pos - view_pos).LengthSqr( ) * 3.5f; // This is faster
} else { // if we dont find a proper bone then cancel all the effects.
barrelLaunch = false;
tracer = false;
beam = false;
}
}
idVec3 &launch_pos = view_pos;
const float tracer_speed = projectileDict.GetFloat( "tracer_speed", "0.0f" );
//ivan start - fire modes - setup
int firemodeCounter = 0;
int firemodeCounterPos = 0;
bool odd_proj_num = (num_projectiles%2 != 0); //dispari
idVec3 updown_offset;
updown_offset.Zero();
if( fireMode == WP_FIREMODE_2D_STEP_SPREAD){
if(num_projectiles > 1){ //Example: spread = 90, num projs = 5
ang = 2*spread/(num_projectiles-1); //Spread step: 2* 90 /(5-1) = 45 degrees
ang = ang/180.0f; //normalized from 0 to 1: 45/180 = 0.5 --> % of 180 degrees to use: 0, 0.25, -0.25, 0.5, -0.5
} else { // DG: make sure ang is initialized, I hope doing the same as WP_FIREMODE_DEFAULT makes at least a bit sense
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
}
}
//ivan end
for( i = 0; i < num_projectiles; i++ ) {
//ivan start - fire modes - direction
if( fireMode == WP_FIREMODE_DEFAULT){
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
}else if( fireMode == WP_FIREMODE_2D_STEP_SPREAD){
dir = (playerViewAxis[ 0 ] * (1-ang*firemodeCounterPos) ) + ( playerViewAxis[ 2 ] * ang*firemodeCounter );
//upd counter
if(firemodeCounter >= 0){
firemodeCounter++;
firemodeCounterPos = firemodeCounter;
}
firemodeCounter = - firemodeCounter;
}else if( fireMode == WP_FIREMODE_2D_PARALLEL_SPREAD){
dir = playerViewAxis[ 0 ];
//upd counter before if even number of projs
if( !odd_proj_num && firemodeCounter >= 0){ firemodeCounter++; }
updown_offset = playerViewAxis[ 2 ]*spread*firemodeCounter;
//upd counter after if odd number of projs
if( odd_proj_num && firemodeCounter >= 0){ firemodeCounter++; }
firemodeCounter = - firemodeCounter;
}else if( fireMode == WP_FIREMODE_2D_RANDOM_SPREAD){
ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) );
}else{
gameLocal.Error("Unknown WEAPON fire mode: %d", fireMode );
}
//ivan test end
dir.Normalize();
if ( barrelLaunch || tracer || beam ) { // Do not execute this part unless projectile is barrel launched or has a tracer effect.
//ivan start
gameLocal.clip.Translation( tr, view_pos, view_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
traceDist = (tr.endpos - view_pos).LengthSqr();
//gameRenderWorld->DebugLine( colorRed, view_pos, tr.endpos, 1000 );
//fix - tracer still need muzzleToTargetDist
if ( tracer ) {
muzzleDir = tr.endpos - muzzle_pos;
muzzleToTargetDist = muzzleDir.LengthSqr();
muzzleDir.Normalize();
}
//fix end
//ivan end
}
if ( isPrediction ) {
if ( tr.fraction < 1.0f ) {
if ( barrelLaunch ) { //a new trace should be made for multiplayer prediction of barrel launched projectiles
gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
} //un noted code change from original sdk
else
{
gameLocal.clip.Translation( tr, view_pos, view_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
}
idProjectile::ClientPredictionCollide( this, projectileDict, tr, vec3_origin, true );
}
}
else { //un noted code change from original sdk
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;
}
if ( barrelLaunch ){ //un noted code change from original sdk
launch_pos = muzzle_pos;
}
proj = static_cast<idProjectile *>(ent);
// proj->Create( owner, muzzleOrigin, dir );
proj->Create( owner, launch_pos, dir ); //un noted code change from original sdk
#ifdef _DENTONMOD
if( tracer ) {
/* if ( traceDist <= muzzleToTargetDist ) // Ideally, this should never happen
gameLocal.Printf ( " Unpredicted traceDistance in idWeapon::Event_LaunchProjectiles " );
*/
bool beamTracer = (projectileDict.GetString( "beam_skin", NULL ) != NULL);
if ( tracer_speed != 0.0f ) {
if( beamTracer ) { // Check whether it's a beamTracer
proj->setTracerEffect( new dnBeamSpeedTracer(proj, tracer_speed, muzzleToTargetDist * idMath::RSqrt( muzzleToTargetDist ), muzzle_pos, muzzleDir.ToMat3()) );
}
else {
proj->setTracerEffect( new dnSpeedTracer(proj, tracer_speed, muzzleToTargetDist * idMath::RSqrt( muzzleToTargetDist ), muzzle_pos, muzzleDir.ToMat3()) );
}
}
else {
if( beamTracer ) {
proj->setTracerEffect( new dnBeamTracer(proj, traceDist/muzzleToTargetDist, view_pos, muzzle_pos, muzzleDir.ToMat3()) );
}
else {
proj->setTracerEffect( new dnTracer(proj, traceDist/muzzleToTargetDist, view_pos, muzzle_pos, muzzleDir.ToMat3()) );
}
}
}
else if( beam ) {
proj->setTracerEffect( new dnRailBeam(proj, muzzleOrigin) );
}
#endif
//ivan start - fire modes - position offset
//proj->Launch( launch_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower );
proj->Launch( launch_pos + updown_offset, dir, pushVelocity, fuseOffset, launchPower, dmgPower );
//ivan test end
}
}
if ( !gameLocal.isClient ) { //un noted code change from original sdk
// toss the brass
if (brassDelay >= 0) // eject brass behaviour can be disabled by simply setting the delay to 0 //un noted code change from original sdk
PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
}
// add the light for the muzzleflash
if ( !lightOn ) {
MuzzleFlashLight();
}
owner->WeaponFireFeedback( &weaponDef->dict );
// reset muzzle smoke
weaponSmokeStartTime = gameLocal.realClientTime;
}
//----------------New Start
#ifdef _DENTONMOD
bool idWeapon::ChangeProjectileDef( int number ) {
if( projectileEnt != NULL ) {
gameLocal.Printf("Projectile Entity exists \n ");
return false;
}
const idKeyValue *kv = spawnArgs.MatchPrefix( "def_projectile", NULL);
for (int i=0; kv && i < number; i++ ) {
kv = spawnArgs.MatchPrefix( "def_projectile", kv);
}
if (kv == NULL) {
return false;
}
const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( kv->GetValue(), false );
if ( projectileDef ) {
const char *spawnclass = projectileDef->dict.GetString( "spawnclass" );
idTypeInfo *cls = idClass::GetClass( spawnclass );
if ( !cls || !cls->IsType( idProjectile::Type ) ) {
gameLocal.Warning( "Invalid spawnclass in Event_ChangeProjectileDef.\n" );
} else {
projectileDict = projectileDef->dict;
owner->SetProjectileType( number );
return true;
}
}
return false;
}
void idWeapon::Event_ChangeProjectileDef( int number ) {
if( number == owner->GetProjectileType() ) {
idThread::ReturnFloat( 1 );
}
idThread::ReturnFloat( ChangeProjectileDef(number) ? 1 : 0 );
}
void idWeapon::Event_GetProjectileType( void ) {
idThread::ReturnFloat( owner->GetProjectileType() );
}
void idWeapon::Event_StartWeaponParticle( const char* prtName) {
WeaponParticle_t* part;
weaponParticles.Get(prtName, &part);
if(part) {
part->particleFlags.isActive = true;
if( part->particleFlags.isSmoke ) {
part->startTime = gameLocal.time;
} else {
if( part->modelDefHandle > -1 ) {
gameRenderWorld->FreeEntityDef( part->modelDefHandle );
part->modelDefHandle = -1;
}
//part->renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
//part->renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.RandomFloat(); // For effects like muzzleflashes etc.
}
}
}
void idWeapon::Event_StopWeaponParticle( const char* prtName) {
WeaponParticle_t* part;
weaponParticles.Get(prtName, &part);
if(part) {
part->particleFlags.isActive = false;
part->startTime = 0;
//Free the particles
if(!part->particleFlags.isSmoke && part->modelDefHandle >= 0) {
gameRenderWorld->FreeEntityDef( part->modelDefHandle );
part->modelDefHandle = -1;
}
}
}
void idWeapon::Event_StartWeaponLight( const char* lightName) {
WeaponLight_t* light;
weaponLights.Get(lightName, &light);
if( light ) {
light->lightFlags.isActive = true;
light->startTime = gameLocal.time;
// these will be different each fire
light->light.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
light->light.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
if( !light->lightFlags.isAlwaysOn ){
light->endTime += light->startTime;
}
}
}
void idWeapon::Event_StopWeaponLight( const char* lightName) {
WeaponLight_t* light;
weaponLights.Get(lightName, &light);
if(light) {
light->lightFlags.isActive = false;
light->startTime = 0;
if(light->lightHandle != -1) {
gameRenderWorld->FreeLightDef( light->lightHandle );
light->lightHandle = -1;
}
}
}
#endif //_DENTONMOD
//----------------- New End
/*
=====================
idWeapon::Event_Melee
=====================
*/
void idWeapon::Event_Melee( void ) {
if( !autoMeleeEnabled && EvaluateMelee() ){ //don't do this if it's already enabled...
idThread::ReturnInt( 1 );
}else{
idThread::ReturnInt( 0 );
}
}
/*
=====================
idWeapon::Event_GetWorldModel
=====================
*/
void idWeapon::Event_GetWorldModel( void ) {
idThread::ReturnEntity( this ); //ivan - was: 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() ) { //Rev 2019 viewmodels not used. all brass code now refers to world model. Was stopping brass from ejecting.
//if ( !g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel() ) {
return;
}
if ( ejectJointWorld == INVALID_JOINT || !brassDict.GetNumKeyVals() ) { //was: ejectJointView //un noted code change from original sdk
return;
}
if ( gameLocal.isClient ) {
return;
}
idMat3 axis;
idVec3 origin, linear_velocity, angular_velocity;
idEntity *ent;
if ( !GetGlobalJointTransform( ejectJointWorld, origin, axis ) ) { //was: ejectJointView //un noted code change from original sdk
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();
if( spawnArgs.GetBool( "fixed_brass_dir" ) ) { //un noted code change from original sdk
linear_velocity = 40 * ( playerViewAxis[0] + playerViewAxis[1] + playerViewAxis[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 );
}
} //un noted code change from original sdk
/*
===============
idWeapon::Event_IsInvisible
===============
*/
void idWeapon::Event_IsInvisible( void ) {
if ( !owner ) {
idThread::ReturnFloat( 0 );
return;
}
idThread::ReturnFloat( owner->PowerUpActive( INVISIBILITY ) ? 1 : 0 );
}
// New------------------
#ifdef _DENTONMOD
/*
===============
idWeapon::Event_StartZoom
===============
*/
void idWeapon::Event_SetZoom( int status ) { //New
if( status ){
owner->SetWeaponZoom( true );
}
else {
owner->SetWeaponZoom( false );
}
}
#endif// _DENTONMOD
/*
===============
idWeapon::ClientPredictionThink
===============
*/
void idWeapon::ClientPredictionThink( void ) {
UpdateAnimation();
}
/*
================
idWeapon::AmmoCount
//un noted code change from original sdk
Returns the total number of rounds regardless of the required ammo // new
================
*/
int idWeapon::AmmoCount() const {
if ( owner ) {
return owner->inventory.HasAmmo( ammoType, 1 );
} else {
return 0;
}
}