/* =========================================================================== 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 . 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" #include "BrittleFracture.h" #include "renderer/ModelManager.h" #include "Weapon.h" /*********************************************************************** idWeapon ***********************************************************************/ // // event defs // const idEventDef EV_Weapon_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", "dffffdd" ); //ivan last "dd" added //was: const idEventDef EV_Weapon_CreateProjectile( "createProjectile", NULL, 'e' ); const idEventDef EV_Weapon_CreateProjectile( "createProjectile", "d", 'e' ); //ivan "d" added 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" ); //rev grab const idEventDef EV_Weapon_GrabberHasTarget( "grabberHasTarget", NULL, 'd' ); const idEventDef EV_Weapon_Grabber( "grabber", "d" ); const idEventDef EV_Weapon_Grabber_SetGrabDistance( "grabberGrabDistance", "f" ); //rev grab // 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_StartParticle( "startWeaponParticle", "s" ); const idEventDef EV_Weapon_StopParticle( "stopWeaponParticle", "s" ); const idEventDef EV_Weapon_StartWeaponLight( "startWeaponLight", "s" ); const idEventDef EV_Weapon_StopWeaponLight( "stopWeaponLight", "s" ); #endif //_DENTONMOD //ivan start 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 ) //rev grab EVENT( EV_Weapon_Grabber, idWeapon::Event_Grabber ) EVENT( EV_Weapon_GrabberHasTarget, idWeapon::Event_GrabberHasTarget ) EVENT( EV_Weapon_Grabber_SetGrabDistance, idWeapon::Event_GrabberSetGrabDistance ) //rev grab #ifdef _DENTONMOD EVENT( EV_Weapon_ChangeProjectileDef, idWeapon::Event_ChangeProjectileDef ) // New EVENT( EV_Weapon_GetProjectileType, idWeapon::Event_GetProjectileType ) // New EVENT( EV_Weapon_StartParticle, idWeapon::Event_StartWeaponParticle ) // All Four New EVENT( EV_Weapon_StopParticle, 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; //worldModel = NULL; weaponDef = NULL; thread = NULL; //memset( &guiLight, 0, sizeof( guiLight ) ); //memset( &muzzleFlash, 0, sizeof( muzzleFlash ) ); memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) ); memset( &nozzleGlow, 0, sizeof( nozzleGlow ) ); muzzleFlashEnd = 0; flashColor = vec3_origin; //muzzleFlashHandle = -1; worldMuzzleFlashHandle = -1; //guiLightHandle = -1; nozzleGlowHandle = -1; modelDefHandle = -1; //REV GRAB START grabberState = -1; //REV GRAB END berserk = 2; brassDelay = 0; allowDrop = true; //ivan start #ifdef TRAIL_FX_CHAIN //done in Clear() #else trailGen = NULL; #endif //ivan end Clear(); fl.networkSync = true; projectileOrigin = 1; //rev 2018 } /* ================ idWeapon::~idWeapon() ================ */ idWeapon::~idWeapon() { Clear(); //delete worldModel.GetEntity(); //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 ); #endif //ivan start } /* ================ idWeapon::Spawn ================ */ void idWeapon::Spawn( void ) { //if ( !gameLocal.isClient ) { // // setup the world model // worldModel = static_cast< idAnimatedEntity * >( gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL ) ); // worldModel.GetEntity()->fl.networkSync = true; //} //REV GRAB if ( 1 /*!gameLocal.isMultiplayer*/ ) { grabber.Initialize(); } //REV GRAB thread = new idThread(); thread->ManualDelete(); thread->ManualControl(); //ivan start #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() ) ); /*if ( worldModel.GetEntity() ) { worldModel.GetEntity()->SetName( va( "%s_weapon_worldmodel", owner->name.c_str() ) ); }*/ } /* ================ idWeapon::ShouldConstructScriptObjectAtSpawn Called during idEntity::Spawn to see if it should construct the script object or not. Overridden by subclasses that need to spawn the script object themselves. ================ */ bool idWeapon::ShouldConstructScriptObjectAtSpawn( void ) const { return false; } /* ================ idWeapon::CacheWeapon ================ */ void idWeapon::CacheWeapon( const char *weaponName ) { const idDeclEntityDef *weaponDef; const char *brassDefName; const char *clipModelName; idTraceModel trm; const char *guiName; weaponDef = gameLocal.FindEntityDef( weaponName, false ); if ( !weaponDef ) { return; } // precache the brass collision model brassDefName = weaponDef->dict.GetString( "def_ejectBrass" ); if ( brassDefName[0] ) { const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false ); if ( brassDef ) { brassDef->dict.GetString( "clipmodel", "", &clipModelName ); if ( !clipModelName[0] ) { clipModelName = brassDef->dict.GetString( "model" ); // use the visual model } // load the trace model collisionModelManager->TrmFromModel( clipModelName, trm ); } } guiName = weaponDef->dict.GetString( "gui" ); if ( guiName[0] ) { uiManager->FindGui( guiName, true, false, true ); } } /* ================ idWeapon::Save ================ */ void idWeapon::Save( idSaveGame *savefile ) const { savefile->WriteInt( status ); savefile->WriteObject( thread ); savefile->WriteString( state ); savefile->WriteString( idealState ); savefile->WriteInt( animBlendFrames ); savefile->WriteInt( animDoneTime ); savefile->WriteInt( lastFiredTime ); //ivan savefile->WriteBool( isLinked ); savefile->WriteObject( owner ); //worldModel.Save( savefile ); savefile->WriteInt( hideTime ); savefile->WriteFloat( hideDistance ); savefile->WriteInt( hideStartTime ); savefile->WriteFloat( hideStart ); savefile->WriteFloat( hideEnd ); //savefile->WriteFloat( hideOffset ); savefile->WriteBool( hide ); savefile->WriteBool( disabled ); savefile->WriteInt( berserk ); savefile->WriteVec3( playerViewOrigin ); savefile->WriteMat3( playerViewAxis ); /* savefile->WriteVec3( viewWeaponOrigin ); savefile->WriteMat3( viewWeaponAxis ); */ savefile->WriteVec3( muzzleOrigin ); savefile->WriteMat3( muzzleAxis ); savefile->WriteVec3( pushVelocity ); savefile->WriteString( weaponDef->GetName() ); savefile->WriteFloat( meleeDistance ); savefile->WriteString( meleeDefName ); savefile->WriteInt( brassDelay ); savefile->WriteString( icon ); /* savefile->WriteInt( guiLightHandle ); savefile->WriteRenderLight( guiLight ); savefile->WriteInt( muzzleFlashHandle ); savefile->WriteRenderLight( muzzleFlash ); */ savefile->WriteInt( worldMuzzleFlashHandle ); savefile->WriteRenderLight( worldMuzzleFlash ); savefile->WriteVec3( flashColor ); savefile->WriteInt( muzzleFlashEnd ); savefile->WriteInt( flashTime ); savefile->WriteBool( lightOn ); savefile->WriteBool( silent_fire ); /* savefile->WriteInt( kick_endtime ); savefile->WriteInt( muzzle_kick_time ); savefile->WriteInt( muzzle_kick_maxtime ); savefile->WriteAngles( muzzle_kick_angles ); savefile->WriteVec3( muzzle_kick_offset ); */ savefile->WriteInt( ammoType ); savefile->WriteInt( ammoRequired ); savefile->WriteInt( clipSize ); savefile->WriteInt( ammoClip ); savefile->WriteInt( lowAmmo ); savefile->WriteBool( powerAmmo ); // savegames <= 17 savefile->WriteInt( 0 ); savefile->WriteInt( zoomFov ); /* savefile->WriteJoint( barrelJointView ); savefile->WriteJoint( flashJointView ); savefile->WriteJoint( ejectJointView ); savefile->WriteJoint( guiLightJointView ); savefile->WriteJoint( ventLightJointView ); */ savefile->WriteJoint( flashJointWorld ); savefile->WriteJoint( barrelJointWorld ); savefile->WriteJoint( ejectJointWorld ); savefile->WriteJoint( meleeJointWorld ); //ivan //ivan start savefile->WriteBool( autoMeleeEnabled ); savefile->WriteBool( useMeleeBox ); savefile->WriteFloat( comboMultiplier ); savefile->WriteFloat( firingWalkSpeedMult ); 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 ); //spread savefile->WriteFloat( dynamicSpreadValue ); savefile->WriteFloat( spreadBaseValue ); savefile->WriteFloat( spreadVelocityFactor ); savefile->WriteFloat( spreadCrouchFactor ); //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 GRAB savefile->WriteStaticObject( grabber ); savefile->WriteInt( grabberState ); //REV GRAB // 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( spreadMode ); //ivan end } /* ================ idWeapon::Restore ================ */ void idWeapon::Restore( idRestoreGame *savefile ) { savefile->ReadInt( (int &)status ); savefile->ReadObject( reinterpret_cast( thread ) ); savefile->ReadString( state ); savefile->ReadString( idealState ); savefile->ReadInt( animBlendFrames ); savefile->ReadInt( animDoneTime ); savefile->ReadInt( lastFiredTime ); //ivan savefile->ReadBool( isLinked ); // Re-link script fields WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" ); WEAPON_SPECIAL.LinkTo( scriptObject, "WEAPON_SPECIAL" ); // New 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( owner ) ); //worldModel.Restore( savefile ); savefile->ReadInt( hideTime ); savefile->ReadFloat( hideDistance ); savefile->ReadInt( hideStartTime ); savefile->ReadFloat( hideStart ); savefile->ReadFloat( hideEnd ); //savefile->ReadFloat( hideOffset ); savefile->ReadBool( hide ); savefile->ReadBool( disabled ); savefile->ReadInt( berserk ); savefile->ReadVec3( playerViewOrigin ); savefile->ReadMat3( playerViewAxis ); /* savefile->ReadVec3( viewWeaponOrigin ); savefile->ReadMat3( viewWeaponAxis ); */ savefile->ReadVec3( muzzleOrigin ); savefile->ReadMat3( muzzleAxis ); savefile->ReadVec3( pushVelocity ); idStr objectname; savefile->ReadString( objectname ); weaponDef = gameLocal.FindEntityDef( objectname ); meleeDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_melee" ), false ); /* const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_projectile" ), false ); if ( projectileDef ) { projectileDict = projectileDef->dict; } else { projectileDict.Clear(); }*/ const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_ejectBrass" ), false ); if ( brassDef ) { brassDict = brassDef->dict; } else { brassDict.Clear(); } savefile->ReadFloat( meleeDistance ); savefile->ReadString( meleeDefName ); savefile->ReadInt( brassDelay ); savefile->ReadString( icon ); /* savefile->ReadInt( guiLightHandle ); savefile->ReadRenderLight( guiLight ); savefile->ReadInt( muzzleFlashHandle ); savefile->ReadRenderLight( muzzleFlash ); */ savefile->ReadInt( worldMuzzleFlashHandle ); savefile->ReadRenderLight( worldMuzzleFlash ); savefile->ReadVec3( flashColor ); savefile->ReadInt( muzzleFlashEnd ); savefile->ReadInt( flashTime ); savefile->ReadBool( lightOn ); savefile->ReadBool( silent_fire ); /* savefile->ReadInt( kick_endtime ); savefile->ReadInt( muzzle_kick_time ); savefile->ReadInt( muzzle_kick_maxtime ); savefile->ReadAngles( muzzle_kick_angles ); savefile->ReadVec3( muzzle_kick_offset ); */ savefile->ReadInt( (int &)ammoType ); savefile->ReadInt( ammoRequired ); savefile->ReadInt( clipSize ); savefile->ReadInt( ammoClip ); savefile->ReadInt( lowAmmo ); savefile->ReadBool( powerAmmo ); // savegame versions <= 17 int foo; savefile->ReadInt( foo ); savefile->ReadInt( zoomFov ); /* savefile->ReadJoint( barrelJointView ); savefile->ReadJoint( flashJointView ); savefile->ReadJoint( ejectJointView ); savefile->ReadJoint( guiLightJointView ); savefile->ReadJoint( ventLightJointView ); */ savefile->ReadJoint( flashJointWorld ); savefile->ReadJoint( barrelJointWorld ); savefile->ReadJoint( ejectJointWorld ); savefile->ReadJoint( meleeJointWorld ); //ivan //ivan start savefile->ReadBool( autoMeleeEnabled ); savefile->ReadBool( useMeleeBox ); savefile->ReadFloat( comboMultiplier ); savefile->ReadFloat( firingWalkSpeedMult ); 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 ); //spread savefile->ReadFloat( dynamicSpreadValue ); savefile->ReadFloat( spreadBaseValue ); savefile->ReadFloat( spreadVelocityFactor ); savefile->ReadFloat( spreadCrouchFactor ); //ivan end 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 ); 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( projectileEnt ) ); savefile->ReadObject( reinterpret_cast( lastMeleeEnt ) ); //ivan //REV GRAB savefile->ReadStaticObject( grabber ); savefile->ReadInt( grabberState ); //REV GRAB #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( 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( spreadMode ); //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 WEAPON_SPECIAL_HOLD.Unlink(); //new WEAPON_RELOAD.Unlink(); WEAPON_NETRELOAD.Unlink(); WEAPON_NETENDRELOAD.Unlink(); WEAPON_NETFIRING.Unlink(); WEAPON_RAISEWEAPON.Unlink(); WEAPON_LOWERWEAPON.Unlink(); /* if ( muzzleFlashHandle != -1 ) { gameRenderWorld->FreeLightDef( muzzleFlashHandle ); muzzleFlashHandle = -1; } if ( muzzleFlashHandle != -1 ) { gameRenderWorld->FreeLightDef( muzzleFlashHandle ); muzzleFlashHandle = -1; } */ if ( worldMuzzleFlashHandle != -1 ) { gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle ); worldMuzzleFlashHandle = -1; } /* if ( guiLightHandle != -1 ) { gameRenderWorld->FreeLightDef( guiLightHandle ); guiLightHandle = -1; } */ if ( nozzleGlowHandle != -1 ) { gameRenderWorld->FreeLightDef( nozzleGlowHandle ); nozzleGlowHandle = -1; } memset( &renderEntity, 0, sizeof( renderEntity ) ); renderEntity.entityNum = entityNumber; renderEntity.noShadow = true; renderEntity.noSelfShadow = true; renderEntity.customSkin = NULL; // set default shader parms renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f; renderEntity.shaderParms[ SHADERPARM_GREEN ]= 1.0f; renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f; renderEntity.shaderParms[3] = 1.0f; renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f; renderEntity.shaderParms[5] = 0.0f; renderEntity.shaderParms[6] = 0.0f; renderEntity.shaderParms[7] = 0.0f; if ( refSound.referenceSound ) { refSound.referenceSound->Free( true ); } memset( &refSound, 0, sizeof( refSound_t ) ); // setting diversity to 0 results in no random sound. -1 indicates random. refSound.diversity = -1.0f; if ( owner ) { // don't spatialize the weapon sounds refSound.listenerId = owner->GetListenerId(); } // clear out the sounds from our spawnargs since we'll copy them from the weapon def const idKeyValue *kv = spawnArgs.MatchPrefix( "snd_" ); while( kv ) { spawnArgs.Delete( kv->GetKey() ); kv = spawnArgs.MatchPrefix( "snd_" ); } hideTime = 300; hideDistance = -15.0f; hideStartTime = gameLocal.time - hideTime; hideStart = 0.0f; hideEnd = 0.0f; //hideOffset = 0.0f; hide = false; disabled = false; weaponSmoke = NULL; weaponSmokeStartTime = 0; continuousSmoke = false; strikeSmoke = NULL; strikeSmokeStartTime = 0; strikePos.Zero(); strikeAxis = mat3_identity; nextStrikeFx = 0; nextMeleeSnd = 0; //ivan icon = ""; playerViewAxis.Identity(); playerViewOrigin.Zero(); //viewWeaponAxis.Identity(); //viewWeaponOrigin.Zero(); muzzleAxis.Identity(); muzzleOrigin.Zero(); pushVelocity.Zero(); status = WP_HOLSTERED; state = ""; idealState = ""; animBlendFrames = 0; animDoneTime = 0; lastFiredTime = 0; //ivan projectileDict.Clear(); meleeDef = NULL; meleeDefName = ""; meleeDistance = 0.0f; brassDict.Clear(); flashTime = 250; lightOn = false; silent_fire = false; //rev grab grabberState = -1; grabber.Update( owner, true ); //rev grab ammoType = 0; ammoRequired = 0; ammoClip = 0; clipSize = 0; lowAmmo = 0; powerAmmo = false; /* kick_endtime = 0; muzzle_kick_time = 0; muzzle_kick_maxtime = 0; muzzle_kick_angles.Zero(); muzzle_kick_offset.Zero(); */ zoomFov = 90; /* barrelJointView = INVALID_JOINT; flashJointView = INVALID_JOINT; ejectJointView = INVALID_JOINT; guiLightJointView = INVALID_JOINT; ventLightJointView = INVALID_JOINT; */ barrelJointWorld = INVALID_JOINT; flashJointWorld = INVALID_JOINT; ejectJointWorld = INVALID_JOINT; 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 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; firingWalkSpeedMult = 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 //spread dynamicSpreadValue = 0.0f; spreadBaseValue = 0.0f; spreadVelocityFactor = 0.0f; spreadCrouchFactor = 0.0f; //ivan end } /* ================ idWeapon::InitWorldModel ================ */ void idWeapon::InitWorldModel( const idDeclEntityDef *def ) { /* idEntity *ent; ent = worldModel.GetEntity(); assert( ent ); */ assert( def ); const char *model = def->dict.GetString( "model_world" ); const char *attach = def->dict.GetString( "joint_attach" ); /* //old code ent->SetSkin( NULL ); if ( model[0] && attach[0] ) { ent->Show(); ent->SetModel( model ); if ( ent->GetAnimator()->ModelDef() ) { ent->SetSkin( ent->GetAnimator()->ModelDef()->GetDefaultSkin() ); } ent->GetPhysics()->SetContents( 0 ); ent->GetPhysics()->SetClipModel( NULL, 1.0f ); ent->BindToJoint( owner, attach, true ); ent->GetPhysics()->SetOrigin( vec3_origin ); ent->GetPhysics()->SetAxis( mat3_identity ); // supress model in player views, but allow it in mirrors and remote views renderEntity_t *worldModelRenderEntity = ent->GetRenderEntity(); if ( worldModelRenderEntity ) { worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber+1; worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber+1; worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber; } } else { ent->SetModel( "" ); ent->Hide(); } flashJointWorld = ent->GetAnimator()->GetJointHandle( "flash" ); ejectJointWorld = ent->GetAnimator()->GetJointHandle( "eject" ); meleeJointWorld = ent->GetAnimator()->GetJointHandle( "melee" ); //ivan //ivan - fix for bfg or custom barrel joint //was: barrelJointWorld = ent->GetAnimator()->GetJointHandle( "barrel" ); //ivan - was "muzzle" barrelJointWorld = ent->GetAnimator()->GetJointHandle( def->dict.GetString( "world_barrel_joint", "barrel" ) ); //ivan end */ //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 ); } } //van start /* ================ idWeapon::UpdSpreadSettings ================ */ void idWeapon::UpdSpreadSettings( void ) { int myMode = owner->GetCurrentWeaponMode(); //base value spreadBaseValue = weaponDef->dict.GetFloat( va( "spread_mode%d", myMode ), "-1" ); //use a negative default to check if it's present quickly if( spreadBaseValue < 0 ) { spreadBaseValue = weaponDef->dict.GetFloat( "spread", "0" ); //default no spread } //spreadVelocityPct spreadVelocityFactor = weaponDef->dict.GetFloat( va( "spreadVelocityPct_mode%d", myMode ), "-1" ); //use a negative default to check if it's present quickly if( spreadVelocityFactor < 0 ) { spreadVelocityFactor = weaponDef->dict.GetFloat( "spreadVelocityPct", "0" ); //default no extra-speed-based spread } //spreadCrouchPct spreadCrouchFactor = weaponDef->dict.GetFloat( va( "spreadCrouchPct_mode%d", myMode ), "-1" ); //use a negative default to check if it's present quickly if( spreadCrouchFactor < 0 ) { spreadCrouchFactor = weaponDef->dict.GetFloat( "spreadCrouchPct", "100" ); //default crouch leave spread 100% unaltered } /* gameLocal.Printf("-- mode %d --\n",myMode); gameLocal.Printf("spreadBaseValue %f\n",spreadBaseValue); gameLocal.Printf("spreadVelocityFactor %f\n",spreadVelocityFactor); gameLocal.Printf("spreadCrouchFactor %f\n",spreadCrouchFactor); */ spreadVelocityFactor = spreadVelocityFactor/100; //pct velocity to use spreadCrouchFactor = spreadCrouchFactor/100; //pct spread to use when crouched } //ivan end /* ================ idWeapon::GetWeaponDef ================ */ void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) { const char *shader; const char *objectType; //const char *vmodel; const char *guiName; // const char *projectileName; const char *brassDefName; const char *smokeName; int ammoAvail; Clear(); if ( !objectname || !objectname[ 0 ] ) { return; } assert( owner ); weaponDef = gameLocal.FindEntityDef( objectname ); ammoType = GetAmmoNumForName( weaponDef->dict.GetString( "ammoType" ) ); ammoRequired = weaponDef->dict.GetInt( "ammoRequired" ); clipSize = weaponDef->dict.GetInt( "clipSize" ); lowAmmo = weaponDef->dict.GetInt( "lowAmmo" ); //ivan start maxAmmo = owner->inventory.MaxAmmoForAmmoClass( owner, weaponDef->dict.GetString( "ammoType" ) ); spreadMode = weaponDef->dict.GetInt( "spreadMode", "0" ); //ivan end icon = weaponDef->dict.GetString( "icon" ); silent_fire = weaponDef->dict.GetBool( "silent_fire" ); powerAmmo = weaponDef->dict.GetBool( "powerAmmo" ); /* #ifdef _DENTONMOD #else muzzle_kick_time = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_time" ) ); muzzle_kick_maxtime = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_maxtime" ) ); muzzle_kick_angles = weaponDef->dict.GetAngles( "muzzle_kick_angles" ); muzzle_kick_offset = weaponDef->dict.GetVector( "muzzle_kick_offset" ); #endif */ 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( 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( declManager->FindType( DECL_PARTICLE, smokeName ) ); } else { strikeSmoke = NULL; } strikeSmokeStartTime = 0; strikePos.Zero(); strikeAxis = mat3_identity; nextStrikeFx = 0; //ivan start nextMeleeSnd = 0; firingWalkSpeedMult = weaponDef->dict.GetFloat( "firingWalkSpeedMult", "1.0" ); trailLowOffset = weaponDef->dict.GetInt( "trailLowOffset", "0" ); trailHighOffset = weaponDef->dict.GetInt( "trailHighOffset", "0" ); //spread UpdSpreadSettings(); //ivan end /* // setup gui light memset( &guiLight, 0, sizeof( guiLight ) ); const char *guiLightShader = weaponDef->dict.GetString( "mtr_guiLightShader" ); if ( *guiLightShader != '\0' ) { guiLight.shader = declManager->FindMaterial( guiLightShader, false ); guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = 3; guiLight.pointLight = true; } // setup the view model vmodel = weaponDef->dict.GetString( "model_view" ); SetModel( vmodel ); */ // setup the world model InitWorldModel( weaponDef ); // copy the sounds from the weapon view model def into out spawnargs const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" ); while( kv ) { spawnArgs.Set( kv->GetKey(), kv->GetValue() ); kv = weaponDef->dict.MatchPrefix( "snd_", kv ); } /* // find some joints in the model for locating effects barrelJointView = animator.GetJointHandle( "barrel" ); flashJointView = animator.GetJointHandle( "flash" ); ejectJointView = animator.GetJointHandle( "eject" ); guiLightJointView = animator.GetJointHandle( "guiLight" ); ventLightJointView = animator.GetJointHandle( "ventLight" ); */ #ifdef _DENTONMOD #else // get the projectile projectileDict.Clear(); const char *projectileName = weaponDef->dict.GetString( "def_projectile" ); if ( projectileName[0] != '\0' ) { const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projectileName, false ); if ( !projectileDef ) { gameLocal.Warning( "Unknown projectile '%s' in weapon '%s'", projectileName, objectname ); } else { const char *spawnclass = projectileDef->dict.GetString( "spawnclass" ); idTypeInfo *cls = idClass::GetClass( spawnclass ); if ( !cls || !cls->IsType( idProjectile::Type ) ) { gameLocal.Warning( "Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname ); } else { projectileDict = projectileDef->dict; } } } #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" ); /* //old code memset( &muzzleFlash, 0, sizeof( muzzleFlash ) ); muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber; muzzleFlash.allowLightInViewID = owner->entityNumber+1; // the weapon lights will only be in first person guiLight.allowLightInViewID = owner->entityNumber+1; nozzleGlow.allowLightInViewID = owner->entityNumber+1; muzzleFlash.pointLight = flashPointLight; muzzleFlash.shader = flashShader; muzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0]; muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1]; muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2]; muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f; muzzleFlash.lightRadius[0] = flashRadius; muzzleFlash.lightRadius[1] = flashRadius; muzzleFlash.lightRadius[2] = flashRadius; if ( !flashPointLight ) { muzzleFlash.target = flashTarget; muzzleFlash.up = flashUp; muzzleFlash.right = flashRight; muzzleFlash.end = flashTarget; } // the world muzzle flash is the same, just positioned differently worldMuzzleFlash = muzzleFlash; worldMuzzleFlash.suppressLightInViewID = owner->entityNumber+1; worldMuzzleFlash.allowLightInViewID = 0; worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber; */ //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" ); 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 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 /* //was if ( !newParticle.particleFlags.isOnWorldModel ) { // Smoke is not differentiated as world and view particles newParticle.particleFlags.isSmoke = weaponDef->dict.GetBool(va("%s_smoke", prtName.c_str()), "0"); newParticle.joint = animator.GetJointHandle( jointName.c_str() ); } else { idEntity *ent = worldModel.GetEntity(); newParticle.particleFlags.isSmoke = false; newParticle.joint = ent->GetAnimator()->GetJointHandle( jointName.c_str() ); } */ 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( 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( 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())); /* //was if ( newLight.lightFlags.isOnWorldModel ) { // third person only light idEntity *ent = worldModel.GetEntity(); newLight.joint = ent->GetAnimator()->GetJointHandle( jointName.c_str() ); newLight.light.suppressLightInViewID = owner->entityNumber+1; } else { newLight.joint = animator.GetJointHandle(jointName.c_str()); newLight.light.allowLightInViewID = owner->entityNumber+1; } */ //ivan start 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; } //ivan end 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 // renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip) ); renderEntity.gui[ 0 ]->SetStateString( "player_ammo", ClipSize() ? va( "%i", inclip ) : "--" ); renderEntity.gui[ 0 ]->SetStateString( "player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--" ); // renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) ); renderEntity.gui[ 0 ]->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 renderEntity.gui[ 0 ]->SetStateString( "player_ammo_count", va("%i", AmmoCount())); //new //rev grab //Grabber Gui Info renderEntity.gui[ 0 ]->SetStateString( "grabber_state", va("%i", grabberState)); //rev grab } /*********************************************************************** 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->particleFlags.isOnWorldModel, part->joint, muzzleOrigin, muzzleAxis ); 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->lightFlags.isOnWorldModel, light->joint, light->light.origin, light->light.axis )) { 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 ) { /* 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( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis ); GetGlobalJointTransform( flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis ); } /* ================ idWeapon::MuzzleFlashLight ================ */ void idWeapon::MuzzleFlashLight( void ) { if ( !lightOn && ( !g_muzzleFlash.GetBool() || !worldMuzzleFlash.lightRadius[0] ) ) { //was: muzzleFlash return; } if ( flashJointWorld == INVALID_JOINT ) { return; } UpdateFlashPosition(); // these will be different each fire //commented out by ivan //muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); //muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ]; worldMuzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); worldMuzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ]; // the light will be removed at this time muzzleFlashEnd = gameLocal.time + flashTime; //ivan start /* //was: if ( muzzleFlashHandle != -1 ) { gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash ); gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash ); } else { muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash ); worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash ); } */ if ( worldMuzzleFlashHandle != -1 ) { gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash ); } else { worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash ); } //ivan end } /* ================ idWeapon::UpdateSkin ================ */ bool idWeapon::UpdateSkin( void ) { const function_t *func; if ( !isLinked ) { return false; } func = scriptObject.GetFunction( "UpdateSkin" ); if ( !func ) { common->Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() ); return false; } // use the frameCommandThread since it's safe to use outside of framecommands gameLocal.frameCommandThread->CallFunction( this, func, true ); gameLocal.frameCommandThread->Execute(); return true; } /* ================ idWeapon::SetModel ================ */ void idWeapon::SetModel( const char *modelname ) { assert( modelname ); if ( modelDefHandle >= 0 ) { gameRenderWorld->RemoveDecals( modelDefHandle ); } renderEntity.hModel = animator.SetModel( modelname ); if ( renderEntity.hModel ) { renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin(); animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints ); } else { renderEntity.customSkin = NULL; renderEntity.callback = NULL; renderEntity.numJoints = 0; renderEntity.joints = NULL; } // hide the model until an animation is played Hide(); } /* ================ idWeapon::GetGlobalJointTransform This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights ================ */ //bool idWeapon::GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) { bool idWeapon::GetGlobalJointTransform( const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) { /* //was: if ( viewModel ) { // view model if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) { offset = offset * viewWeaponAxis + viewWeaponOrigin; axis = axis * viewWeaponAxis; return true; } } else { // world model if ( worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) { offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis(); axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis(); return true; } } offset = viewWeaponOrigin; axis = viewWeaponAxis; return false; */ //ivan start - world model is now the model itself if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) { offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis(); axis = axis * GetPhysics()->GetAxis(); return true; } offset = GetPhysics()->GetOrigin(); //viewWeaponOrigin; 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(); /* if ( worldModel.GetEntity() ) { worldModel.GetEntity()->Hide(); } */ muzzleFlashEnd = 0; } /* ================ idWeapon::ShowWeapon ================ */ void idWeapon::ShowWeapon( void ) { Show(); /* if ( worldModel.GetEntity() ) { worldModel.GetEntity()->Show(); } */ if ( lightOn ) { MuzzleFlashLight(); } } /* ================ idWeapon::HideWorldModel ================ */ void idWeapon::HideWorldModel( void ) { /* //was: if ( worldModel.GetEntity() ) { worldModel.GetEntity()->Hide(); } */ Hide(); //ivan } /* ================ idWeapon::ShowWorldModel ================ */ void idWeapon::ShowWorldModel( void ) { /* //was: if ( worldModel.GetEntity() ) { worldModel.GetEntity()->Show(); } */ Show(); //ivan } /* ================ idWeapon::OwnerDied ================ */ void idWeapon::OwnerDied( void ) { if ( isLinked ) { SetState( "OwnerDied", 0 ); thread->Execute(); //rev grab // Update the grabber effects if ( /*!gameLocal.isMultiplayer &&*/ grabberState != -1 ) { grabber.Update( owner, hide ); } //rev grab } Hide(); /* if ( worldModel.GetEntity() ) { worldModel.GetEntity()->Hide(); } */ // don't clear the weapon immediately since the owner might have killed himself by firing the weapon // within the current stack frame PostEventMS( &EV_Weapon_Clear, 0 ); } /* ================ idWeapon::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 ) { //rev grab //if ( sndHum ) { if ( sndHum && grabberState == -1 ) { // _D3XP :: don't stop grabber hum //rev grab 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; //rev grab //if ( sndHum ) { if ( sndHum && grabberState == -1 ) { // _D3XP :: don't stop grabber hum //rev grab StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL ); } } } /* ================ idWeapon::isReady ================ */ bool idWeapon::IsReady( void ) const { return !hide && !IsHidden() && ( ( status == WP_RELOAD ) || ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) ); } /* ================ idWeapon::IsReloading ================ */ bool idWeapon::IsReloading( void ) const { return ( status == WP_RELOAD ); } /* ================ idWeapon::IsHolstered ================ */ bool idWeapon::IsHolstered( void ) const { return ( status == WP_HOLSTERED ); } /* ================ idWeapon::ShowCrosshair ================ */ bool idWeapon::ShowCrosshair( void ) const { return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) ); } /* ===================== idWeapon::CanDrop ===================== */ bool idWeapon::CanDrop( void ) const { if ( !weaponDef ) { // || !worldModel.GetEntity() return false; } const char *classname = weaponDef->dict.GetString( "def_dropItem" ); if ( !classname[ 0 ] ) { return false; } return true; } /* ================ idWeapon::WeaponStolen ================ */ void idWeapon::WeaponStolen( void ) { assert( !gameLocal.isClient ); //ivan start - always set WeaponDropped state /* //was: if ( projectileEnt ) { if ( isLinked ) { SetState( "WeaponStolen", 0 ); thread->Execute(); } projectileEnt = NULL; } */ if ( isLinked ) { SetState( "WeaponDropped", 0 ); thread->Execute(); } if ( projectileEnt ) { projectileEnt = NULL; } //ivan end // set to holstered so we can switch weapons right away status = WP_HOLSTERED; HideWeapon(); } /* ===================== idWeapon::DropItem ===================== */ idEntity * idWeapon::DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died ) { if ( !weaponDef ) { //|| !worldModel.GetEntity() return NULL; } if ( !allowDrop ) { return NULL; } const char *classname = weaponDef->dict.GetString( "def_dropItem" ); if ( !classname[0] ) { return NULL; } StopSound( SND_CHANNEL_BODY, true ); StopSound( SND_CHANNEL_BODY3, true ); //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; /* if ( ventLightJointView == INVALID_JOINT ) { return; } // // vent light // if ( nozzleGlowHandle == -1 ) { memset(&nozzleGlow, 0, sizeof(nozzleGlow)); if ( owner ) { nozzleGlow.allowLightInViewID = owner->entityNumber+1; } nozzleGlow.pointLight = true; nozzleGlow.noShadows = true; nozzleGlow.lightRadius.x = nozzleGlowRadius; nozzleGlow.lightRadius.y = nozzleGlowRadius; nozzleGlow.lightRadius.z = nozzleGlowRadius; nozzleGlow.shader = nozzleGlowShader; nozzleGlow.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f; nozzleGlow.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis ); nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow); } GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis ); nozzleGlow.shaderParms[ SHADERPARM_RED ] = nozzleGlowColor.x * s; nozzleGlow.shaderParms[ SHADERPARM_GREEN ] = nozzleGlowColor.y * s; nozzleGlow.shaderParms[ SHADERPARM_BLUE ] = nozzleGlowColor.z * s; gameRenderWorld->UpdateLightDef(nozzleGlowHandle, &nozzleGlow); */ } /* ================ idWeapon::BloodSplat ================ */ bool idWeapon::BloodSplat( float size ) { float s, c; idMat3 localAxis, axistemp; idVec3 localOrigin, normal; if ( hasBloodSplat ) { return true; } hasBloodSplat = true; if ( modelDefHandle < 0 ) { return false; } if ( !GetGlobalJointTransform( 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 ) { int time; float amount; idAngles ang; idVec3 offset; time = kick_endtime - gameLocal.time; if ( time <= 0 ) { return; } if ( muzzle_kick_maxtime <= 0 ) { return; } if ( time > muzzle_kick_maxtime ) { time = muzzle_kick_maxtime; } amount = ( float )time / ( float )muzzle_kick_maxtime; ang = muzzle_kick_angles * amount; offset = muzzle_kick_offset * amount; origin = origin - axis * offset; axis = ang.ToMat3() * axis; } */ /* ================ idWeapon::ConstructScriptObject Called during idEntity::Spawn. Calls the constructor on the script object. Can be overridden by subclasses when a thread doesn't need to be allocated. ================ */ idThread *idWeapon::ConstructScriptObject( void ) { const function_t *constructor; thread->EndThread(); // call script object's constructor constructor = scriptObject.GetConstructor(); if ( !constructor ) { gameLocal.Error( "Missing constructor on '%s' for weapon", scriptObject.GetTypeName() ); } // init the script object's data scriptObject.ClearObject(); thread->CallFunction( this, constructor, true ); thread->Execute(); return thread; } /* ================ idWeapon::DeconstructScriptObject Called during idEntity::~idEntity. Calls the destructor on the script object. Can be overridden by subclasses when a thread doesn't need to be allocated. Not called during idGameLocal::MapShutdown. ================ */ void idWeapon::DeconstructScriptObject( void ) { const function_t *destructor; if ( !thread ) { return; } // don't bother calling the script object's destructor on map shutdown if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) { return; } thread->EndThread(); // call script object's destructor destructor = scriptObject.GetDestructor(); if ( destructor ) { // start a thread that will run immediately and end thread->CallFunction( this, destructor, true ); thread->Execute(); thread->EndThread(); } // clear out the object's memory scriptObject.ClearObject(); } /* ================ idWeapon::UpdateScript ================ */ void idWeapon::UpdateScript( void ) { int count; if ( !isLinked ) { return; } // only update the script on new frames if ( !gameLocal.isNewFrame ) { return; } if ( idealState.Length() ) { SetState( idealState, animBlendFrames ); } // update script state, which may call Event_LaunchProjectiles, among other things count = 10; while( ( thread->Execute() || idealState.Length() ) && count-- ) { // happens for weapons with no clip (like grenades) if ( idealState.Length() ) { SetState( idealState, animBlendFrames ); } } WEAPON_RELOAD = false; WEAPON_SPECIAL = false; } /* ================ idWeapon::AlertMonsters ================ */ void idWeapon::AlertMonsters( void ) { trace_t tr; idEntity *ent; /* //was: idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target; gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner ); if ( g_debugWeapon.GetBool() ) { gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 ); gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 ); } */ //ivan start idVec3 end = worldMuzzleFlash.origin + worldMuzzleFlash.axis * worldMuzzleFlash.target; 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 ); gameRenderWorld->DebugArrow( colorGreen, worldMuzzleFlash.origin, tr.endpos, 2, 0 ); } //ivan end if ( tr.fraction < 1.0f ) { ent = gameLocal.GetTraceEntity( tr ); if ( ent->IsType( idAI::Type ) ) { static_cast( 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 /* //was: end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f ); end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f ); gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner ); if ( g_debugWeapon.GetBool() ) { gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 ); gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 ); } */ //ivan start end += worldMuzzleFlash.axis * worldMuzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f ); 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 ); gameRenderWorld->DebugArrow( colorGreen, worldMuzzleFlash.origin, tr.endpos, 2, 0 ); } //ivan end if ( tr.fraction < 1.0f ) { ent = gameLocal.GetTraceEntity( tr ); if ( ent->IsType( idAI::Type ) ) { static_cast( ent )->TouchedByFlashlight( owner ); } else if ( ent->IsType( idTrigger::Type ) ) { ent->Signal( SIG_TOUCH ); ent->ProcessEvent( &EV_Touch, owner, &tr ); } } } //ivan start /* ================ idWeapon::CalculateDynamicSpread ================ */ void idWeapon::CalculateDynamicSpread( void ) { if( spreadVelocityFactor > 0 ){ //float velocity = owner->GetPlayerPhysics()->GetLinearVelocity().LengthFast(); ////Updated Rev 2018 for walk aiming Movement speed is no longer a factor. Spread changes depend on the player walking, running and crouching. dynamicSpreadValue = spreadBaseValue + spreadVelocityFactor; //dynamicSpreadValue = spreadBaseValue + velocity * spreadVelocityFactor; }else{ dynamicSpreadValue = spreadBaseValue; } if( owner->AI_CROUCH ){ dynamicSpreadValue = dynamicSpreadValue * spreadCrouchFactor; } if( owner->AI_RUN ){ //Added in 2018 by Rev for new walk aim system. We will just use crouch factor to set this instead of making a clone of it. Really no need for the extra code dynamicSpreadValue = dynamicSpreadValue * spreadCrouchFactor; } //gameLocal.Printf("dynamicSpreadValue: %f\n", dynamicSpreadValue); } //ivan end /* ================ 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 playerViewOrigin = owner->firstPersonViewOrigin; #ifdef USE_AIM_DIR_FIX playerViewAxis = owner->fixedAimViewAxis; #else playerViewAxis = owner->firstPersonViewAxis; #endif /* //ivan test start if ( worldModel.GetEntity() ) { gameLocal.Printf( "DIVERSITY: %f", worldModel.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_DIVERSITY ] ); gameLocal.Printf( " - TIMEOFFSET: %f", worldModel.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] ); gameLocal.Printf( " - MODE: %f", worldModel.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_MODE ] ); gameLocal.Printf( " - TIMESCALE: %f", worldModel.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMESCALE ] ); gameLocal.Printf( " - PARTICLE_STOPTIME: %f\n", worldModel.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] ); } //ivan test end */ //ivan start // calculate dynamic spread CalculateDynamicSpread(); //ivan end /* //was: // calculate weapon position based on player movement bobbing owner->CalculateViewWeaponPos( viewWeaponOrigin, viewWeaponAxis ); //no: now weapon is bound to joint // hide offset is for dropping the gun when approaching a GUI or NPC //no: now weapon is bound to joint // This is simpler to manage than doing the weapon put-away animation if ( gameLocal.time - hideStartTime < hideTime ) { float frac = ( float )( gameLocal.time - hideStartTime ) / ( float )hideTime; if ( hideStart < hideEnd ) { frac = 1.0f - frac; frac = 1.0f - frac * frac; } else { frac = frac * frac; } hideOffset = hideStart + ( hideEnd - hideStart ) * frac; } else { hideOffset = hideEnd; if ( hide && disabled ) { Hide(); } } viewWeaponOrigin += hideOffset * viewWeaponAxis[ 2 ]; // kick up based on repeat firing //no: now weapon is bound to joint MuzzleRise( viewWeaponOrigin, viewWeaponAxis ); // 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(); /* //was: // only show the surface in player view renderEntity.allowSurfaceInViewID = owner->entityNumber+1; // crunch the depth range so it never pokes into walls this breaks the machine gun gui renderEntity.weaponDepthHack = true; // present the model if ( showViewModel ) { Present(); } else { FreeModelDef(); } if ( worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity() ) { // deal with the third-person visible world model // don't show shadows of the world model in first person if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() ) { worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0; } else { worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber+1; worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber; } } */ //ivan start Present(); //always show the model renderEntity.suppressShadowInViewID = 0; //allow shadows //ivan end if ( nozzleFx ) { UpdateNozzleFx(); } // muzzle smoke if ( !disabled && weaponSmoke && ( weaponSmokeStartTime != 0 ) ) { // use the barrel joint if available /* //was: //ivan start - try worldmodel first! if ( barrelJointWorld != INVALID_JOINT ) { //ivan - compare with INVALID_JOINT ! GetGlobalJointTransform( false, barrelJointWorld, muzzleOrigin, muzzleAxis ); } else //ivan end if ( showViewModel && barrelJointView != INVALID_JOINT ) { //ivan - compare with INVALID_JOINT ! - showViewModel added GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis ); } else { // default to going straight out the view muzzleOrigin = playerViewOrigin; muzzleAxis = playerViewAxis; } */ //ivan start if ( barrelJointWorld != INVALID_JOINT ) { //compare with INVALID_JOINT ! GetGlobalJointTransform( barrelJointWorld, muzzleOrigin, muzzleAxis ); } else { // default to going straight out the view muzzleOrigin = playerViewOrigin; muzzleAxis = playerViewAxis; } //ivan end // spit out a particle if ( !gameLocal.smokeParticles->EmitSmoke( weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis ) ) { weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0; } } if ( strikeSmoke && strikeSmokeStartTime != 0 ) { // spit out a particle if ( !gameLocal.smokeParticles->EmitSmoke( strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis ) ) { strikeSmokeStartTime = 0; } } #ifdef _DENTONMOD //ivan start /* //was: if ( showViewModel && !disabled ) { UpdateWeaponFx(); } */ //disabled flag ensures that fx wont be played when entering cinematic if ( !disabled ) { UpdateWeaponFx(); } //ivan end #endif //_DENTONMOD //rev grab // Update the grabber effects if ( grabberState != -1 ) { grabberState = grabber.Update( owner, hide ); } //rev grab // remove the muzzle flash light when it's done if ( ( !lightOn && ( gameLocal.time >= muzzleFlashEnd ) ) || IsHidden() ) { /* if ( muzzleFlashHandle != -1 ) { gameRenderWorld->FreeLightDef( muzzleFlashHandle ); muzzleFlashHandle = -1; } */ if ( worldMuzzleFlashHandle != -1 ) { gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle ); worldMuzzleFlashHandle = -1; } } // update the muzzle flash light, so it moves with the gun if ( worldMuzzleFlashHandle != -1 ) { //was: muzzleFlashHandle UpdateFlashPosition(); //gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash ); gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash ); // wake up monsters with the flashlight if ( !gameLocal.isMultiplayer && lightOn && !owner->fl.notarget ) { AlertMonsters(); } } /* // update the gui light if ( guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT ) { GetGlobalJointTransform( true, guiLightJointView, guiLight.origin, guiLight.axis ); if ( ( guiLightHandle != -1 ) ) { gameRenderWorld->UpdateLightDef( guiLightHandle, &guiLight ); } else { guiLightHandle = gameRenderWorld->AddLightDef( &guiLight ); } } */ if ( status != WP_READY && sndHum ) { StopSound( SND_CHANNEL_BODY, false ); } UpdateSound(); //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 WEAPON_SPECIAL_HOLD = false; // new WEAPON_RELOAD = false; WEAPON_NETRELOAD = false; WEAPON_NETENDRELOAD = false; WEAPON_NETFIRING = false; WEAPON_RAISEWEAPON = false; WEAPON_LOWERWEAPON = false; //rev grab grabber.Update( this->GetOwner(), true ); //rev grab } 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 ); } 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; } //REV GRAB START /* ================ idWeapon::GetGrabberState Returns the current grabberState ================ */ int idWeapon::GetGrabberState( void ) const { return grabberState; } //REV GRAB END /* ================ idWeapon::WriteToSnapshot ================ */ void idWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const { msg.WriteBits( ammoClip, ASYNC_PLAYER_INV_CLIP_BITS ); //msg.WriteBits( worldModel.GetSpawnId(), 32 ); msg.WriteBits( lightOn, 1 ); msg.WriteBits( isFiring ? 1 : 0, 1 ); } /* ================ idWeapon::ReadFromSnapshot ================ */ void idWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) { ammoClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS ); //worldModel.SetSpawnId( msg.ReadBits( 32 ) ); bool snapLight = msg.ReadBits( 1 ) != 0; isFiring = msg.ReadBits( 1 ) != 0; // WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client if ( owner && gameLocal.localClientNum != owner->entityNumber && WEAPON_NETFIRING.IsLinked() ) { // immediately go to the firing state so we don't skip fire animations if ( !WEAPON_NETFIRING && isFiring ) { idealState = "Fire"; } // immediately switch back to idle if ( WEAPON_NETFIRING && !isFiring ) { idealState = "Idle"; } WEAPON_NETFIRING = isFiring; } if ( snapLight != lightOn ) { Reload(); } } /* ================ idWeapon::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( declManager->DeclByIndex( DECL_SKIN, index ) ) : NULL; UpdateVisuals(); /* if ( worldModel.GetEntity() ) { worldModel.GetEntity()->SetSkin( renderEntity.customSkin ); } */ return true; } default: break; } return idEntity::ClientReceiveEvent( event, time, msg ); } /*********************************************************************** Script events ***********************************************************************/ /* =============== idWeapon::Event_Clear =============== */ void idWeapon::Event_Clear( void ) { Clear(); } /* =============== idWeapon::Event_GetOwner =============== */ void idWeapon::Event_GetOwner( void ) { idThread::ReturnEntity( owner ); } /* =============== idWeapon::Event_WeaponState =============== */ void idWeapon::Event_WeaponState( const char *statename, int blendFrames ) { const function_t *func; func = scriptObject.GetFunction( statename ); if ( !func ) { assert( 0 ); gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() ); } idealState = statename; //ivan start /* was: if ( !idealState.Icmp( "Fire" ) ) { isFiring = true; } else { isFiring = false; } */ //ivan start if ( !idealState.Icmp( "Fire" ) ) { isFiring = true; isSecFiring = false; } else if ( !idealState.Icmp( "SecFire" ) ) { isFiring = false; isSecFiring = true; } else { isFiring = false; isSecFiring = false; } //ivan end 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; } //#ifdef _DENTONMOD //#else owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? amount : ( amount * ammoRequired ) ); //#endif if ( clipSize && ammoRequired ) { ammoClip -= powerAmmo ? amount : ( amount * ammoRequired ); if ( ammoClip < 0 ) { //#ifdef _DENTONMOD // Now, the ammo in clip is separate from that of ammo in inventory. So when the ammo is removed from clip, // only remove remaining ammo from the inventory // owner->inventory.UseAmmo( ammoType, -ammoClip ); //#endif 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(); /* if ( worldModel.GetEntity() ) { anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname ); if ( anim ) { worldModel.GetEntity()->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) ); } } */ } animBlendFrames = 0; idThread::ReturnInt( 0 ); } /* =============== idWeapon::Event_PlayCycle =============== */ void idWeapon::Event_PlayCycle( int channel, const char *animname ) { int anim; anim = animator.GetAnim( animname ); if ( !anim ) { gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() ); animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) ); animDoneTime = 0; } else { if ( !( owner && owner->GetInfluenceLevel() ) ) { Show(); } animator.CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) ); animDoneTime = animator.CurrentAnim( channel )->GetEndTime(); /* if ( worldModel.GetEntity() ) { anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname ); worldModel.GetEntity()->GetAnimator()->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) ); } */ } animBlendFrames = 0; idThread::ReturnInt( 0 ); } /* =============== idWeapon::Event_AnimDone =============== */ void idWeapon::Event_AnimDone( int channel, int blendFrames ) { if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) { idThread::ReturnInt( true ); } else { idThread::ReturnInt( false ); } } /* =============== idWeapon::Event_SetBlendFrames =============== */ void idWeapon::Event_SetBlendFrames( int channel, int blendFrames ) { animBlendFrames = blendFrames; } /* =============== idWeapon::Event_GetBlendFrames =============== */ void idWeapon::Event_GetBlendFrames( int channel ) { idThread::ReturnInt( animBlendFrames ); } /* ================ idWeapon::Event_Next ================ */ void idWeapon::Event_Next( void ) { // change to another weapon if possible owner->NextBestWeapon(); } /* ================ idWeapon::Event_SetSkin ================ */ void idWeapon::Event_SetSkin( const char *skinname ) { const idDeclSkin *skinDecl; if ( !skinname || !skinname[ 0 ] ) { skinDecl = NULL; } else { skinDecl = declManager->FindSkin( skinname ); } renderEntity.customSkin = skinDecl; UpdateVisuals(); /* if ( worldModel.GetEntity() ) { worldModel.GetEntity()->SetSkin( skinDecl ); } */ 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 ] ); //was muzzleFlash } /* ================ idWeapon::Event_SetLightParm ================ */ void idWeapon::Event_SetLightParm( int parmnum, float value ) { if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) { gameLocal.Error( "shader parm index (%d) out of range", parmnum ); } //muzzleFlash.shaderParms[ parmnum ] = value; worldMuzzleFlash.shaderParms[ parmnum ] = value; UpdateVisuals(); } /* ================ idWeapon::Event_SetLightParms ================ */ void idWeapon::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) { /* muzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0; muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1; muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2; muzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3; */ worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0; worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1; worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2; worldMuzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3; UpdateVisuals(); } //rev grab start /* ================ idWeapon::Event_Grabber ================ */ void idWeapon::Event_Grabber( int enable ) { if ( enable ) { grabberState = 0; } else { grabberState = -1; } } /* ================ idWeapon::Event_GrabberHasTarget ================ */ void idWeapon::Event_GrabberHasTarget() { idThread::ReturnInt( grabberState ); } /* ================ idWeapon::Event_GrabberSetGrabDistance ================ */ void idWeapon::Event_GrabberSetGrabDistance( float dist ) { grabber.SetDragDistance( dist ); } //rev grab end /* ================ idWeapon::Event_CreateProjectile ================ */ void idWeapon::Event_CreateProjectile( int projtype ) { //ivan: projtype added if ( !gameLocal.isClient ) { //ivan start if( projtype != owner->GetProjectileType() ) { if(!ChangeProjectileDef(projtype)){ gameLocal.Warning( "Cannot fire proj number %d", projtype ); return; } } //ivan end 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, int projtype, int useBarrelDir ) { //ivan: projtype, useBarrelDir added idProjectile *proj; idEntity *ent; int i; idVec3 dir; float ang = 0.0f; float spin; //float distance; trace_t tr; //idVec3 start; //idBounds ownerBounds, projBounds; bool barrelLaunch; bool tracer, beam; if ( IsHidden() ) { return; } //ivan start if( projtype != owner->GetProjectileType() ) { if(!ChangeProjectileDef(projtype)){ gameLocal.Warning( "Cannot fire proj number %d", projtype ); return; } } //ivan end if ( !projectileDict.GetNumKeyVals() ) { const char *classname = weaponDef->dict.GetString( "classname" ); gameLocal.Warning( "No projectile defined on '%s'", classname ); return; } // avoid all ammo considerations on an MP client if ( !gameLocal.isClient ) { // check if we're out of ammo or the clip is empty int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired ); if ( !ammoAvail || ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) ) { return; } // if this is a power ammo weapon ( currently only the bfg ) then make sure // we only fire as much power as available in each clip if ( powerAmmo ) { // power comes in as a float from zero to max // if we use this on more than the bfg will need to define the max // in the .def as opposed to just in the script so proper calcs // can be done here. dmgPower = ( int )dmgPower + 1; if ( dmgPower > ammoClip ) { dmgPower = ammoClip; } } owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired ); if ( clipSize && ammoRequired ) { ammoClip -= powerAmmo ? dmgPower : ammoRequired; } } 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 const bool isPrediction = gameLocal.isClient && projectileDict.GetBool( "net_instanthit" ); //ivan start //spread if( spread < 0 ){ //auto spread! spread = dynamicSpreadValue; } //ivan end 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; // DG: unused float traceDist = 0.0f, muzzleToTargetDist = 0.0f; // DG: make sure it's initialized to shut up compiler 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 || useBarrelDir ) { //ivan - useBarrelDir added, so we get muzzleAxis // calculate the muzzle position if ( barrelJointWorld != INVALID_JOINT ) { //ivan - was barrelJointView // there is an explicit joint for the muzzle GetGlobalJointTransform( barrelJointWorld, muzzleOrigin, muzzleAxis ); //ivan: was true , barrelJointView if ( barrelLaunch ) { tracer = false; } muzzle_pos = muzzleOrigin; // + playerViewAxis[ 0 ] * 2.0f; // muzzle_pos for multiplayer prediction 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 - DG: unused } else { // if we dont find a proper bone then cancel all the effects. barrelLaunch = false; tracer = false; beam = false; useBarrelDir = false; //ivan - useBarrelDir added } } 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; const idMat3 &aimAxis = ( useBarrelDir ? muzzleAxis : playerViewAxis ); updown_offset.Zero(); if( spreadMode == WP_SPREADMODE_2D_STEP){ 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 } } //ivan end for( i = 0; i < num_projectiles; i++ ) { //ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); //spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); //ivan start - fire modes - direction switch( spreadMode ) { case WP_SPREADMODE_DEFAULT: { ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); dir = aimAxis[ 0 ] + aimAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - aimAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); break; } case WP_SPREADMODE_2D_STEP: { // FIXME: DG: what if num_projectiles == 1? then ang is not initialized properly (I set it to 0 above) dir = (aimAxis[ 0 ] * (1-ang*firemodeCounterPos) ) + ( aimAxis[ 2 ] * ang*firemodeCounter ); //upd counter if(firemodeCounter >= 0){ firemodeCounter++; firemodeCounterPos = firemodeCounter; } firemodeCounter = - firemodeCounter; break; } case WP_SPREADMODE_2D_PARALLEL: { dir = aimAxis[ 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; break; } case WP_SPREADMODE_2D_RANDOM: { ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); dir = aimAxis[ 0 ] + aimAxis[ 2 ] * ( ang * idMath::Sin( spin ) ); break; } default: { gameLocal.Error("Unknown WEAPON fire mode: %d", spreadMode ); break; } } //ivan test end dir.Normalize(); if ( barrelLaunch || tracer || beam ) { // Do not execute this part unless projectile is barrel launched or has a tracer effect. //Rev start projectileOrigin = cvarSystem->GetCVarInteger( "pm_projectileOrigin" ); if ( projectileOrigin > 0 ) { gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner ); traceDist = (tr.endpos - muzzle_pos).LengthSqr(); //THIS FIXES PROJECTILES FIRED FROM THE BARREL NOT GOING TO THE CROSSHAIR //Launching projectiles now always comes from the thirdperson weapon's barrel model... even in first person. Direction is always towards Thirdperson crosshair. //Launchfrombarrel should be set to 0 in weapon defs. setting to 1 is no longer needed. } else { gameLocal.clip.Translation( tr, view_pos, view_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner ); traceDist = (tr.endpos - view_pos).LengthSqr(); } //Rev end //ivan start - fix aim // Problem: muzzleDistFromView is the dist from muzzle and viewpos. // It was usually very low in D3. // If tracer lenght was smaller than it, then we are too close to a wall/something and the aim was corrected. // Now: dist from muzzle and viewpos is very big --> aim was corrected even when it should not. // Aim correction is now disabled. /* if ( traceDist > muzzleDistFromView ) { // make sure the muzzle is not to close to walls etc if ( barrelLaunch ) { dir = tr.endpos - muzzle_pos; dir.Normalize(); } else if ( tracer ) { muzzleDir = tr.endpos - muzzle_pos; // muzzleToTargetDist = muzzleDir.Length(); muzzleToTargetDist = muzzleDir.LengthSqr(); // This is faster muzzleDir.Normalize(); } } else{ if ( tracer || beam ) { // Dont do tracers when weapon is too close to walls, objects etc. tracer = false; beam = false; } } */ //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 ); } 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 { 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 ){ launch_pos = muzzle_pos; } proj = static_cast(ent); // proj->Create( owner, muzzleOrigin, dir ); proj->Create( owner, launch_pos, dir ); // make sure the projectile starts inside the bounding box of the owner /* //commented out by ivan: don't check bbox // Do this for every projectile, no matter how slow it makes whole thing // if ( i == 0 ) { // Do this only for first projectile projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() ); if ( ( ownerBounds - projBounds).RayIntersection( launch_pos, playerViewAxis[0], distance ) ) { start = launch_pos + distance * playerViewAxis[0]; >>>>>>> } else { start = ownerBounds.GetCenter(); } gameLocal.clip.Translation( tr, start, launch_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner ); launch_pos = tr.endpos; // } */ #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 - postion 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 ) { // toss the brass if (brassDelay >= 0) // eject brass behaviour can be disabled by simply setting the delay to 0 PostEventMS( &EV_Weapon_EjectBrass, brassDelay ); } // add the light for the muzzleflash if ( !lightOn ) { MuzzleFlashLight(); } owner->WeaponFireFeedback( &weaponDef->dict ); //ivan - todo: call WeaponSecFireFeedback if is secondary firemode // reset muzzle smoke weaponSmokeStartTime = gameLocal.realClientTime; //ivan - keep track of this moment lastFiredTime = gameLocal.time; } //----------------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 ); /* float time_in_secs; if ( projectileDict.GetFloat( "muzzle_kick_time", "0.0f", time_in_secs ) ) { muzzle_kick_time = SEC2MS( time_in_secs ); } else { muzzle_kick_time = SEC2MS( spawnArgs.GetFloat( "muzzle_kick_time" ) ); } if ( projectileDict.GetFloat( "muzzle_kick_maxtime", "0.0f", time_in_secs ) ) { muzzle_kick_maxtime = SEC2MS( time_in_secs ); } else { muzzle_kick_maxtime = SEC2MS( spawnArgs.GetFloat( "muzzle_kick_maxtime" ) ); } if( !projectileDict.GetAngles( "muzzle_kick_angles", "0 0 0", muzzle_kick_angles ) ) { muzzle_kick_angles = spawnArgs.GetAngles( "muzzle_kick_angles" ); } if( !projectileDict.GetVector( "muzzle_kick_offset", "0 0 0", muzzle_kick_offset ) ) { muzzle_kick_offset = spawnArgs.GetVector( "muzzle_kick_offset" ); } */ 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::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_StartWeaponParticle( const char* prtName) { StartWeaponParticle( prtName ); } void idWeapon::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_StopWeaponParticle( const char* prtName) { StopWeaponParticle( prtName ); } 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 2018 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 return; } if ( gameLocal.isClient ) { return; } idMat3 axis; idVec3 origin, linear_velocity, angular_velocity; idEntity *ent; if ( !GetGlobalJointTransform( ejectJointWorld, origin, axis ) ) { //was: ejectJointView 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(ent); debris->Create( owner, origin, axis ); debris->Launch(); if( spawnArgs.GetBool( "fixed_brass_dir" ) ) { 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 ); } } /* =============== 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 Returns the total number of rounds regardless of the required ammo // new ================ */ int idWeapon::AmmoCount( void ) const { if ( owner ) { return owner->inventory.HasAmmo( ammoType, 1 ); } else { return 0; } } //ivan start /* =============== idWeapon::HasToWalk =============== */ bool idWeapon::HasToWalk( void ) const { if ( isFiring || isSecFiring ) { return true; } else if( lastFiredTime + 500 > gameLocal.time){ //go on walking after the last fired proj for a bit return true; } else { return false; } } /* ================ idWeapon::GetWalkSpeedMult ================ */ float idWeapon::GetWalkSpeedMult() const { if ( HasToWalk() ) { return firingWalkSpeedMult; } else { return 1.0f; } } /* =============== idWeapon::Event_PwAmmoAvailable =============== */ void idWeapon::Event_SetWeaponMode( int value ) { owner->SetCurrentWeaponMode( value ); UpdateSkin(); UpdSpreadSettings(); //upd spread settings } /* =============== idWeapon::Event_PwAmmoAvailable =============== */ void idWeapon::Event_GetWeaponMode( void ) { int myMode = owner->GetCurrentWeaponMode(); idThread::ReturnInt( myMode ); } //ivan end