// RAVEN BEGIN // bdube: note that this file is no longer merged with Doom3 updates // // MERGE_DATE 09/30/2004 #include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" #include "Weapon.h" #include "Projectile.h" #include "ai/AI.h" #include "ai/AI_Manager.h" #include "client/ClientEffect.h" //#include "../renderer/tr_local.h" /*********************************************************************** rvViewWeapon ***********************************************************************/ // class def CLASS_DECLARATION( idAnimatedEntity, rvViewWeapon ) EVENT( EV_CallFunction, rvViewWeapon::Event_CallFunction ) END_CLASS /*********************************************************************** init ***********************************************************************/ /* ================ rvViewWeapon::rvViewWeapon() ================ */ rvViewWeapon::rvViewWeapon() { modelDefHandle = -1; weapon = NULL; Clear(); fl.networkSync = true; } /* ================ rvViewWeapon::~rvViewWeapon() ================ */ rvViewWeapon::~rvViewWeapon() { Clear(); } /* ================ rvViewWeapon::Spawn ================ */ void rvViewWeapon::Spawn( void ) { GetPhysics()->SetContents( 0 ); GetPhysics()->SetClipMask( 0 ); GetPhysics()->SetClipModel( NULL, 1.0f ); } /* ================ rvViewWeapon::Save ================ */ void rvViewWeapon::Save( idSaveGame *savefile ) const { int i; savefile->WriteInt ( pendingGUIEvents.Num() ); for ( i = 0; i < pendingGUIEvents.Num(); i ++ ) { savefile->WriteString ( pendingGUIEvents[i] ); } // TOSAVE: const idDeclSkin * saveSkin; // TOSAVE: const idDeclSkin * invisSkin; // TOSAVE: const idDeclSkin * saveWorldSkin; // TOSAVE: const idDeclSkin * worldInvisSkin; // TOSAVE: const idDeclSkin * saveHandsSkin; // TOSAVE: const idDeclSkin * handsSkin; // TOSAVE: friend rvWeapon; // TOSAVE: rvWeapon* weapon; } /* ================ rvViewWeapon::Restore ================ */ void rvViewWeapon::Restore( idRestoreGame *savefile ) { int i; int num; savefile->ReadInt ( num ); pendingGUIEvents.SetNum ( num ); for ( i = 0; i < num; i ++ ) { savefile->ReadString ( pendingGUIEvents[i] ); } } /* =============== rvViewWeapon::ClientPredictionThink =============== */ void rvViewWeapon::ClientPredictionThink( void ) { UpdateAnimation(); } /*********************************************************************** Weapon definition management ***********************************************************************/ /* ================ rvViewWeapon::Clear ================ */ void rvViewWeapon::Clear( void ) { DeconstructScriptObject(); scriptObject.Free(); StopAllEffects( ); // TTimo - the weapon doesn't get a proper Event_DisableWeapon sometimes, so the sound sticks // typically, client side instance join in tourney mode just wipes all ents StopSound( SND_CHANNEL_ANY, false ); 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; memset( &refSound, 0, sizeof( refSound_t ) ); refSound.referenceSoundHandle = -1; // setting diversity to 0 results in no random sound. -1 indicates random. refSound.diversity = -1.0f; if ( weapon && weapon->GetOwner ( ) ) { // don't spatialize the weapon sounds refSound.listenerId = weapon->GetOwner( )->GetListenerId(); } animator.ClearAllAnims( gameLocal.time, 0 ); FreeModelDef(); } /* ===================== rvViewWeapon::GetDebugInfo ===================== */ void rvViewWeapon::GetDebugInfo( debugInfoProc_t proc, void* userData ) { // Base class first idAnimatedEntity::GetDebugInfo( proc, userData ); weapon->GetDebugInfo( proc, userData ); } /*********************************************************************** GUIs ***********************************************************************/ /* ================ rvViewWeapon::PostGUIEvent ================ */ void rvViewWeapon::PostGUIEvent( const char* event ) { pendingGUIEvents.Append ( event ); } /*********************************************************************** Model and muzzleflash ***********************************************************************/ /* ================ rvViewWeapon::SetPowerUpSkin ================ */ void rvViewWeapon::SetPowerUpSkin( const char *name ) { /* FIXME saveSkin = renderEntity.customSkin; renderEntity.customSkin = invisSkin; if ( worldModel.GetEntity() ) { saveWorldSkin = worldModel.GetEntity()->GetSkin(); worldModel.GetEntity()->SetSkin( worldInvisSkin ); } */ } /* ================ rvViewWeapon::UpdateSkin ================ */ void rvViewWeapon::UpdateSkin( void ) { /* FIXME renderEntity.customSkin = saveSkin; if ( worldModel.GetEntity() ) { worldModel.GetEntity()->SetSkin( saveWorldSkin ); } */ } /* ================ rvViewWeapon::SetModel ================ */ void rvViewWeapon::SetModel( const char *modelname, int mods ) { 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(); } /*********************************************************************** State control/player interface ***********************************************************************/ /* ================ rvViewWeapon::Think ================ */ void rvViewWeapon::Think( void ) { // do nothing because the present is called from the player through PresentWeapon } /* ===================== rvViewWeapon::ConvertLocalToWorldTransform ===================== */ void rvViewWeapon::ConvertLocalToWorldTransform( idVec3 &offset, idMat3 &axis ) { if( !weapon ) { idAnimatedEntity::ConvertLocalToWorldTransform( offset, axis ); return; } offset = GetPhysics()->GetOrigin() + offset * weapon->ForeshortenAxis( GetPhysics()->GetAxis() ); axis *= GetPhysics()->GetAxis(); } /* ================ rvViewWeapon::UpdateModelTransform ================ */ void rvViewWeapon::UpdateModelTransform( void ) { idVec3 origin; idMat3 axis; if( !weapon ) { idAnimatedEntity::UpdateModelTransform(); return; } if ( GetPhysicsToVisualTransform( origin, axis ) ) { renderEntity.axis = axis * weapon->ForeshortenAxis( GetPhysics()->GetAxis() ); renderEntity.origin = GetPhysics()->GetOrigin() + origin * renderEntity.axis; } else { renderEntity.axis = weapon->ForeshortenAxis( GetPhysics()->GetAxis() ); renderEntity.origin = GetPhysics()->GetOrigin(); } } /* ================ rvViewWeapon::PresentWeapon ================ */ void rvViewWeapon::PresentWeapon( bool showViewModel ) { // Dont do anything with the weapon while its stale if ( fl.networkStale ) { return; } // RAVEN BEGIN // rjohnson: cinematics should never be done from the player's perspective, so don't think the weapon ( and their sounds! ) if ( gameLocal.inCinematic ) { return; } // RAVEN END // only show the surface in player view renderEntity.allowSurfaceInViewID = weapon->GetOwner()->entityNumber + 1; // crunch the depth range so it never pokes into walls this breaks the machine gun gui renderEntity.weaponDepthHackInViewID = weapon->GetOwner()->entityNumber + 1; weapon->Think(); // present the model if ( showViewModel && !(weapon->wsfl.zoom && weapon->GetZoomGui() ) ) { Present(); } else { FreeModelDef(); } UpdateSound(); } /* ================ rvViewWeapon::WriteToSnapshot ================ */ void rvViewWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const { } /* ================ rvViewWeapon::ReadFromSnapshot ================ */ void rvViewWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) { } /* ================ rvViewWeapon::ClientStale ================ */ bool rvViewWeapon::ClientStale ( void ) { StopSound( SND_CHANNEL_ANY, false ); if ( weapon ) { weapon->ClientStale( ); } idEntity::ClientStale( ); return false; } /* ================ rvViewWeapon::ClientReceiveEvent ================ */ bool rvViewWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { if ( idEntity::ClientReceiveEvent( event, time, msg ) ) { return true; } if ( weapon ) { return weapon->ClientReceiveEvent ( event, time, msg ); } return false; } /*********************************************************************** Script events ***********************************************************************/ /* ===================== rvViewWeapon::Event_CallFunction ===================== */ void rvViewWeapon::Event_CallFunction( const char *funcname ) { if ( weapon ) { stateParms_t parms = {0}; if ( weapon->ProcessState ( funcname, parms ) == SRESULT_ERROR ) { gameLocal.Error ( "Unknown function '%s' on entity '%s'", funcname, GetName() ); } } } /* ================ rvViewWeapon::SetSkin ================ */ void rvViewWeapon::SetSkin( const char *skinname ) { const idDeclSkin *skinDecl; if ( !skinname || !skinname[ 0 ] ) { skinDecl = NULL; } else { skinDecl = declManager->FindSkin( skinname ); } renderEntity.customSkin = skinDecl; UpdateVisuals(); // Set the skin on the world model as well if ( weapon->GetWorldModel() ) { weapon->GetWorldModel()->SetSkin( skinDecl ); } } void rvViewWeapon::SetSkin( const idDeclSkin* skin ) { renderEntity.customSkin = skin; UpdateVisuals(); if( weapon && weapon->GetWorldModel() ) { weapon->GetWorldModel()->SetSkin( skin ); } } /* ================ rvViewWeapon::GetPosition ================ */ void rvViewWeapon::GetPosition( idVec3& origin, idMat3& axis ) const { origin = GetPhysics()->GetOrigin(); axis = GetPhysics()->GetAxis(); } void rvViewWeapon::SetOverlayShader( const idMaterial* material ) { renderEntity.overlayShader = material; } /*********************************************************************** rvWeapon ***********************************************************************/ CLASS_DECLARATION( idClass, rvWeapon ) END_CLASS /* ================ rvWeapon::rvWeapon ================ */ rvWeapon::rvWeapon ( void ) { viewModel = NULL; worldModel = NULL; weaponDef = NULL; #ifdef _XENON aimAssistFOV = 10.0f; #endif memset ( &animDoneTime, 0, sizeof(animDoneTime) ); memset ( &wsfl, 0, sizeof(wsfl) ); memset ( &wfl, 0, sizeof(wfl) ); hitscanAttackDef = -1; forceGUIReload = false; } /* ================ rvWeapon::~rvWeapon ================ */ rvWeapon::~rvWeapon( void ) { int i; // Free all current light defs for ( i = 0; i < WPLIGHT_MAX; i ++ ) { FreeLight ( i ); } // Disassociate with the view model if ( viewModel ) { viewModel->weapon = NULL; } } /* ================ rvWeapon::Init ================ */ void rvWeapon::Init( idPlayer* _owner, const idDeclEntityDef* def, int _weaponIndex, bool _isStrogg ) { int i; viewModel = _owner->GetWeaponViewModel( ); worldModel = _owner->GetWeaponWorldModel( ); weaponDef = def; owner = _owner; scriptObject = &viewModel->scriptObject; weaponIndex = _weaponIndex; mods = owner->inventory.weaponMods[ weaponIndex ]; isStrogg = _isStrogg; spawnArgs = weaponDef->dict; #ifdef _XENON aimAssistFOV = spawnArgs.GetFloat( "aimAssistFOV", "10.0f" ); #endif // Apply the mod dictionaries for ( i = 0; i < MAX_WEAPONMODS; i ++ ) { const idDict* modDict; if ( !(mods & (1<weapon = this; } /* ================ rvWeapon::FindViewModelPositionStyle ================ */ void rvWeapon::FindViewModelPositionStyle( idVec3& viewOffset, idAngles& viewAngles ) const { int viewStyle = g_gunViewStyle.GetInteger(); const char* styleDefName = spawnArgs.GetString( va("def_viewStyle%d", viewStyle) ); const idDict* styleDef = gameLocal.FindEntityDefDict( styleDefName, false ); if( !styleDef ) { styleDefName = spawnArgs.GetString( "def_viewStyle" ); styleDef = gameLocal.FindEntityDefDict( styleDefName, false ); } assert( styleDef ); viewAngles = styleDef->GetAngles( "viewangles" ); viewOffset = styleDef->GetVector( "viewoffset" ); } /* ================ rvWeapon::Spawn ================ */ void rvWeapon::Spawn ( void ) { memset ( &wsfl, 0, sizeof(wsfl) ); memset ( &wfl, 0, sizeof(wfl) ); // RAVEN BEGIN // nrausch: #if defined(_XENON) aimAssistFOV = spawnArgs.GetFloat( "aimAssistFOV", "10.0f" ); #endif // RAVEN END // Initialize variables projectileEnt = NULL; kick_endtime = 0; hideStart = 0.0f; hideEnd = 0.0f; hideOffset = 0.0f; status = WP_HOLSTERED; lastAttack = 0; clipPredictTime = 0; muzzleAxis.Identity(); muzzleOrigin.Zero(); pushVelocity.Zero(); playerViewAxis.Identity(); playerViewOrigin.Zero(); viewModelAxis.Identity(); viewModelOrigin.Zero(); // View viewModelForeshorten = spawnArgs.GetFloat ( "foreshorten", "1" ); FindViewModelPositionStyle( viewModelOffset, viewModelAngles ); // Offsets weaponAngleOffsetAverages = spawnArgs.GetInt( "weaponAngleOffsetAverages", "10" ); weaponAngleOffsetScale = spawnArgs.GetFloat( "weaponAngleOffsetScale", "0.25" ); weaponAngleOffsetMax = spawnArgs.GetFloat( "weaponAngleOffsetMax", "10" ); weaponOffsetTime = spawnArgs.GetFloat( "weaponOffsetTime", "400" ); weaponOffsetScale = spawnArgs.GetFloat( "weaponOffsetScale", "0.005" ); fireRate = SEC2MS ( spawnArgs.GetFloat ( "fireRate" ) ); altFireRate = SEC2MS ( spawnArgs.GetFloat ( "altFireRate" ) ); if( altFireRate == 0 ) { altFireRate = fireRate; } spread = (gameLocal.IsMultiplayer()&&spawnArgs.FindKey("spread_mp"))?spawnArgs.GetFloat ( "spread_mp" ):spawnArgs.GetFloat ( "spread" ); nextAttackTime = 0; // Zoom zoomFov = spawnArgs.GetInt( "zoomFov", "-1" ); zoomGui = uiManager->FindGui ( spawnArgs.GetString ( "gui_zoom", "" ), true ); zoomTime = spawnArgs.GetFloat ( "zoomTime", ".15" ); wfl.zoomHideCrosshair = spawnArgs.GetBool ( "zoomHideCrosshair", "1" ); // Attack related values muzzle_kick_time = SEC2MS( spawnArgs.GetFloat( "muzzle_kick_time" ) ); muzzle_kick_maxtime = SEC2MS( spawnArgs.GetFloat( "muzzle_kick_maxtime" ) ); muzzle_kick_angles = spawnArgs.GetAngles( "muzzle_kick_angles" ); muzzle_kick_offset = spawnArgs.GetVector( "muzzle_kick_offset" ); // General weapon properties wfl.silent_fire = spawnArgs.GetBool( "silent_fire" ); wfl.hasWindupAnim = spawnArgs.GetBool( "has_windup", "0" ); icon = spawnArgs.GetString( "mtr_icon" ); hideTime = SEC2MS( weaponDef->dict.GetFloat( "hide_time", "0.3" ) ); hideDistance = weaponDef->dict.GetFloat( "hide_distance", "-15" ); hideStartTime = gameLocal.time - hideTime; muzzleOffset = weaponDef->dict.GetFloat ( "muzzleOffset", "14" ); // Ammo clipSize = spawnArgs.GetInt( "clipSize" ); ammoRequired = spawnArgs.GetInt( "ammoRequired" ); lowAmmo = spawnArgs.GetInt( "lowAmmo" ); ammoType = GetAmmoIndexForName( spawnArgs.GetString( "ammoType" ) ); maxAmmo = owner->inventory.MaxAmmoForAmmoClass ( owner, GetAmmoNameForIndex ( ammoType ) ); if ( ( ammoType < 0 ) || ( ammoType >= MAX_AMMO ) ) { gameLocal.Warning( "Unknown ammotype for class '%s'", this->GetClassname ( ) ); } // If the weapon has a clip, then fill it up ammoClip = owner->inventory.clip[weaponIndex]; if ( ( ammoClip < 0 ) || ( ammoClip > clipSize ) ) { // first time using this weapon so have it fully loaded to start ammoClip = clipSize; int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired ); if ( ammoClip > ammoAvail ) { ammoClip = ammoAvail; } } // Complex initializations Initialize InitDefs( ); InitWorldModel( ); InitViewModel( ); // Requires the view model so must be done after it InitLights( ); viewModel->PostGUIEvent( "weapon_init" ); viewModel->PostGUIEvent( "weapon_ammo" ); if ( ammoClip == 0 && AmmoAvailable() == 0 ) { viewModel->PostGUIEvent( "weapon_noammo" ); } stateThread.SetName( va("%s_%s_%s", owner->GetName(), viewModel->GetName ( ), spawnArgs.GetString("classname") ) ); stateThread.SetOwner( this ); forceGUIReload = true; } /* ================ rvWeapon::InitViewModel ================ */ void rvWeapon::InitViewModel( void ) { const char* guiName; const char* temp; int i; const idKeyValue* kv; // Reset view model to clean state viewModel->Clear ( ); // Make sure the sound handle is initted viewModel->refSound.referenceSoundHandle = -1; // Intialize the weapon guis if ( spawnArgs.GetString ( "gui", "", &guiName ) ) { int g = 0; do { viewModel->GetRenderEntity()->gui[g++] = uiManager->FindGui ( guiName, true, false, true ); guiName = spawnArgs.GetString ( va("gui%d", g + 1 ) ); } while ( *guiName && viewModel->GetRenderEntity()->gui[g-1] ); } // Set the view models spawn args viewModel->spawnArgs = weaponDef->dict; // Set the model for the view model if ( isStrogg ) { temp = spawnArgs.GetString ( "model_view_strogg", spawnArgs.GetString ( "model_view" ) ); } else { temp = spawnArgs.GetString ( "model_view" ); } viewModel->SetModel( temp, mods ); // Hide surfaces for ( kv = spawnArgs.MatchPrefix ( "hidesurface", NULL ); kv; kv = spawnArgs.MatchPrefix ( "hidesurface", kv ) ) { viewModel->ProcessEvent ( &EV_HideSurface, kv->GetValue() ); } // Show and Hide the mods for ( i = 0; i < MAX_WEAPONMODS; i ++ ) { const idDict* modDict = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( va("def_mod%d", i+1) ), false ); if ( !modDict ) { continue; } // Hide any show surfaces for mods that arent on if ( !(mods & (1<MatchPrefix ( "mod_showsurface", NULL ); kv; kv = modDict->MatchPrefix ( "mod_showsurface", kv ) ) { viewModel->ProcessEvent ( &EV_HideSurface, kv->GetValue() ); // NOTE: HIDING them because we don't have this mod yet } } else { for ( kv = modDict->MatchPrefix ( "mod_hidesurface", NULL ); kv; kv = modDict->MatchPrefix ( "mod_hidesurface", kv ) ) { viewModel->ProcessEvent ( &EV_HideSurface, kv->GetValue() ); } } } // find some joints in the model for locating effects viewAnimator = viewModel->GetAnimator ( ); barrelJointView = viewAnimator->GetJointHandle( spawnArgs.GetString ( "joint_view_barrel", "barrel" ) ); flashJointView = viewAnimator->GetJointHandle( spawnArgs.GetString ( "joint_view_flash", "flash" ) ); ejectJointView = viewAnimator->GetJointHandle( spawnArgs.GetString ( "joint_view_eject", "eject" ) ); guiLightJointView = viewAnimator->GetJointHandle( spawnArgs.GetString ( "joint_view_guiLight", "guiLight" ) ); flashlightJointView = viewAnimator->GetJointHandle( spawnArgs.GetString ( "joint_view_flashlight", "flashlight" ) ); // Eject offset spawnArgs.GetVector ( "ejectOffset", "0 0 0", ejectOffset ); // Setup a skin for the view model if ( spawnArgs.GetString ( "skin", "", &temp ) ) { viewModel->GetRenderEntity()->customSkin = declManager->FindSkin ( temp ); } // make sure we have the correct skin viewModel->UpdateSkin(); } /* ================ rvWeapon::InitLights ================ */ void rvWeapon::InitLights ( void ) { const char* shader; idVec4 color; renderLight_t* light; memset ( lights, 0, sizeof(lights) ); memset ( lightHandles, -1, sizeof(lightHandles) ); // setup gui light light = &lights[WPLIGHT_GUI]; shader = spawnArgs.GetString( "mtr_guiLightShader", "" ); if ( shader && *shader && viewModel->GetRenderEntity()->gui[0] ) { light->shader = declManager->FindMaterial( shader, false ); light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = spawnArgs.GetFloat("glightRadius", "3" ); color = viewModel->GetRenderEntity()->gui[0]->GetLightColor ( ); light->shaderParms[ SHADERPARM_RED ] = color[0] * color[3]; light->shaderParms[ SHADERPARM_GREEN ] = color[1] * color[3]; light->shaderParms[ SHADERPARM_BLUE ] = color[2] * color[3]; light->shaderParms[ SHADERPARM_ALPHA ] = 1.0f; light->pointLight = true; // RAVEN BEGIN // dluetscher: added detail levels to render lights light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL; // dluetscher: changed lights to no shadow for performance reasons light->noShadows = true; // RAVEN END light->lightId = WPLIGHT_GUI * 100 + owner->entityNumber; light->allowLightInViewID = owner->entityNumber+1; spawnArgs.GetVector ( "glightOffset", "0 0 0", guiLightOffset ); } // Muzzle flash light = &lights[WPLIGHT_MUZZLEFLASH]; shader = spawnArgs.GetString( "mtr_flashShader", "muzzleflash" ); if ( shader && *shader ) { light->shader = declManager->FindMaterial( shader, false ); spawnArgs.GetVec4( "flashColor", "0 0 0 0", color ); light->shaderParms[ SHADERPARM_RED ] = color[0]; light->shaderParms[ SHADERPARM_GREEN ] = color[1]; light->shaderParms[ SHADERPARM_BLUE ] = color[2]; light->shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f; light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = (float)spawnArgs.GetInt( "flashRadius" ); // RAVEN BEGIN // dluetscher: added detail levels to render lights light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL; // dluetscher: changed lights to no shadow for performance reasons light->noShadows = true; // RAVEN END light->pointLight = spawnArgs.GetBool( "flashPointLight", "1" ); if ( !light->pointLight ) { light->target = spawnArgs.GetVector ( "flashTarget" ); light->up = spawnArgs.GetVector ( "flashUp" ); light->right = spawnArgs.GetVector ( "flashRight" ); light->end = light->target; } light->lightId = WPLIGHT_MUZZLEFLASH * 100 + owner->entityNumber; light->allowLightInViewID = owner->entityNumber+1; muzzleFlashTime = SEC2MS( spawnArgs.GetFloat( "flashTime", "0.25" ) ); muzzleFlashEnd = 0; spawnArgs.GetVector ( "flashViewOffset", "0 0 0", muzzleFlashViewOffset ); } // the world muzzle flash is the same, just positioned differently lights[WPLIGHT_MUZZLEFLASH_WORLD] = lights[WPLIGHT_MUZZLEFLASH]; light = &lights[WPLIGHT_MUZZLEFLASH_WORLD]; light->suppressLightInViewID = owner->entityNumber+1; light->allowLightInViewID = 0; light->lightId = WPLIGHT_MUZZLEFLASH_WORLD * 100 + owner->entityNumber; // flashlight light = &lights[WPLIGHT_FLASHLIGHT]; shader = spawnArgs.GetString( "mtr_flashlightShader", "lights/muzzleflash" ); if ( shader && *shader ) { light->shader = declManager->FindMaterial( shader, false ); spawnArgs.GetVec4( "flashlightColor", "0 0 0 0", color ); light->shaderParms[ SHADERPARM_RED ] = color[0]; light->shaderParms[ SHADERPARM_GREEN ] = color[1]; light->shaderParms[ SHADERPARM_BLUE ] = color[2]; light->shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f; light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = (float)spawnArgs.GetInt( "flashlightRadius" ); // RAVEN BEGIN // dluetscher: added detail levels to render lights light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL; // dluetscher: changed lights to no shadow for performance reasons light->noShadows = cvarSystem->GetCVarInteger("com_machineSpec") < 3; // RAVEN END light->pointLight = spawnArgs.GetBool( "flashlightPointLight", "1" ); if ( !light->pointLight ) { light->target = spawnArgs.GetVector( "flashlightTarget" ); light->up = spawnArgs.GetVector( "flashlightUp" ); light->right = spawnArgs.GetVector( "flashlightRight" ); light->end = light->target; } light->allowLightInViewID = owner->entityNumber+1; light->lightId = WPLIGHT_FLASHLIGHT * 100 + owner->entityNumber; spawnArgs.GetVector ( "flashlightViewOffset", "0 0 0", flashlightViewOffset ); } // the world muzzle flashlight is the same, just positioned differently lights[WPLIGHT_FLASHLIGHT_WORLD] = lights[WPLIGHT_FLASHLIGHT]; light = &lights[WPLIGHT_FLASHLIGHT_WORLD]; light->suppressLightInViewID = owner->entityNumber+1; light->allowLightInViewID = 0; light->lightId = WPLIGHT_FLASHLIGHT_WORLD * 100 + owner->entityNumber; } /* ================ rvWeapon::InitDefs ================ */ void rvWeapon::InitDefs( void ) { const char* name; const idDeclEntityDef* def; const char* spawnclass; idTypeInfo* cls; // get the projectile attackDict.Clear(); // Projectile if ( spawnArgs.GetString( "def_projectile", "", &name ) && *name ) { def = gameLocal.FindEntityDef( name, false ); if ( !def ) { gameLocal.Warning( "Unknown projectile '%s' for weapon '%s'", name, weaponDef->GetName() ); } else { spawnclass = def->dict.GetString( "spawnclass" ); cls = idClass::GetClass( spawnclass ); if ( !cls || !cls->IsType( idProjectile::GetClassType() ) ) { gameLocal.Warning( "Invalid spawnclass '%s' for projectile '%s' (used by weapon '%s')", spawnclass, name, weaponDef->GetName ( ) ); } else { attackDict = def->dict; } } } else if ( spawnArgs.GetString( "def_hitscan", "", &name ) && *name ) { def = gameLocal.FindEntityDef( name, false ); if ( !def ) { gameLocal.Warning( "Unknown hitscan '%s' for weapon '%s'", name, weaponDef->GetName ( ) ); } else { attackDict = def->dict; hitscanAttackDef = def->Index(); } wfl.attackHitscan = true; } // Alternate projectile attackAltDict.Clear (); if ( spawnArgs.GetString( "def_altprojectile", "", &name ) && *name ) { def = gameLocal.FindEntityDef( name, false ); if ( !def ) { gameLocal.Warning( "Unknown alt projectile '%s' for weapon '%s'", name, weaponDef->GetName() ); } else { spawnclass = def->dict.GetString( "spawnclass" ); cls = idClass::GetClass( spawnclass ); if ( !cls || !cls->IsType( idProjectile::GetClassType() ) ) { gameLocal.Warning( "Invalid spawnclass '%s' for alt projectile '%s' (used by weapon '%s')", spawnclass, name, weaponDef->GetName ( ) ); } else { attackAltDict = def->dict; } } } else if ( spawnArgs.GetString( "def_althitscan", "", &name ) && *name ) { def = gameLocal.FindEntityDef( name, false ); if ( !def ) { gameLocal.Warning( "Unknown hitscan '%s' for weapon '%s'", name, weaponDef->GetName ( ) ); } else { attackAltDict = def->dict; } wfl.attackAltHitscan = true; } // get the melee damage def meleeDistance = spawnArgs.GetFloat( "melee_distance" ); if ( spawnArgs.GetString( "def_melee", "", &name ) && *name ) { meleeDef = gameLocal.FindEntityDef( name, false ); if ( !meleeDef ) { gameLocal.Error( "Unknown melee '%s' for weapon '%s'", name, weaponDef->GetName() ); } } else { meleeDef = NULL; } // get the brass def brassDict.Clear(); if ( spawnArgs.GetString( "def_ejectBrass", "", &name ) && *name ) { def = gameLocal.FindEntityDef( name, false ); if ( !def ) { gameLocal.Warning( "Unknown brass def '%s' for weapon '%s'", name, weaponDef->GetName() ); } else { brassDict = def->dict; // force any brass to spawn as client moveable brassDict.Set( "spawnclass", "rvClientMoveable" ); } } } /* ================ rvWeapon::Think ================ */ void rvWeapon::Think ( void ) { // Cache the player origin and axis playerViewOrigin = owner->firstPersonViewOrigin; playerViewAxis = owner->firstPersonViewAxis; // calculate weapon position based on player movement bobbing owner->CalculateViewWeaponPos( viewModelOrigin, viewModelAxis ); // hide offset is for dropping the gun when approaching a GUI or NPC // This is simpler to manage than doing the weapon put-away animation if ( gameLocal.time - hideStartTime < hideTime ) { float frac = ( float )( gameLocal.time - hideStartTime ) / ( float )hideTime; if ( hideStart < hideEnd ) { frac = 1.0f - frac; frac = 1.0f - frac * frac; } else { frac = frac * frac; } hideOffset = hideStart + ( hideEnd - hideStart ) * frac; } else { hideOffset = hideEnd; } viewModelOrigin += hideOffset * viewModelAxis[ 2 ]; // kick up based on repeat firing MuzzleRise( viewModelOrigin, viewModelAxis ); if ( viewModel ) { // set the physics position and orientation viewModel->GetPhysics()->SetOrigin( viewModelOrigin ); viewModel->GetPhysics()->SetAxis( viewModelAxis ); viewModel->UpdateVisuals(); } else { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); } // Update the zoom variable before updating the script wsfl.zoom = owner->IsZoomed( ); // Only update the state loop on new frames if ( gameLocal.isNewFrame ) { stateThread.Execute( ); } if ( viewModel ) { viewModel->UpdateAnimation( ); } // Clear reload and flashlight flags wsfl.reload = false; wsfl.flashlight = false; // deal with the third-person visible world model // don't show shadows of the world model in first person if ( worldModel && worldModel->GetRenderEntity() ) { // always show your own weapon if( owner->entityNumber == gameLocal.localClientNum ) { worldModel->GetRenderEntity()->suppressLOD = 1; } else { worldModel->GetRenderEntity()->suppressLOD = 0; } if ( gameLocal.IsMultiplayer() && g_skipPlayerShadowsMP.GetBool() ) { // Disable all weapon shadows for the local client worldModel->GetRenderEntity()->suppressShadowInViewID = gameLocal.localClientNum+1; worldModel->GetRenderEntity()->suppressShadowInLightID = WPLIGHT_MUZZLEFLASH * 100 + owner->entityNumber; } else if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() ) { // Show all weapon shadows worldModel->GetRenderEntity()->suppressShadowInViewID = 0; } else { // Only show weapon shadows for other clients worldModel->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber+1; worldModel->GetRenderEntity()->suppressShadowInLightID = WPLIGHT_MUZZLEFLASH * 100 + owner->entityNumber; } } UpdateGUI(); // Update lights UpdateFlashlight ( ); UpdateMuzzleFlash ( ); // update the gui light renderLight_t& light = lights[WPLIGHT_GUI]; if ( light.lightRadius[0] && guiLightJointView != INVALID_JOINT ) { if ( viewModel ) { idVec4 color = viewModel->GetRenderEntity()->gui[0]->GetLightColor ( ); light.shaderParms[ SHADERPARM_RED ] = color[0] * color[3]; light.shaderParms[ SHADERPARM_GREEN ] = color[1] * color[3]; light.shaderParms[ SHADERPARM_BLUE ] = color[2] * color[3]; GetGlobalJointTransform( true, guiLightJointView, light.origin, light.axis, guiLightOffset ); UpdateLight ( WPLIGHT_GUI ); } else { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); } } // Alert Monsters if the flashlight is one or a muzzle flash is active? if ( !gameLocal.isMultiplayer ) { if ( !owner->fl.notarget && (lightHandles[WPLIGHT_MUZZLEFLASH] != -1 || lightHandles[WPLIGHT_FLASHLIGHT] != -1 ) ) { AlertMonsters ( ); } } } /* ================ rvWeapon::InitWorldModel ================ */ void rvWeapon::InitWorldModel( void ) { idEntity *ent; ent = worldModel; if ( !ent ) { gameLocal.Warning ( "InitWorldModel failed due to missing entity" ); return; } const char *model = spawnArgs.GetString( "model_world" ); const char *attach = spawnArgs.GetString( "joint_attach" ); if ( model[0] && attach[0] ) { ent->Show(); ent->SetModel( model ); 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 = WPLIGHT_MUZZLEFLASH * 100 + owner->entityNumber; } } else { ent->SetModel( "" ); ent->Hide(); } // the renderEntity is reused, so the relevant fields (except this one) appear to be correctly reinitialized worldModel->GetRenderEntity()->suppressSurfaceMask = 0; // Cache the world joints worldAnimator = ent->GetAnimator ( ); flashJointWorld = worldAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_world_flash", "flash" ) ); flashlightJointWorld = worldAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_world_flashlight", "flashlight" ) ); ejectJointWorld = worldAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_world_eject", "eject" ) ); } /* ================ rvWeapon::SetState ================ */ void rvWeapon::SetState( const char *statename, int blendFrames ) { stateThread.SetState( statename, blendFrames ); } /* ================ rvWeapon::PostState ================ */ void rvWeapon::PostState( const char* statename, int blendFrames ) { stateThread.PostState( statename, blendFrames ); } /* ===================== rvWeapon::ExecuteState ===================== */ void rvWeapon::ExecuteState ( const char* statename ) { SetState ( statename, 0 ); stateThread.Execute ( ); } /* ================ rvWeapon::UpdateLight ================ */ void rvWeapon::UpdateLight ( int lightID ) { if ( lightHandles[lightID] == -1 ) { lightHandles[lightID] = gameRenderWorld->AddLightDef ( &lights[lightID] ); } else { gameRenderWorld->UpdateLightDef( lightHandles[lightID], &lights[lightID] ); } } /* ================ rvWeapon::FreeLight ================ */ void rvWeapon::FreeLight ( int lightID ) { if ( lightHandles[lightID] != -1 ) { gameRenderWorld->FreeLightDef( lightHandles[lightID] ); lightHandles[lightID] = -1; } } /*********************************************************************** Networking ***********************************************************************/ /* ================ rvWeapon::WriteToSnapshot ================ */ void rvWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const { msg.WriteBits( ammoClip, ASYNC_PLAYER_INV_CLIP_BITS ); } /* ================ rvWeapon::ReadFromSnapshot ================ */ void rvWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) { ammoClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS ); } /* ================ rvWeapon::SkipFromSnapshot ================ */ void rvWeapon::SkipFromSnapshot ( const idBitMsgDelta &msg ) { msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS ); } /* ================ rvWeapon::ClientStale ================ */ void rvWeapon::ClientStale( void ) { } /* ================ rvWeapon::ClientReceiveEvent ================ */ bool rvWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { switch( event ) { case EVENT_RELOAD: { if ( gameLocal.time - time < 1000 ) { wsfl.netReload = true; wsfl.netEndReload = false; } return true; } case EVENT_ENDRELOAD: { wsfl.netEndReload = true; return true; } case EVENT_CHANGESKIN: { /* // FIXME: use idGameLocal::ReadDecl int index = msg.ReadLong(); renderEntity.customSkin = ( index != -1 ) ? static_cast( declManager->DeclByIndex( DECL_SKIN, index ) ) : NULL; UpdateVisuals(); if ( worldModel.GetEntity() ) { worldModel.GetEntity()->SetSkin( renderEntity.customSkin ); } */ return true; } } return false; } /*********************************************************************** Save / Load ***********************************************************************/ /* ================ rvWeapon::Save ================ */ void rvWeapon::Save ( idSaveGame *savefile ) const { int i; // Flags savefile->Write ( &wsfl, sizeof( wsfl ) ); savefile->Write ( &wfl, sizeof( wfl ) ); // Write all cached joints savefile->WriteJoint ( barrelJointView ); savefile->WriteJoint ( flashJointView ); savefile->WriteJoint ( ejectJointView ); savefile->WriteJoint ( guiLightJointView ); savefile->WriteJoint ( flashlightJointView ); savefile->WriteJoint ( flashJointWorld ); savefile->WriteJoint ( ejectJointWorld ); savefile->WriteJoint ( flashlightJointWorld ); savefile->WriteInt ( status ); savefile->WriteInt ( lastAttack ); // Hide / Show savefile->WriteInt ( hideTime ); savefile->WriteFloat ( hideDistance ); savefile->WriteInt ( hideStartTime ); savefile->WriteFloat ( hideStart ); savefile->WriteFloat ( hideEnd ); savefile->WriteFloat ( hideOffset ); // Write attack related values savefile->WriteVec3 ( pushVelocity ); 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->WriteVec3 ( muzzleOrigin ); savefile->WriteMat3 ( muzzleAxis ); savefile->WriteFloat ( muzzleOffset ); projectileEnt.Save ( savefile ); savefile->WriteVec3 ( ejectOffset ); // cnicholson: Added unsaved var savefile->WriteInt ( fireRate ); savefile->WriteFloat ( spread ); // savefile->WriteInt ( nextAttackTime ); // cnicholson: This is set to 0 in restore, so don't save it // cnicholson: These 3 idDicts are setup during restore, no need to save them. // TOSAVE: idDict attackAltDict; // TOSAVE: idDict attackDict; // TOSAVE: idDict brassDict; // Defs // TOSAVE: const idDeclEntityDef * meleeDef; // cnicholson: This is setup in restore, so don't save it savefile->WriteFloat ( meleeDistance ); // Zoom savefile->WriteInt ( zoomFov ); savefile->WriteUserInterface ( zoomGui, true ); savefile->WriteFloat ( zoomTime ); // Lights for ( i = 0; i < WPLIGHT_MAX; i ++ ) { savefile->WriteInt( lightHandles[i] ); savefile->WriteRenderLight( lights[i] ); } savefile->WriteVec3 ( guiLightOffset ); savefile->WriteInt ( muzzleFlashEnd ); savefile->WriteInt ( muzzleFlashTime ); savefile->WriteVec3 ( muzzleFlashViewOffset ); savefile->WriteVec3 ( flashlightViewOffset ); savefile->WriteBool ( flashlightOn ); // cnicholson: Added unsaved var savefile->WriteVec3 ( flashlightViewOffset ); // cnicholson: Added unsaved var // Write ammo values savefile->WriteInt ( ammoType ); savefile->WriteInt ( ammoRequired ); savefile->WriteInt ( clipSize ); savefile->WriteInt ( ammoClip ); savefile->WriteInt ( lowAmmo ); savefile->WriteInt ( maxAmmo ); // multiplayer savefile->WriteInt ( clipPredictTime ); // TOSAVE: Save MP value? // View savefile->WriteVec3 ( playerViewOrigin ); savefile->WriteMat3 ( playerViewAxis ); savefile->WriteVec3 ( viewModelOrigin ); savefile->WriteMat3 ( viewModelAxis ); savefile->WriteAngles ( viewModelAngles ); savefile->WriteVec3 ( viewModelOffset ); // cnicholson: Added unsaved var // Offsets savefile->WriteInt ( weaponAngleOffsetAverages ); savefile->WriteFloat ( weaponAngleOffsetScale ); savefile->WriteFloat ( weaponAngleOffsetMax ); savefile->WriteFloat ( weaponOffsetTime ); savefile->WriteFloat ( weaponOffsetScale ); savefile->WriteString ( icon ); savefile->WriteBool ( isStrogg ); // TOSAVE: idDict spawnArgs; // TOSAVE: idEntityPtr viewModel; // cnicholson: Setup in restore, no need to save // TOSAVE: idAnimator* viewAnimator; // TOSAVE: idEntityPtr worldModel; // cnicholson: Setup in restore, no need to save // TOSAVE: idAnimator* worldAnimator; // TOSAVE: const idDeclEntityDef* weaponDef; // TOSAVE: idScriptObject* scriptObject; savefile->WriteObject ( owner ); savefile->WriteInt ( weaponIndex ); // cnicholson: Added unsaved var savefile->WriteInt ( mods ); // cnicholson: Added unsaved var savefile->WriteFloat ( viewModelForeshorten ); stateThread.Save( savefile ); for ( i = 0; i < ANIM_NumAnimChannels; i++ ) { savefile->WriteInt( animDoneTime[i] ); } savefile->WriteInt ( methodOfDeath ); // cnicholson: Added unsaved var } /* ================ rvWeapon::Restore ================ */ void rvWeapon::Restore ( idRestoreGame *savefile ) { int i; const idDeclEntityDef* def; // General savefile->Read ( &wsfl, sizeof( wsfl ) ); savefile->Read ( &wfl, sizeof( wfl ) ); // Read cached joints savefile->ReadJoint ( barrelJointView ); savefile->ReadJoint ( flashJointView ); savefile->ReadJoint ( ejectJointView ); savefile->ReadJoint ( guiLightJointView ); savefile->ReadJoint ( flashlightJointView ); savefile->ReadJoint ( flashJointWorld ); savefile->ReadJoint ( ejectJointWorld ); savefile->ReadJoint ( flashlightJointWorld ); savefile->ReadInt ( (int&)status ); savefile->ReadInt ( lastAttack ); // Hide / Show savefile->ReadInt ( hideTime ); savefile->ReadFloat ( hideDistance ); savefile->ReadInt ( hideStartTime ); savefile->ReadFloat ( hideStart ); savefile->ReadFloat ( hideEnd ); savefile->ReadFloat ( hideOffset ); // Read attack related values savefile->ReadVec3 ( pushVelocity ); 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->ReadVec3 ( muzzleOrigin ); savefile->ReadMat3 ( muzzleAxis ); savefile->ReadFloat ( muzzleOffset ); projectileEnt.Restore ( savefile ); savefile->ReadVec3 ( ejectOffset ); // cnicholson: Added unrestored var savefile->ReadInt ( fireRate ); savefile->ReadFloat ( spread ); nextAttackTime = 0; // Attack Alt Def attackAltDict.Clear( ); wfl.attackAltHitscan = false; def = gameLocal.FindEntityDef( spawnArgs.GetString( "def_altprojectile" ), false ); if ( def ) { attackAltDict = def->dict; } else { def = gameLocal.FindEntityDef( spawnArgs.GetString( "def_althitscan" ), false ); if ( def ) { attackAltDict = def->dict; wfl.attackAltHitscan = true; } } // Attack def attackDict.Clear( ); def = gameLocal.FindEntityDef( spawnArgs.GetString( "def_projectile" ), false ); wfl.attackHitscan = false; if ( def ) { attackDict = def->dict; } else { def = gameLocal.FindEntityDef( spawnArgs.GetString( "def_hitscan" ), false ); if ( def ) { attackDict = def->dict; wfl.attackHitscan = true; } } // Brass Def def = gameLocal.FindEntityDef( spawnArgs.GetString( "def_ejectBrass" ), false ); if ( def ) { brassDict = def->dict; } else { brassDict.Clear(); } // Melee Def meleeDef = gameLocal.FindEntityDef( spawnArgs.GetString( "def_melee" ), false ); savefile->ReadFloat( meleeDistance ); // Zoom savefile->ReadInt ( zoomFov ); savefile->ReadUserInterface ( zoomGui, &spawnArgs ); savefile->ReadFloat ( zoomTime ); // Lights for ( i = 0; i < WPLIGHT_MAX; i ++ ) { savefile->ReadInt ( lightHandles[i] ); savefile->ReadRenderLight( lights[i] ); if ( lightHandles[i] != -1 ) { //get the handle again as it's out of date after a restore! lightHandles[i] = gameRenderWorld->AddLightDef ( &lights[i] ); } } savefile->ReadVec3 ( guiLightOffset ); savefile->ReadInt ( muzzleFlashEnd ); savefile->ReadInt ( muzzleFlashTime ); savefile->ReadVec3 ( muzzleFlashViewOffset ); savefile->ReadVec3 ( flashlightViewOffset ); savefile->ReadBool ( flashlightOn ); // cnicholson: Added unrestored var savefile->ReadVec3 ( flashlightViewOffset ); // cnicholson: Added unrestored var // Read the ammo values savefile->ReadInt ( (int&)ammoType ); savefile->ReadInt ( ammoRequired ); savefile->ReadInt ( clipSize ); savefile->ReadInt ( ammoClip ); savefile->ReadInt ( lowAmmo ); savefile->ReadInt ( maxAmmo ); // multiplayer savefile->ReadInt ( clipPredictTime ); // TORESTORE: Restore MP value? // View savefile->ReadVec3 ( playerViewOrigin ); savefile->ReadMat3 ( playerViewAxis ); savefile->ReadVec3 ( viewModelOrigin ); savefile->ReadMat3 ( viewModelAxis ); savefile->ReadAngles ( viewModelAngles ); savefile->ReadVec3 ( viewModelOffset ); // cnicholson: Added unrestored var // Offsets savefile->ReadInt ( weaponAngleOffsetAverages ); savefile->ReadFloat ( weaponAngleOffsetScale ); savefile->ReadFloat ( weaponAngleOffsetMax ); savefile->ReadFloat ( weaponOffsetTime ); savefile->ReadFloat ( weaponOffsetScale ); savefile->ReadString ( icon ); savefile->ReadBool ( isStrogg ); // TORESTORE: idDict spawnArgs; // TORESTORE: idAnimator* viewAnimator; // TORESTORE: idAnimator* worldAnimator; // TORESTORE: const idDeclEntityDef* weaponDef; // TORESTORE: idScriptObject* scriptObject; // Entities savefile->ReadObject( reinterpret_cast( owner ) ); viewModel = owner->GetWeaponViewModel ( ); worldModel = owner->GetWeaponWorldModel ( ); savefile->ReadInt ( weaponIndex ); // cnicholson: Added unrestored var savefile->ReadInt ( mods ); // cnicholson: Added unrestored var savefile->ReadFloat ( viewModelForeshorten ); stateThread.Restore( savefile, this ); for ( i = 0; i < ANIM_NumAnimChannels; i++ ) { savefile->ReadInt( animDoneTime[i] ); } savefile->ReadInt ( methodOfDeath ); // cnicholson: Added unrestored var #ifdef _XENON aimAssistFOV = spawnArgs.GetFloat( "aimAssistFOV", "10.0f" ); #endif } /*********************************************************************** State control/player interface ***********************************************************************/ /* ================ rvWeapon::Hide ================ */ void rvWeapon::Hide( void ) { muzzleFlashEnd = 0; if ( viewModel ) { viewModel->Hide(); } if ( worldModel ) { worldModel->Hide ( ); } // Stop flashlight and gui lights FreeLight ( WPLIGHT_GUI ); FreeLight ( WPLIGHT_FLASHLIGHT ); FreeLight ( WPLIGHT_FLASHLIGHT_WORLD ); } /* ================ rvWeapon::Show ================ */ void rvWeapon::Show ( void ) { if ( viewModel ) { viewModel->Show(); } if ( worldModel ) { worldModel->Show(); } } /* ================ rvWeapon::IsHidden ================ */ bool rvWeapon::IsHidden( void ) const { return !viewModel || viewModel->IsHidden(); } /* ================ rvWeapon::HideWorldModel ================ */ void rvWeapon::HideWorldModel ( void ) { if ( worldModel ) { worldModel->Hide(); } } /* ================ rvWeapon::ShowWorldModel ================ */ void rvWeapon::ShowWorldModel ( void ) { if ( worldModel ) { worldModel->Show(); } } /* ================ rvWeapon::LowerWeapon ================ */ void rvWeapon::LowerWeapon( void ) { if ( !wfl.hide ) { hideStart = 0.0f; hideEnd = hideDistance; if ( gameLocal.time - hideStartTime < hideTime ) { hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) ); } else { hideStartTime = gameLocal.time; } wfl.hide = true; } } /* ================ rvWeapon::RaiseWeapon ================ */ void rvWeapon::RaiseWeapon( void ) { if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->Show(); if ( forceGUIReload ) { forceGUIReload = false; int ammo = AmmoInClip(); for ( int g = 0; g < MAX_RENDERENTITY_GUI && viewModel->GetRenderEntity()->gui[g]; g ++ ) { idUserInterface* gui = viewModel->GetRenderEntity()->gui[g]; if ( gui ) { gui->SetStateInt ( "player_ammo", ammo ); if ( ClipSize ( ) ) { gui->SetStateFloat ( "player_ammopct", (float)ammo / (float)ClipSize() ); gui->SetStateInt ( "player_clip_size", ClipSize() ); } else { gui->SetStateFloat ( "player_ammopct", (float)ammo / (float)maxAmmo ); gui->SetStateInt ( "player_clip_size", maxAmmo ); } gui->SetStateInt ( "player_cachedammo", ammo ); gui->HandleNamedEvent ( "weapon_ammo" ); } } } if ( wfl.hide ) { hideStart = hideDistance; hideEnd = 0.0f; if ( gameLocal.time - hideStartTime < hideTime ) { hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) ); } else { hideStartTime = gameLocal.time; } wfl.hide = false; } } /* ================ rvWeapon::PutAway ================ */ void rvWeapon::PutAway( void ) { wfl.hasBloodSplat = false; wsfl.lowerWeapon = true; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->PostGUIEvent ( "weapon_lower" ); } /* ================ rvWeapon::Raise ================ */ void rvWeapon::Raise( void ) { wsfl.raiseWeapon = true; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->PostGUIEvent ( "weapon_raise" ); } /* ================ rvWeapon::Flashlight ================ */ void rvWeapon::Flashlight ( void ) { wsfl.flashlight = true; } /* ================ rvWeapon::SetPushVelocity ================ */ void rvWeapon::SetPushVelocity( const idVec3& _pushVelocity ) { pushVelocity = _pushVelocity; } /* ================ rvWeapon::Reload NOTE: this is only for impulse-triggered reload, auto reload is scripted ================ */ void rvWeapon::Reload( void ) { if ( clipSize ) { wsfl.reload = true; } } /* ================ rvWeapon::CancelReload ================ */ void rvWeapon::CancelReload( void ) { wsfl.attack = true; } /* ================ rvWeapon::AutoReload ================ */ bool rvWeapon::AutoReload ( void ) { assert( owner ); // on a network client, never predict reloads of other clients. wait for the server if ( gameLocal.isClient ) { return false; } return gameLocal.userInfo[ owner->entityNumber ].GetBool( "ui_autoReload" ); } /* ================ rvWeapon::NetReload ================ */ void rvWeapon::NetReload ( void ) { assert( owner ); if ( gameLocal.isServer ) { if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->ServerSendEvent( EVENT_RELOAD, NULL, false, -1 ); } } /* =============== rvWeapon::NetEndReload =============== */ void rvWeapon::NetEndReload ( void ) { assert( owner ); if ( gameLocal.isServer ) { if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->ServerSendEvent( EVENT_ENDRELOAD, NULL, false, -1 ); } } /* ================ rvWeapon::SetStatus ================ */ void rvWeapon::SetStatus ( weaponStatus_t _status ) { status = _status; switch ( status ) { case WP_READY: wsfl.raiseWeapon = false; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); break; } viewModel->PostGUIEvent ( "weapon_ready" ); break; case WP_OUTOFAMMO: wsfl.raiseWeapon = false; break; case WP_RELOAD: if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); break; } viewModel->PostGUIEvent ( "weapon_reload" ); break; case WP_HOLSTERED: case WP_RISING: wsfl.lowerWeapon = false; owner->WeaponRisingCallback(); break; case WP_LOWERING: wsfl.raiseWeapon = false; owner->WeaponLoweringCallback(); break; } } /* ================ rvWeapon::OwnerDied ================ */ void rvWeapon::OwnerDied( void ) { CleanupWeapon(); ExecuteState( "OwnerDied" ); if ( viewModel ) { viewModel->StopSound( SCHANNEL_ANY, false ); viewModel->StopAllEffects( ); viewModel->Hide(); } if ( worldModel ) { worldModel->Hide(); } } /* ================ rvWeapon::BeginAttack ================ */ void rvWeapon::BeginAttack( void ) { wsfl.attack = true; if ( status != WP_OUTOFAMMO ) { lastAttack = gameLocal.time; } } /* ================ rvWeapon::EndAttack ================ */ void rvWeapon::EndAttack( void ) { wsfl.attack = false; } /* ================ rvWeapon::isReady ================ */ bool rvWeapon::IsReady( void ) const { return !wfl.hide && !(gameLocal.time - hideStartTime < hideTime) && (viewModel && !viewModel->IsHidden()) && ( ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) ); } /* ================ rvWeapon::IsReloading ================ */ bool rvWeapon::IsReloading( void ) const { return ( status == WP_RELOAD ); } /* ================ rvWeapon::IsHolstered ================ */ bool rvWeapon::IsHolstered( void ) const { return ( status == WP_HOLSTERED ); } /* ================ rvWeapon::ShowCrosshair ================ */ bool rvWeapon::ShowCrosshair( void ) const { if ( owner->IsZoomed ( ) && zoomGui && wfl.zoomHideCrosshair ) { return false; } return !( status == WP_HOLSTERED ); } /* ===================== rvWeapon::CanDrop ===================== */ bool rvWeapon::CanDrop( void ) const { const char *classname = spawnArgs.GetString( "def_dropItem" ); if ( !classname[ 0 ] ) { return false; } return true; } /* ===================== rvViewWeapon::CanZoom ===================== */ bool rvWeapon::CanZoom( void ) const { #ifdef _XENON // apparently a xenon specific bug in medlabs. return zoomFov != -1 && !IsHidden(); #else return zoomFov != -1; #endif } /*********************************************************************** Visual presentation ***********************************************************************/ /* ================ rvWeapon::MuzzleRise ================ */ void rvWeapon::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; } /* ================ rvWeapon::UpdateFlashPosition ================ */ void rvWeapon::UpdateMuzzleFlash ( void ) { // remove the muzzle flash light when it's done if ( gameLocal.time >= muzzleFlashEnd || !gameLocal.GetLocalPlayer() || !owner || gameLocal.GetLocalPlayer()->GetInstance() != owner->GetInstance() ) { FreeLight ( WPLIGHT_MUZZLEFLASH ); FreeLight ( WPLIGHT_MUZZLEFLASH_WORLD ); return; } renderLight_t& light = lights[WPLIGHT_MUZZLEFLASH]; renderLight_t& lightWorld = lights[WPLIGHT_MUZZLEFLASH_WORLD]; light.origin = playerViewOrigin + (playerViewAxis * muzzleFlashViewOffset); light.axis = playerViewAxis; // put the world muzzle flash on the end of the joint, no matter what GetGlobalJointTransform( false, flashJointWorld, lightWorld.origin, lightWorld.axis ); UpdateLight ( WPLIGHT_MUZZLEFLASH ); UpdateLight ( WPLIGHT_MUZZLEFLASH_WORLD ); } /* ================ rvWeapon::UpdateFlashlight ================ */ void rvWeapon::UpdateFlashlight ( void ) { // Turn flashlight off? if (! owner->IsFlashlightOn ( ) ) { FreeLight ( WPLIGHT_FLASHLIGHT ); FreeLight ( WPLIGHT_FLASHLIGHT_WORLD ); return; } renderLight_t& light = lights[WPLIGHT_FLASHLIGHT]; renderLight_t& lightWorld = lights[WPLIGHT_FLASHLIGHT_WORLD]; trace_t tr; // the flash has an explicit joint for locating it GetGlobalJointTransform( true, flashlightJointView, light.origin, light.axis, flashlightViewOffset ); // if the desired point is inside or very close to a wall, back it up until it is clear gameLocal.TracePoint( owner, tr, light.origin - playerViewAxis[0] * 8.0f, light.origin, MASK_SHOT_BOUNDINGBOX, owner ); // be at least 8 units away from a solid light.origin = tr.endpos - (tr.fraction < 1.0f ? (playerViewAxis[0] * 8) : vec3_origin); // put the world muzzle flash on the end of the joint, no matter what if ( flashlightJointWorld != INVALID_JOINT ) { GetGlobalJointTransform( false, flashlightJointWorld, lightWorld.origin, lightWorld.axis ); } else { lightWorld.origin = playerViewOrigin + playerViewAxis[0] * 20.0f; lightWorld.axis = playerViewAxis; } UpdateLight ( WPLIGHT_FLASHLIGHT ); UpdateLight ( WPLIGHT_FLASHLIGHT_WORLD ); } /* ================ rvWeapon::MuzzleFlash ================ */ void rvWeapon::MuzzleFlash ( void ) { renderLight_t& light = lights[WPLIGHT_MUZZLEFLASH]; renderLight_t& lightWorld = lights[WPLIGHT_MUZZLEFLASH_WORLD]; if ( !g_muzzleFlash.GetBool() || flashJointView == INVALID_JOINT || !light.lightRadius[0] ) { return; } if ( g_perfTest_weaponNoFX.GetBool() ) { return; } if ( viewModel ) { // these will be different each fire light.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); light.shaderParms[ SHADERPARM_DIVERSITY ] = viewModel->GetRenderEntity()->shaderParms[ SHADERPARM_DIVERSITY ]; light.noShadows = true; lightWorld.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); lightWorld.shaderParms[ SHADERPARM_DIVERSITY ] = viewModel->GetRenderEntity()->shaderParms[ SHADERPARM_DIVERSITY ]; lightWorld.noShadows = true; // the light will be removed at this time muzzleFlashEnd = gameLocal.time + muzzleFlashTime; } else { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); } UpdateMuzzleFlash ( ); } /* ================ rvWeapon::UpdateGUI ================ */ void rvWeapon::UpdateGUI( void ) { idUserInterface* gui; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } gui = viewModel->GetRenderEntity()->gui[0]; if ( !gui || status == WP_HOLSTERED ) { return; } int g; for ( g = 0; g < MAX_RENDERENTITY_GUI && viewModel->GetRenderEntity()->gui[g]; g ++ ) { gui = viewModel->GetRenderEntity()->gui[g]; if ( gameLocal.localClientNum != owner->entityNumber ) { // if updating the hud for a followed client idPlayer *p = gameLocal.GetLocalPlayer(); if ( !p ) { return; } if ( !p->spectating || p->spectator != owner->entityNumber ) { return; } } int ammo = AmmoInClip(); if ( ammo >= 0 ) { // show remaining ammo if ( gui->State().GetInt ( "player_cachedammo", "-1") != ammo ) { gui->SetStateInt ( "player_ammo", ammo ); if ( ClipSize ( ) ) { gui->SetStateFloat ( "player_ammopct", (float)ammo / (float)ClipSize() ); gui->SetStateInt ( "player_clip_size", ClipSize() ); } else { gui->SetStateFloat ( "player_ammopct", (float)ammo / (float)maxAmmo ); gui->SetStateInt ( "player_clip_size", maxAmmo ); } gui->SetStateInt ( "player_cachedammo", ammo ); gui->HandleNamedEvent ( "weapon_ammo" ); } } // viewModel->GetRenderEntity()->gui[g]->SetStateInt ( "player_clip_size", ClipSize() ); } for ( int i = 0; i < viewModel->pendingGUIEvents.Num(); i ++ ) { gui->HandleNamedEvent( viewModel->pendingGUIEvents[i] ); } viewModel->pendingGUIEvents.Clear(); } /* ================ rvWeapon::UpdateCrosshairGUI ================ */ void rvWeapon::UpdateCrosshairGUI( idUserInterface* gui ) const { // RAVEN BEGIN // cnicholson: Added support for universal crosshair // COMMENTED OUT until Custom crosshair GUI is implemented. if ( g_crosshairCustom.GetBool() ) { // If there's a custom crosshair, use it. gui->SetStateString( "crossImage", g_crosshairCustomFile.GetString()); const idMaterial *material = declManager->FindMaterial( g_crosshairCustomFile.GetString() ); if ( material ) { material->SetSort( SS_GUI ); } } else { gui->SetStateString( "crossImage", spawnArgs.GetString( "mtr_crosshair" ) ); const idMaterial *material = declManager->FindMaterial( spawnArgs.GetString( "mtr_crosshair" ) ); if ( material ) { material->SetSort( SS_GUI ); } } // Original Block //gui->SetStateString ( "crossImage", spawnArgs.GetString ( "mtr_crosshair" ) ); // RAVEN END gui->SetStateString( "crossColor", g_crosshairColor.GetString() ); gui->SetStateInt( "crossOffsetX", spawnArgs.GetInt( "crosshairOffsetX", "0" ) ); gui->SetStateInt( "crossOffsetY", spawnArgs.GetInt( "crosshairOffsetY", "0" ) ); gui->StateChanged( gameLocal.time ); } /* ================ rvWeapon::ForeshortenAxis ================ */ idMat3 rvWeapon::ForeshortenAxis( const idMat3& axis ) const { return idMat3( axis[0] * viewModelForeshorten, axis[1], axis[2] ); } /* ================ rvWeapon::GetAngleOffsets ================ */ void rvWeapon::GetAngleOffsets ( int *average, float *scale, float *max ) { *average = weaponAngleOffsetAverages; *scale = weaponAngleOffsetScale; *max = weaponAngleOffsetMax; } /* ================ rvWeapon::GetTimeOffsets ================ */ void rvWeapon::GetTimeOffsets ( float *time, float *scale ) { *time = weaponOffsetTime; *scale = weaponOffsetScale; } /* ================ rvWeapon::GetGlobalJointTransform This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights ================ */ bool rvWeapon::GetGlobalJointTransform ( bool view, const jointHandle_t jointHandle, idVec3 &origin, idMat3 &axis, const idVec3& offset ) { if ( view) { // view model if ( viewModel && viewAnimator->GetJointTransform( jointHandle, gameLocal.time, origin, axis ) ) { origin = offset * axis + origin; origin = origin * ForeshortenAxis(viewModelAxis) + viewModelOrigin; axis = axis * viewModelAxis; return true; } } else { // world model if ( worldModel && worldAnimator->GetJointTransform( jointHandle, gameLocal.time, origin, axis ) ) { origin = offset * axis + origin; origin = worldModel->GetPhysics()->GetOrigin() + origin * worldModel->GetPhysics()->GetAxis(); axis = axis * worldModel->GetPhysics()->GetAxis(); return true; } } origin = viewModelOrigin + offset * viewModelAxis; axis = viewModelAxis; return false; } /*********************************************************************** Ammo ***********************************************************************/ /* ================ rvWeapon::GetAmmoIndexForName ================ */ int rvWeapon::GetAmmoIndexForName( 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 >= MAX_AMMOTYPES ) ) { gameLocal.Error( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, MAX_AMMOTYPES ); } return num; } /* ================ rvWeapon::GetAmmoNameForNum ================ */ const char* rvWeapon::GetAmmoNameForIndex( int index ) { 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", index ); num = ammoDict->GetNumKeyVals(); for( i = 0; i < num; i++ ) { kv = ammoDict->GetKeyVal( i ); if ( kv->GetValue() == text ) { return kv->GetKey(); } } return NULL; } /* ================ rvWeapon::TotalAmmoCount ================ */ int rvWeapon::TotalAmmoCount ( void ) const { return owner->inventory.HasAmmo( ammoType, 1 ); } /* ================ rvWeapon::AmmoAvailable ================ */ int rvWeapon::AmmoAvailable( void ) const { if ( owner ) { return owner->inventory.HasAmmo( ammoType, ammoRequired ); } else { return 0; } } /* ================ rvWeapon::AmmoInClip ================ */ int rvWeapon::AmmoInClip( void ) const { if ( !clipSize ) { return AmmoAvailable(); } return ammoClip; } /* ================ rvWeapon::ResetAmmoClip ================ */ void rvWeapon::ResetAmmoClip( void ) { ammoClip = -1; } /* ================ rvWeapon::GetAmmoType ================ */ int rvWeapon::GetAmmoType( void ) const { return ammoType; } /* ================ rvWeapon::ClipSize ================ */ int rvWeapon::ClipSize( void ) const { return clipSize; } /* ================ rvWeapon::LowAmmo ================ */ int rvWeapon::LowAmmo() const { return lowAmmo; } /* ================ rvWeapon::AmmoRequired ================ */ int rvWeapon::AmmoRequired( void ) const { return ammoRequired; } /* ================ rvWeapon::SetClip ================ */ void rvWeapon::SetClip ( int amount ) { ammoClip = amount; if ( amount < 0 ) { ammoClip = 0; } else if ( amount > clipSize ) { ammoClip = clipSize; } if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->PostGUIEvent ( "weapon_ammo" ); if ( ammoClip == 0 && AmmoAvailable() == 0 ) { viewModel->PostGUIEvent ( "weapon_noammo" ); } } /* ================ rvWeapon::UseAmmo ================ */ void rvWeapon::UseAmmo ( int amount ) { owner->inventory.UseAmmo( ammoType, amount * ammoRequired ); if ( clipSize && ammoRequired ) { ammoClip -= ( amount * ammoRequired ); if ( ammoClip < 0 ) { ammoClip = 0; } } } /* ================ rvWeapon::AddToClip ================ */ void rvWeapon::AddToClip ( int amount ) { int ammoAvail; if ( gameLocal.isClient ) { return; } ammoClip += amount; if ( ammoClip > clipSize ) { ammoClip = clipSize; } ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired ); if ( ammoAvail > 0 && ammoClip > ammoAvail ) { ammoClip = ammoAvail; } if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } viewModel->PostGUIEvent ( "weapon_ammo" ); if ( ammoClip == 0 && AmmoAvailable() == 0 ) { viewModel->PostGUIEvent ( "weapon_noammo" ); } } /*********************************************************************** Attack ***********************************************************************/ /* ================ rvWeapon::Attack ================ */ void rvWeapon::Attack( bool altAttack, int num_attacks, float spread, float fuseOffset, float power ) { idVec3 muzzleOrigin; idMat3 muzzleAxis; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } if ( viewModel->IsHidden() ) { 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; } owner->inventory.UseAmmo( ammoType, ammoRequired ); if ( clipSize && ammoRequired ) { clipPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots ammoClip -= 1; } // wake up nearby monsters if ( !wfl.silent_fire ) { 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 viewModel->SetShaderParm ( SHADERPARM_DIVERSITY, gameLocal.random.CRandomFloat() ); viewModel->SetShaderParm ( SHADERPARM_TIMEOFFSET, -MS2SEC( gameLocal.realClientTime ) ); if ( worldModel.GetEntity() ) { worldModel->SetShaderParm( SHADERPARM_DIVERSITY, viewModel->GetRenderEntity()->shaderParms[ SHADERPARM_DIVERSITY ] ); worldModel->SetShaderParm( SHADERPARM_TIMEOFFSET, viewModel->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] ); } // calculate the muzzle position if ( barrelJointView != INVALID_JOINT && spawnArgs.GetBool( "launchFromBarrel" ) ) { // there is an explicit joint for the muzzle GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis ); } else { // go straight out of the view muzzleOrigin = playerViewOrigin; muzzleAxis = playerViewAxis; muzzleOrigin += playerViewAxis[0] * muzzleOffset; } // 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; } // add the muzzleflash MuzzleFlash(); // quad damage overlays a sound if ( owner->PowerUpActive( POWERUP_QUADDAMAGE ) ) { viewModel->StartSound( "snd_quaddamage", SND_CHANNEL_VOICE, 0, false, NULL ); } // Muzzle flash effect bool muzzleTint = spawnArgs.GetBool( "muzzleTint" ); viewModel->PlayEffect( "fx_muzzleflash", flashJointView, false, vec3_origin, false, EC_IGNORE, muzzleTint ? owner->GetHitscanTint() : vec4_one ); if ( worldModel && flashJointWorld != INVALID_JOINT ) { worldModel->PlayEffect( gameLocal.GetEffect( weaponDef->dict, "fx_muzzleflash_world" ), flashJointWorld, vec3_origin, mat3_identity, false, vec3_origin, false, EC_IGNORE, muzzleTint ? owner->GetHitscanTint() : vec4_one ); } owner->WeaponFireFeedback( &weaponDef->dict ); // Inform the gui of the ammo change viewModel->PostGUIEvent ( "weapon_ammo" ); if ( ammoClip == 0 && AmmoAvailable() == 0 ) { viewModel->PostGUIEvent ( "weapon_noammo" ); } // The attack is either a hitscan or a launched projectile, do that now. if ( !gameLocal.isClient ) { idDict& dict = altAttack ? attackAltDict : attackDict; power *= owner->PowerUpModifier( PMOD_PROJECTILE_DAMAGE ); if ( altAttack ? wfl.attackAltHitscan : wfl.attackHitscan ) { Hitscan( dict, muzzleOrigin, muzzleAxis, num_attacks, spread, power ); } else { LaunchProjectiles( dict, muzzleOrigin, muzzleAxis, num_attacks, spread, fuseOffset, power ); } //asalmon: changed to keep stats even in single player statManager->WeaponFired( owner, weaponIndex, num_attacks ); } } /* ================ rvWeapon::LaunchProjectiles ================ */ void rvWeapon::LaunchProjectiles ( idDict& dict, const idVec3& muzzleOrigin, const idMat3& muzzleAxis, int num_projectiles, float spread, float fuseOffset, float power ) { idProjectile* proj; idEntity* ent; int i; float spreadRad; idVec3 dir; idBounds ownerBounds; if ( gameLocal.isClient ) { return; } // Let the AI know about the new attack if ( !gameLocal.isMultiplayer ) { aiManager.ReactToPlayerAttack ( owner, muzzleOrigin, muzzleAxis[0] ); } ownerBounds = owner->GetPhysics()->GetAbsBounds(); spreadRad = DEG2RAD( spread ); idVec3 dirOffset; idVec3 startOffset; spawnArgs.GetVector( "dirOffset", "0 0 0", dirOffset ); spawnArgs.GetVector( "startOffset", "0 0 0", startOffset ); for( i = 0; i < num_projectiles; i++ ) { float ang; float spin; idVec3 dir; idBounds projBounds; idVec3 muzzle_pos; // Calculate a random launch direction based on the spread ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); //RAVEN BEGIN //asalmon: xbox must use muzzle Axis for aim assistance #ifdef _XBOX dir = muzzleAxis[ 0 ] + muzzleAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - muzzleAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); dir += dirOffset; #else dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); dir += dirOffset; #endif //RAVEN END dir.Normalize(); // If a projectile entity has already been created then use that one, otherwise // spawn a new one based on the given dictionary if ( projectileEnt ) { ent = projectileEnt; ent->Show(); ent->Unbind(); projectileEnt = NULL; } else { dict.SetInt( "instance", owner->GetInstance() ); gameLocal.SpawnEntityDef( dict, &ent, false ); } // Make sure it spawned if ( !ent ) { gameLocal.Error( "failed to spawn projectile for weapon '%s'", weaponDef->GetName ( ) ); } assert ( ent->IsType( idProjectile::GetClassType() ) ); // Create the projectile proj = static_cast(ent); proj->Create( owner, muzzleOrigin + startOffset, dir, NULL, owner->extraProjPassEntity ); projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() ); // make sure the projectile starts inside the bounding box of the owner if ( i == 0 ) { idVec3 start; float distance; trace_t tr; //RAVEN BEGIN //asalmon: xbox must use muzzle Axis for aim assistance #ifdef _XBOX muzzle_pos = muzzleOrigin + muzzleAxis[ 0 ] * 2.0f; if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, muzzleAxis[0], distance ) ) { start = muzzle_pos + distance * muzzleAxis[0]; } #else muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f; if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, playerViewAxis[0], distance ) ) { start = muzzle_pos + distance * playerViewAxis[0]; } #endif //RAVEN END else { start = ownerBounds.GetCenter(); } // RAVEN BEGIN // ddynerman: multiple clip worlds gameLocal.Translation( owner, tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner ); // RAVEN END muzzle_pos = tr.endpos; } // Launch the actual projectile proj->Launch( muzzle_pos + startOffset, dir, pushVelocity, fuseOffset, power ); // Increment the projectile launch count and let the derived classes // mess with it if they want. OnLaunchProjectile ( proj ); } } /* ================ rvWeapon::OnLaunchProjectile ================ */ void rvWeapon::OnLaunchProjectile ( idProjectile* proj ) { owner->AddProjectilesFired( 1 ); if ( proj ) { proj->methodOfDeath = owner->GetCurrentWeapon(); } } /* ================ rvWeapon::Hitscan ================ */ void rvWeapon::Hitscan( const idDict& dict, const idVec3& muzzleOrigin, const idMat3& muzzleAxis, int num_hitscans, float spread, float power ) { idVec3 fxOrigin; idMat3 fxAxis; int i; float ang; float spin; idVec3 dir; int areas[ 2 ]; idBitMsg msg; byte msgBuf[ MAX_GAME_MESSAGE_SIZE ]; // Let the AI know about the new attack if ( !gameLocal.isMultiplayer ) { aiManager.ReactToPlayerAttack( owner, muzzleOrigin, muzzleAxis[0] ); } GetGlobalJointTransform( true, flashJointView, fxOrigin, fxAxis, dict.GetVector( "fxOriginOffset" ) ); if ( gameLocal.isServer ) { assert( hitscanAttackDef >= 0 ); assert( owner && owner->entityNumber < MAX_CLIENTS ); int ownerId = owner ? owner->entityNumber : 0; msg.Init( msgBuf, sizeof( msgBuf ) ); msg.BeginWriting(); msg.WriteByte( GAME_UNRELIABLE_MESSAGE_HITSCAN ); msg.WriteLong( hitscanAttackDef ); msg.WriteBits( ownerId, idMath::BitsForInteger( MAX_CLIENTS ) ); msg.WriteFloat( muzzleOrigin[0] ); msg.WriteFloat( muzzleOrigin[1] ); msg.WriteFloat( muzzleOrigin[2] ); msg.WriteFloat( fxOrigin[0] ); msg.WriteFloat( fxOrigin[1] ); msg.WriteFloat( fxOrigin[2] ); } float spreadRad = DEG2RAD( spread ); idVec3 end; for( i = 0; i < num_hitscans; i++ ) { if( weaponDef->dict.GetBool( "machinegunSpreadStyle" ) ) { float r = gameLocal.random.RandomFloat() * idMath::PI * 2.0f; float u = idMath::Sin( r ) * gameLocal.random.CRandomFloat() * spread * 16; r = idMath::Cos( r ) * gameLocal.random.CRandomFloat() * spread * 16; #ifdef _XBOX end = muzzleOrigin + ( ( 8192 * 16 ) * muzzleAxis[ 0 ] ); end += ( r * muzzleAxis[ 1 ] ); end += ( u * muzzleAxis[ 2 ] ); #else end = muzzleOrigin + ( ( 8192 * 16 ) * playerViewAxis[ 0 ] ); end += ( r * playerViewAxis[ 1 ] ); end += ( u * playerViewAxis[ 2 ] ); #endif dir = end - muzzleOrigin; } else if( weaponDef->dict.GetBool( "shotgunSpreadStyle" ) ) { float r = gameLocal.random.CRandomFloat() * spread * 16; float u = gameLocal.random.CRandomFloat() * spread * 16; #ifdef _XBOX end = muzzleOrigin + ( ( 8192 * 16 ) * muzzleAxis[ 0 ] ); end += ( r * muzzleAxis[ 1 ] ); end += ( u * muzzleAxis[ 2 ] ); #else end = muzzleOrigin + ( ( 8192 * 16 ) * playerViewAxis[ 0 ] ); end += ( r * playerViewAxis[ 1 ] ); end += ( u * playerViewAxis[ 2 ] ); #endif dir = end - muzzleOrigin; } else { ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() ); spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat(); //RAVEN BEGIN //asalmon: xbox must use the muzzleAxis so the aim can be adjusted for aim assistance #ifdef _XBOX dir = muzzleAxis[ 0 ] + muzzleAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - muzzleAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); #else dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) ); #endif //RAVEN END } dir.Normalize(); gameLocal.HitScan( dict, muzzleOrigin, dir, fxOrigin, owner, false, 1.0f, NULL, areas ); if ( gameLocal.isServer ) { msg.WriteDir( dir, 24 ); if ( i == num_hitscans - 1 ) { // NOTE: we emit to the areas of the last hitscan // there is a remote possibility that multiple hitscans for shotgun would cover more than 2 areas, // so in some rare case a client might miss it gameLocal.SendUnreliableMessagePVS( msg, owner, areas[0], areas[1] ); } } } } /* ================ rvWeapon::AlertMonsters ================ */ void rvWeapon::AlertMonsters( void ) { trace_t tr; idEntity *ent; idVec3 end; renderLight_t& muzzleFlash = lights[WPLIGHT_MUZZLEFLASH]; end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target; gameLocal.TracePoint( owner, tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner ); if ( g_debugWeapon.GetBool() ) { gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 ); gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 ); } if ( tr.fraction < 1.0f ) { ent = gameLocal.GetTraceEntity( tr ); if ( ent->IsType( idAI::GetClassType() ) ) { static_cast( ent )->TouchedByFlashlight( owner ); } else if ( ent->IsType( idTrigger::GetClassType() ) ) { ent->Signal( SIG_TOUCH ); ent->ProcessEvent( &EV_Touch, owner, &tr ); } } // jitter the trace to try to catch cases where a trace down the center doesn't hit the monster end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f ); end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f ); gameLocal.TracePoint( owner, tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner ); if ( g_debugWeapon.GetBool() ) { gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 ); gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 ); } if ( tr.fraction < 1.0f ) { ent = gameLocal.GetTraceEntity( tr ); if ( ent->IsType( idAI::GetClassType() ) ) { static_cast( ent )->TouchedByFlashlight( owner ); } else if ( ent->IsType( idTrigger::GetClassType() ) ) { ent->Signal( SIG_TOUCH ); ent->ProcessEvent( &EV_Touch, owner, &tr ); } } } /* ================ rvWeapon::EjectBrass ================ */ void rvWeapon::EjectBrass ( void ) { if ( g_brassTime.GetFloat() <= 0.0f || !owner->CanShowWeaponViewmodel() ) { return; } if ( g_perfTest_weaponNoFX.GetBool() ) { return; } if ( gameLocal.isMultiplayer ) { return; } if ( ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals() ) { return; } idMat3 axis; idVec3 origin; idVec3 linear_velocity; idVec3 angular_velocity; int brassTime; if ( !GetGlobalJointTransform( true, ejectJointView, origin, axis ) ) { return; } // Spawn the client side moveable for the brass rvClientMoveable* cent = NULL; gameLocal.SpawnClientEntityDef( brassDict, (rvClientEntity**)(¢), false ); if( !cent ) { return; } cent->SetOwner( GetOwner() ); cent->SetOrigin ( origin + playerViewAxis * ejectOffset ); cent->SetAxis ( playerViewAxis ); // Depth hack the brass to make sure it clips in front of view weapon properly cent->GetRenderEntity()->weaponDepthHackInViewID = GetOwner()->entityNumber + 1; // Clear the depth hack soon after it clears the view cent->PostEventMS ( &CL_ClearDepthHack, 200 ); // Fade the brass out so they dont accumulate brassTime =(int)SEC2MS(g_brassTime.GetFloat() / 2.0f); cent->PostEventMS ( &CL_FadeOut, brassTime, brassTime ); // Generate a good velocity for the brass idVec3 linearVelocity = brassDict.GetVector("linear_velocity").Random( brassDict.GetVector("linear_velocity_range"), gameLocal.random ); cent->GetPhysics()->SetLinearVelocity( GetOwner()->GetPhysics()->GetLinearVelocity() + linearVelocity * cent->GetPhysics()->GetAxis() ); idAngles angularVelocity = brassDict.GetAngles("angular_velocity").Random( brassDict.GetVector("angular_velocity_range"), gameLocal.random ); cent->GetPhysics()->SetAngularVelocity( angularVelocity.ToAngularVelocity() * cent->GetPhysics()->GetAxis() ); } /* ================ rvWeapon::BloodSplat ================ */ bool rvWeapon::BloodSplat( float size ) { float s, c; idMat3 localAxis, axistemp; idVec3 localOrigin, normal; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return false; } if ( wfl.hasBloodSplat ) { return true; } wfl.hasBloodSplat = true; if ( viewModel->modelDefHandle < 0 ) { return false; } if ( !GetGlobalJointTransform( true, ejectJointView, localOrigin, localAxis ) ) { return false; } localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f; localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f; localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f; normal = idVec3( gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1 ); normal.Normalize(); idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c ); localAxis[2] = -normal; localAxis[2].NormalVectors( axistemp[0], axistemp[1] ); localAxis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s; localAxis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c; localAxis[0] *= 1.0f / size; localAxis[1] *= 1.0f / size; idPlane localPlane[2]; localPlane[0] = localAxis[0]; localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f; localPlane[1] = localAxis[1]; localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f; const idMaterial *mtr = declManager->FindMaterial( "textures/decals/duffysplatgun" ); gameRenderWorld->ProjectOverlay( viewModel->modelDefHandle, localPlane, mtr ); return true; } /* ================ rvWeapon::EnterCinematic ================ */ void rvWeapon::EnterCinematic( void ) { if( viewModel ) { viewModel->StopSound( SND_CHANNEL_ANY, false ); } ExecuteState( "EnterCinematic" ); memset( &wsfl, 0, sizeof(wsfl) ); wfl.disabled = true; LowerWeapon(); } /* ================ rvWeapon::ExitCinematic ================ */ void rvWeapon::ExitCinematic( void ) { wfl.disabled = false; ExecuteState ( "ExitCinematic" ); RaiseWeapon(); } /* ================ rvWeapon::NetCatchup ================ */ void rvWeapon::NetCatchup( void ) { ExecuteState ( "NetCatchup" ); } /* =============== rvWeapon::PlayAnim =============== */ void rvWeapon::PlayAnim( int channel, const char *animname, int blendFrames ) { if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } int anim; anim = viewAnimator->GetAnim( animname ); if ( !anim ) { gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, viewModel->GetName(), viewModel->GetEntityDefName() ); viewAnimator->Clear( channel, gameLocal.time, FRAME2MS( blendFrames ) ); animDoneTime[channel] = 0; } else { viewModel->Show(); viewAnimator->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); animDoneTime[channel] = viewAnimator->CurrentAnim( channel )->GetEndTime(); // Play the animation on the world model as well if ( worldAnimator ) { worldAnimator->GetAnim( animname ); if ( anim ) { worldAnimator->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); } } } } /* =============== rvWeapon::PlayCycle =============== */ void rvWeapon::PlayCycle( int channel, const char *animname, int blendFrames ) { int anim; if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return; } anim = viewAnimator->GetAnim( animname ); if ( !anim ) { gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, viewModel->GetName(), viewModel->GetEntityDefName() ); viewAnimator->Clear( channel, gameLocal.time, FRAME2MS( blendFrames ) ); animDoneTime[channel] = 0; } else { viewModel->Show(); viewAnimator->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); animDoneTime[channel] = viewAnimator->CurrentAnim( channel )->GetEndTime(); // Play the animation on the world model as well if ( worldAnimator ) { anim = worldAnimator->GetAnim( animname ); if ( anim ) { worldAnimator->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( blendFrames ) ); } } } } /* =============== rvWeapon::AnimDone =============== */ bool rvWeapon::AnimDone( int channel, int blendFrames ) { if ( animDoneTime[channel] - FRAME2MS( blendFrames ) <= gameLocal.time ) { return true; } return false; } /* =============== rvWeapon::StartSound =============== */ bool rvWeapon::StartSound ( const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length ) { if ( !viewModel ) { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return false; } return viewModel->StartSound( soundName, channel, soundShaderFlags, broadcast, length ); } /* =============== rvWeapon::StopSound =============== */ void rvWeapon::StopSound( const s_channelType channel, bool broadcast ) { if ( viewModel ) { viewModel->StopSound( channel, broadcast ); } else { common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); } } /* =============== rvWeapon::PlayEffect =============== */ rvClientEffect* rvWeapon::PlayEffect( const char* effectName, jointHandle_t joint, bool loop, const idVec3& endOrigin, bool broadcast ) { if ( viewModel ) { return viewModel->PlayEffect( effectName, joint, loop, endOrigin, broadcast ); } common->Warning( "NULL viewmodel %s\n", __FUNCTION__ ); return 0; } /* ================ rvWeapon::CacheWeapon ================ */ void rvWeapon::CacheWeapon( const char *weaponName ) { const idDeclEntityDef *weaponDef; const char *brassDefName; const char *clipModelName; idTraceModel trm; 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 ( idStr::Icmp( clipModelName, SIMPLE_TRI_NAME ) == 0 ) { trm.SetupPolygon( simpleTri, 3 ); } else { if ( !clipModelName[0] ) { clipModelName = brassDef->dict.GetString( "model" ); // use the visual model } // load the trace model collisionModelManager->TrmFromModel( gameLocal.GetMapName(), clipModelName, trm ); } } } const idKeyValue* kv; kv = weaponDef->dict.MatchPrefix( "gui", NULL ); while( kv ) { if ( kv->GetValue().Length() ) { uiManager->FindGui( kv->GetValue().c_str(), true, false, true ); } kv = weaponDef->dict.MatchPrefix( "gui", kv ); } } /* =============================================================================== States =============================================================================== */ CLASS_STATES_DECLARATION ( rvWeapon ) STATE ( "Raise", rvWeapon::State_Raise ) STATE ( "Lower", rvWeapon::State_Lower ) STATE ( "ExitCinematic", rvWeapon::State_ExitCinematic ) STATE ( "NetCatchup", rvWeapon::State_NetCatchup ) STATE ( "EjectBrass", rvWeapon::Frame_EjectBrass ) END_CLASS_STATES /* ================ rvWeapon::State_Raise Raise the weapon ================ */ stateResult_t rvWeapon::State_Raise ( const stateParms_t& parms ) { enum { STAGE_INIT, STAGE_WAIT, }; switch ( parms.stage ) { // Start the weapon raising case STAGE_INIT: SetStatus ( WP_RISING ); PlayAnim( ANIMCHANNEL_ALL, "raise", 0 ); return SRESULT_STAGE ( STAGE_WAIT ); case STAGE_WAIT: if ( AnimDone ( ANIMCHANNEL_ALL, 4 ) ) { SetState ( "Idle", 4 ); return SRESULT_DONE; } if ( wsfl.lowerWeapon ) { SetState ( "Lower", 4 ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } /* ================ rvWeapon::State_Lower Lower the weapon ================ */ stateResult_t rvWeapon::State_Lower ( const stateParms_t& parms ) { enum { STAGE_INIT, STAGE_WAIT, STAGE_WAITRAISE }; switch ( parms.stage ) { case STAGE_INIT: SetStatus ( WP_LOWERING ); PlayAnim ( ANIMCHANNEL_ALL, "putaway", parms.blendFrames ); return SRESULT_STAGE(STAGE_WAIT); case STAGE_WAIT: if ( AnimDone ( ANIMCHANNEL_ALL, 0 ) ) { SetStatus ( WP_HOLSTERED ); return SRESULT_STAGE(STAGE_WAITRAISE); } return SRESULT_WAIT; case STAGE_WAITRAISE: if ( wsfl.raiseWeapon ) { SetState ( "Raise", 0 ); return SRESULT_DONE; } return SRESULT_WAIT; } return SRESULT_ERROR; } /* ===================== rvWeapon::State_ExitCinematic ===================== */ stateResult_t rvWeapon::State_NetCatchup ( const stateParms_t& parms ) { SetState ( "idle", parms.blendFrames ); return SRESULT_DONE; } /* ===================== rvWeapon::State_ExitCinematic ===================== */ stateResult_t rvWeapon::State_ExitCinematic ( const stateParms_t& parms ) { SetState ( "Idle", 0 ); return SRESULT_DONE; } /* ===================== rvWeapon::Frame_EjectBrass ===================== */ stateResult_t rvWeapon::Frame_EjectBrass( const stateParms_t& parms ) { EjectBrass(); return SRESULT_DONE; } /* ===================== rvWeapon::GetDebugInfo ===================== */ void rvWeapon::GetDebugInfo ( debugInfoProc_t proc, void* userData ) { idClass::GetDebugInfo ( proc, userData ); proc ( "rvWeapon", "state", stateThread.GetState()?stateThread.GetState()->state->name : "", userData ); }