/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program 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. ** ** This program 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 this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/weaponview.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 1/16/02 11:46a $* * * * $Revision:: 56 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "weaponview.h" #include "assets.h" #include "hanim.h" #include "combat.h" #include "pscene.h" #include "ccamera.h" #include "texture.h" #include "physicalgameobj.h" #include "debug.h" #include "animobj.h" #include "soldier.h" #include "wwprofile.h" #include "weapons.h" #include "decophys.h" #include "stealtheffect.h" #include "animcontrol.h" /* ** */ enum { WEAPON_STATE_IDLE, WEAPON_STATE_FIRE, WEAPON_STATE_RELOAD, WEAPON_STATE_ENTER, WEAPON_STATE_EXIT, // WEAPON_STATE_SPIN_DOWN, NUM_WEAPON_STATES, }; int WeaponViewEnabled; int WeaponState; bool LastMuzzleFlash; DecorationPhysClass*HandsPhysObj; RenderObjClass* WeaponModel; RenderObjClass* ClipModel; Vector3 HandsOffset; HAnimClass * HandsAnims[ NUM_WEAPON_STATES ]; HAnimClass * WeaponAnims[ NUM_WEAPON_STATES ]; // Anim controls that provide blending SimpleAnimControlClass HandAnimControl; SimpleAnimControlClass WeaponAnimControl; // Hands Bob enum { BOB_NONE, BOB_IDLE, BOB_WALK, BOB_RUN, }; int BobState; HTreeClass * BobHTree; HAnimClass * BobHAnim; float BobFrame; float BobRecoil; static void Set_Bob( int bob_state ); static void Set_Bob_Recoil( float amount ); static void Aquire_Hands_Assets( void ); static void Release_Hands_Assets( void ); static void Aquire_Weapon_Assets( const WeaponClass * weapon ); static void Release_Weapon_Assets( void ); /* ** */ void WeaponViewClass::Init() { WeaponModel = NULL; HandsPhysObj = NULL; HandAnimControl.Set_Model( NULL ); WeaponAnimControl.Set_Model( NULL ); ClipModel = NULL; for ( int i = 0; i < NUM_WEAPON_STATES; i++ ) { WeaponAnims[i] = NULL; HandsAnims[i] = NULL; } WeaponState = -1; BobState = BOB_NONE; BobHTree = NULL; BobHAnim = NULL; BobFrame = 0; BobRecoil = 0; WeaponViewEnabled = false; } /* ** */ void WeaponViewClass::Shutdown() { Set_Bob( BOB_NONE ); Release_Weapon_Assets(); Release_Hands_Assets(); } /* ** */ void WeaponViewClass::Reset() { Set_Bob( BOB_NONE ); Release_Weapon_Assets(); Release_Hands_Assets(); } enum { CHUNKID_VARIABLES = 730011054, MICROCHUNKID_HANDS_PHYS_OBJ = 1, MICROCHUNKID_ENABLED, }; bool WeaponViewClass::Save( ChunkSaveClass &csave ) { csave.Begin_Chunk( CHUNKID_VARIABLES ); // If the scene has our hands, we must save and swizzle them if ( HandsPhysObj != NULL && COMBAT_SCENE->Contains( HandsPhysObj ) ) { WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HANDS_PHYS_OBJ, HandsPhysObj ); } WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ENABLED, WeaponViewEnabled ); csave.End_Chunk(); return true; } bool WeaponViewClass::Load( ChunkLoadClass &cload ) { Release_Weapon_Assets(); Release_Hands_Assets(); while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_VARIABLES: { while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_HANDS_PHYS_OBJ, HandsPhysObj ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ENABLED, WeaponViewEnabled ); default: Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; } default: Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } if ( HandsPhysObj != NULL ) { REQUEST_REF_COUNTED_POINTER_REMAP ((RefCountClass **)&HandsPhysObj); } return true; } /* ** */ void WeaponViewClass::Enable( bool enable ) { WeaponViewEnabled = enable; } /* ** called each time through the main loop */ void WeaponViewClass::Think() { WWPROFILE( "WeaponView Think" ); bool bail = false; if ( COMBAT_CAMERA && COMBAT_CAMERA->Is_Star_Sniping() ) { bail = true; } if ( COMBAT_CAMERA && COMBAT_CAMERA->Is_Using_Host_Model() ) { bail = true; } if ( COMBAT_STAR == NULL ) { bail = true; } if ( !WeaponViewEnabled ) { bail = true; } // No first person weapon in vehicles if ( COMBAT_STAR && COMBAT_STAR->Get_Vehicle() != NULL ) { bail = true; } // not if in trans (camera lerping ) if ( COMBAT_CAMERA && COMBAT_CAMERA->Is_Lerping() ) { bail = true; } WeaponClass * weapon = NULL; if ( COMBAT_STAR != NULL ) { weapon = COMBAT_STAR->Get_Weapon(); } if ( weapon == NULL && WeaponModel == NULL ) { bail = true; } if ( weapon != NULL ) { if ( ( weapon->Get_Style() == WEAPON_HOLD_STYLE_C4 ) || ( weapon->Get_Style() == WEAPON_HOLD_STYLE_BEACON ) ) { if ( weapon->Get_Total_Rounds() == 0 ) { if ( WeaponModel == NULL || WeaponState == WEAPON_STATE_IDLE ) { bail = true; } } } } // If not in first person mode, hide the model and bail if ( bail ) { if ( HandsPhysObj != NULL && COMBAT_SCENE->Contains( HandsPhysObj ) ) { COMBAT_SCENE->Remove_Object( HandsPhysObj ); } // To force an enter when we come back WeaponState = WEAPON_STATE_EXIT; Release_Weapon_Assets(); return; } // Setup camera bob if ( COMBAT_STAR ) { Vector3 vel; COMBAT_STAR->Get_Velocity( vel ); if ( vel.Length() > WWMATH_EPSILON ) { if ( COMBAT_STAR->Is_Slow() ) { Set_Bob( BOB_WALK ); } else { Set_Bob( BOB_RUN ); } } else { Set_Bob( BOB_IDLE ); } // Setup weapon State if ( weapon != NULL && weapon->Is_Firing() ) { Set_Bob_Recoil( 0.15f ); } } // is the current (non-looping) animation completed bool is_current_complete = true; #if 0 if ( HandsPhysObj != NULL && (WeaponState != WEAPON_STATE_IDLE) ) { RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); if ( hands_model != NULL && ( hands_model->Peek_Animation() != NULL ) && ((Animatable3DObjClass*)hands_model)->Is_Animation_Complete() == false ) { is_current_complete = false; } } #else if ( HandsPhysObj != NULL && (WeaponState != WEAPON_STATE_IDLE) ) { is_current_complete = HandAnimControl.Is_Complete(); } #endif bool muzzle_flash_on = false; static bool ForceFireLoop = false; // What state are we going to? int new_weapon_state = WEAPON_STATE_IDLE; if ( COMBAT_STAR ) { WeaponClass * star_weapon = COMBAT_STAR->Get_Weapon(); if ( star_weapon != NULL ) { if ( star_weapon->Is_Reloading() ) { new_weapon_state = WEAPON_STATE_RELOAD; } else if ( star_weapon->Is_Firing() ) { if ( WeaponState == WEAPON_STATE_FIRE ) { ForceFireLoop = true; } new_weapon_state = WEAPON_STATE_FIRE; if ( WeaponState == WEAPON_STATE_RELOAD ) { is_current_complete = true; // Force a fire during reload } muzzle_flash_on = true; if ( WeaponModel != NULL ) { // if this gun has an eject bone, eject a shell. int eject_index = WeaponModel->Get_Bone_Index( "eject" ); if ( eject_index > 0 ) { star_weapon->Make_Shell_Eject( WeaponModel->Get_Bone_Transform( eject_index ) ); } } } } } const char* cur_weapon_model_name=""; if ( WeaponModel ) { cur_weapon_model_name = WeaponModel->Get_Name(); } StringClass new_weapon_model_name(true); if ( weapon ) { Get_Render_Obj_Name_From_Filename( new_weapon_model_name, weapon->Get_First_Person_Model_Name() ); } // If we need to change the assets, if ( stricmp( cur_weapon_model_name,new_weapon_model_name)!=0) { // if ( cur_weapon_model_name.Compare_No_Case( new_weapon_model_name ) != 0 ) { // If we don't have a weapon, switch! if ( WeaponModel == NULL ) { WeaponState = WEAPON_STATE_EXIT; is_current_complete = true; } if ( WeaponState != WEAPON_STATE_EXIT ) { // If we have a current weapon, exit it new_weapon_state = WEAPON_STATE_EXIT; } else { if ( is_current_complete ) { // If we are done exiting, switch and enter Release_Weapon_Assets(); Aquire_Weapon_Assets( COMBAT_STAR->Get_Weapon() ); new_weapon_state = WEAPON_STATE_ENTER; } } } if ( WeaponModel ) { // Update the muzzle flash RenderObjClass * model = WeaponModel; if ( model && (LastMuzzleFlash != muzzle_flash_on)) { for (int i=0; iGet_Num_Sub_Objects(); i++) { RenderObjClass * robj = model->Get_Sub_Object(i); if (strstr(robj->Get_Name(),"MUZZLEFLASH") || strstr(robj->Get_Name(),"MZ")) { robj->Set_Hidden( !muzzle_flash_on ); } robj->Release_Ref(); } LastMuzzleFlash = muzzle_flash_on; } } if ( HandsPhysObj != NULL && WeaponModel != NULL ) { // make sure the "hands" object has the stealth effect if (COMBAT_STAR->Peek_Stealth_Effect() != NULL) { HandsPhysObj->Add_Effect_To_Me(COMBAT_STAR->Peek_Stealth_Effect()); } // Add all rendobjs from the scene if ( !COMBAT_SCENE->Contains( HandsPhysObj ) ) { COMBAT_SCENE->Add_Dynamic_Object( HandsPhysObj ); if ( new_weapon_state == WEAPON_STATE_ENTER ) { new_weapon_state = WEAPON_STATE_IDLE; is_current_complete = true; } } // When stopping, go to spin down if ( is_current_complete && WeaponState == WEAPON_STATE_FIRE && new_weapon_state == WEAPON_STATE_IDLE ) { // if the anims exist... // new_weapon_state = WEAPON_STATE_SPIN_DOWN; } if ( WeaponState != WEAPON_STATE_IDLE ) { if ( is_current_complete ) { // If done playing the current non-looping anim, go to next state if ( WeaponState == WEAPON_STATE_FIRE && ForceFireLoop ) { ForceFireLoop = false; // Clear any force fire // Keep it by restarting the fire anim RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); if ( hands_model != NULL ) { // hands_model->Set_Animation( HandsAnims[ WeaponState ], 0, RenderObjClass::ANIM_MODE_ONCE ); HandAnimControl.Set_Animation( HandsAnims[ WeaponState ] ); HandAnimControl.Set_Mode( ANIM_MODE_ONCE, 0 ); HandAnimControl.Update(0); } // WeaponModel->Set_Animation( WeaponAnims[ WeaponState ], 0, RenderObjClass::ANIM_MODE_ONCE ); WeaponAnimControl.Set_Animation( WeaponAnims[ WeaponState ] ); WeaponAnimControl.Set_Mode( ANIM_MODE_ONCE, 0 ); WeaponAnimControl.Update(0); } else { WeaponState = -1; } } } if ( new_weapon_state == WEAPON_STATE_EXIT ) { WeaponState = -1; // force a change } // If playing a looping anim, switch if needed if ( (WeaponState == -1) || ( WeaponState == WEAPON_STATE_IDLE ) ) { if ( new_weapon_state != WeaponState ) { WeaponState = new_weapon_state; float anim_blend_time = 0.15f; if ( new_weapon_state == WEAPON_STATE_FIRE ) { anim_blend_time = 0; // No blend when firing } int mode = ( WeaponState == WEAPON_STATE_IDLE ) ? RenderObjClass::ANIM_MODE_LOOP : RenderObjClass::ANIM_MODE_ONCE; RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); if ( hands_model != NULL ) { mode = ( WeaponState == WEAPON_STATE_IDLE ) ? ANIM_MODE_LOOP : ANIM_MODE_ONCE; // hands_model->Set_Animation( HandsAnims[ WeaponState ], 0, mode ); HandAnimControl.Set_Animation( HandsAnims[ WeaponState ], anim_blend_time ); HandAnimControl.Set_Mode( (AnimMode)mode ); } // WeaponModel->Set_Animation( WeaponAnims[ WeaponState ], 0, mode ); WeaponAnimControl.Set_Animation( WeaponAnims[ WeaponState ], anim_blend_time ); mode = ( WeaponState == WEAPON_STATE_IDLE ) ? ANIM_MODE_LOOP : ANIM_MODE_ONCE; WeaponAnimControl.Set_Mode( (AnimMode)mode ); // if ( HandsAnims[ WeaponState ] != NULL ) { // Debug_Say(( "Playing %s\n", HandsAnims[ WeaponState ]->Get_Name() )); // } } } // Lets put the gun relative to the camera Matrix3D tm = COMBAT_CAMERA->Get_Transform(); // Add bobing position if ( BobHTree != NULL && BobHAnim != NULL ) { BobFrame += TimeManager::Get_Frame_Seconds() * BobHAnim->Get_Frame_Rate(); if ( BobFrame > BobHAnim->Get_Num_Frames()-1 ) { BobFrame -= BobHAnim->Get_Num_Frames()-1; } #if 0 // no more programatic recoil tm.Translate( 0, 0, BobRecoil ); if ( BobRecoil > 0 ) { BobRecoil -= TimeManager::Get_Frame_Real_Seconds() * 2; if ( BobRecoil < 0 ) { BobRecoil = 0; } } #endif BobHTree->Anim_Update( tm, BobHAnim, BobFrame ); // Now get the camera bone // Debug_Say(( "%f %f %f\n", tm.Get_Translation().X, tm.Get_Translation().Y, tm.Get_Translation().Z )); tm = BobHTree->Get_Transform( BobHTree->Get_Bone_Index( "CAMERA" ) ); // Debug_Say(( "%f %f %f\n", tm.Get_Translation().X, tm.Get_Translation().Y, tm.Get_Translation().Z )); } // Convert from camera to world convention tm.Rotate_Z( DEG_TO_RADF(90.0) ); tm.Rotate_Y( DEG_TO_RADF(90.0) ); Vector3 fp_offset = HandsOffset + COMBAT_CAMERA->Get_First_Person_Offset_Tweak(); tm.Translate( fp_offset ); //WWASSERT(COMBAT_STAR != NULL);//TSS if (COMBAT_STAR != NULL) { // // COMBAT_STAR may be NULL during MP game intermission // Matrix3D obj_convention_camera = COMBAT_CAMERA->Get_Transform(); obj_convention_camera.Rotate_Z( DEG_TO_RADF(90.0) ); obj_convention_camera.Rotate_Y( DEG_TO_RADF(90.0) ); Vector3 camera_space_target; Vector3 camera_space_source; Vector3 target_pos = COMBAT_STAR->Get_Targeting_Pos(); // Always look at a point a constant distance ahead, to avoid the gun poping around target_pos = COMBAT_CAMERA->Get_Transform() * Vector3( 0,0,-100 ); Matrix3D::Inverse_Transform_Vector( obj_convention_camera, target_pos, &camera_space_target ); Matrix3D::Inverse_Transform_Vector( obj_convention_camera, tm.Get_Translation(), &camera_space_source ); Matrix3D camera_space_aim; camera_space_aim.Obj_Look_At( camera_space_source, camera_space_target, 0 ); Matrix3D::Multiply( obj_convention_camera, camera_space_aim, &tm ); } HandsPhysObj->Set_Transform( tm ); // Now, pull the hands backwards enough to clear any walls if ( WeaponModel && COMBAT_CAMERA) { const Matrix3D & muzzle_tm = WeaponModel->Get_Bone_Transform( "muzzlea0" ); Vector3 ray_end = muzzle_tm * Vector3( 0.1f, 0, 0 ); Vector3 ray_start = COMBAT_CAMERA->Get_Transform().Get_Translation(); ray_start.Z = ray_end.Z; LineSegClass ray; ray.Set( ray_start, ray_end ); CastResultStruct result; PhysRayCollisionTestClass raytest(ray, &result, BULLET_COLLISION_GROUP, COLLISION_TYPE_PROJECTILE); { if (COMBAT_STAR != NULL && COMBAT_STAR->Peek_Physical_Object()!=NULL) { COMBAT_STAR->Peek_Physical_Object()->Inc_Ignore_Counter(); } WWPROFILE( "Cast Ray" ); WWASSERT(COMBAT_SCENE != NULL); COMBAT_SCENE->Cast_Ray( raytest ); if (COMBAT_STAR != NULL && COMBAT_STAR->Peek_Physical_Object()!=NULL) { COMBAT_STAR->Peek_Physical_Object()->Dec_Ignore_Counter(); } } /* if ( raytest.CollidedPhysObj != NULL && raytest.CollidedPhysObj->Get_Observer() != NULL ) { DamageableGameObj * obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_DamageableGameObj(); if ( obj ) { Debug_Say(( "Hit %s\n", obj->Get_Definition().Get_Name() )); } }*/ // Determine the Camera Target Point and object Vector3 collision_pt; ray.Compute_Point( raytest.Result->Fraction, &collision_pt ); collision_pt -= ray_end; float pullback = collision_pt.Length(); // Debug_Say(( "Pullback = %f\n", pullback )); tm.Translate( Vector3( -pullback, 0, 0 ) ); HandsPhysObj->Set_Transform( tm ); } } if ( HandsPhysObj && HandsPhysObj->Peek_Model() ) { HandAnimControl.Update( TimeManager::Get_Frame_Seconds() ); } if ( WeaponModel ) { WeaponAnimControl.Update( TimeManager::Get_Frame_Seconds() ); } } Vector3 WeaponViewClass::Get_Muzzle_Pos() { if ( WeaponModel ) { return WeaponModel->Get_Bone_Transform( "muzzlea0" ).Get_Translation(); } return COMBAT_CAMERA->Get_Transform().Get_Translation(); } const char * WeaponActionNames[ NUM_WEAPON_STATES ] = { "IDLE", //WEAPON_STATE_IDLE, "FIRE", //WEAPON_STATE_FIRE, "RELOD", //WEAPON_STATE_RELOAD, "ENTER", //WEAPON_STATE_ENTER, "EXIT", //WEAPON_STATE_EXIT, // "EFIRE", //WEAPON_STATE_SPIN_DOWN, }; /* ** Get and release the Weapon specific assets ** Each weapon has a name ie: PIST ** The weapon model is in the form F_GM_ ie: F_GM_PIST ** The clip model is in the form F_CM_ ie: F_CM_PIST ** The weapon states are IDLE, FIRE, RELOAD, ENTER, EXIT ** For each weapon state, except ENTER and EXIT, we have a weapon anim tied to the weapon model (use IDLE anim for ENTER/EXIT ) ** The weapon anims are in the form F_GA__ ie: F_GA_PIST_IDLE, F_GA_PIST_FIRE ** For each weapon state, we have a hand anim tied to the F_SKELETON ** The hand anims are in the form F_GA__ ie: F_GA_PIST_IDLE, F_GA_PIST_FIRE ** Weapon anims should be the same duration (number of frames) and the corresponding hand anim (except looping +IDLES) */ static void Aquire_Weapon_Assets( const WeaponClass * weapon ) { if ( HandsPhysObj == NULL ) { Aquire_Hands_Assets(); } else { HandAnimControl.Set_Model( HandsPhysObj->Peek_Model() ); HandAnimControl.Set_Animation( (HAnimClass*)NULL ); } if ( HandsPhysObj == NULL || HandsPhysObj->Peek_Model() == NULL ) { return; } RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); StringClass weapon_name; if ( weapon ) { // Debug_Say(( "Loading Weapon from %s\n", weapon->Get_First_Person_Model_Name() )); WeaponModel = ::Create_Render_Obj_From_Filename( weapon->Get_First_Person_Model_Name() ); LastMuzzleFlash = true; // force it off // Debug_Say(( "Loaded %s\n", WeaponModel ? WeaponModel->Get_Name() : "NONE" )); // Use model name if ( WeaponModel != NULL ) { weapon_name = WeaponModel->Get_Name() + 5; // Get the weapon name from the model Debug_Say(( "weapon_name is %s\n", (const char *)weapon_name )); StringClass clip_name; clip_name = WeaponModel->Get_Name(); if ( clip_name.Get_Length() > 4 ) { clip_name[2] = 'C'; clip_name[3] = 'M'; } ClipModel = WW3DAssetManager::Get_Instance()->Create_Render_Obj( clip_name ); if ( ClipModel != NULL ) { hands_model->Add_Sub_Object_To_Bone( ClipModel, "CLIPBONE" ); } } } WeaponAnimControl.Set_Model( WeaponModel ); WeaponAnimControl.Set_Animation( (HAnimClass*)NULL ); if ( WeaponModel != NULL ) { // Add the model hands_model->Add_Sub_Object_To_Bone( WeaponModel, "GUNBONE" ); HandsOffset = weapon->Get_First_Person_Model_Offset(); StringClass weapon_htree_name; if ( WeaponModel->Get_HTree() != NULL ) { weapon_htree_name = WeaponModel->Get_HTree()->Get_Name(); } SET_REF_OWNER( WeaponModel ); for ( int i = 0; i < NUM_WEAPON_STATES; i++ ) { StringClass anim_name; // Get Weapon Anims, use the idle anim for enter and exit int state = ( i >= WEAPON_STATE_ENTER ) ? WEAPON_STATE_IDLE : i; anim_name.Format( "%s.F_GA_%s_%s", weapon_htree_name, weapon_name, WeaponActionNames[state] ); // Debug_Say(( "Loading Weapon Anim %s\n", anim_name )); WeaponAnims[i] = WW3DAssetManager::Get_Instance()->Get_HAnim( anim_name ); if ( WeaponAnims[i] == NULL ) { Debug_Say(( "Missing Weapon Anim %s\n", anim_name )); } // Get Hands Anims anim_name.Format( "F_SKELETON.F_HA_%s_%s", weapon_name, WeaponActionNames[i] ); // Debug_Say(( "Loading Hands Anim %s\n", anim_name )); HandsAnims[i] = WW3DAssetManager::Get_Instance()->Get_HAnim( anim_name ); if ( HandsAnims[i] == NULL ) { Debug_Say(( "Missing Hands Anim %s\n", anim_name )); HandsAnims[i] = WW3DAssetManager::Get_Instance()->Get_HAnim( "F_SKELETON.F_HA_PIST_IDLE" ); } } } } static void Release_Weapon_Assets( void ) { WeaponAnimControl.Set_Model( NULL ); WeaponAnimControl.Set_Animation( (HAnimClass*)NULL ); if ( WeaponModel != NULL ) { // Remove WWASSERT( HandsPhysObj != NULL ); RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); WWASSERT( hands_model != NULL ); hands_model->Remove_Sub_Objects_From_Bone( "GUNBONE" ); WeaponModel->Release_Ref(); WeaponModel = NULL; } if ( ClipModel != NULL ) { // Remove WWASSERT( HandsPhysObj != NULL ); RenderObjClass * hands_model = HandsPhysObj->Peek_Model(); WWASSERT( hands_model != NULL ); hands_model->Remove_Sub_Objects_From_Bone( "CLIPBONE" ); ClipModel->Release_Ref(); ClipModel = NULL; } for ( int i = 0; i < NUM_WEAPON_STATES; i++ ) { if ( WeaponAnims[i] != NULL ) { WeaponAnims[i]->Release_Ref(); WeaponAnims[i] = NULL; } if ( HandsAnims[i] != NULL ) { HandsAnims[i]->Release_Ref(); HandsAnims[i] = NULL; } } } /* ** Get and release the Hand assets ** All hand models are built on the F_SKELETON HTree, which has a GUNBONE and CLIPBONE ** The hand models can be different for each player model, and are in the form F_HM_XXX ie: F_HM_HAVOC ** The hand anims are created from the particular weapon model loaded. */ static void Aquire_Hands_Assets( void ) { StringClass name = COMBAT_STAR->Get_First_Person_Hands_Model_Name(); // Debug_Say(( "Loading Hands from %s\n", name )); HandsPhysObj = new DecorationPhysClass(); if ( HandsPhysObj != NULL ) { HandsPhysObj->Set_Collision_Group( UNCOLLIDEABLE_GROUP ); RenderObjClass * model = ::Create_Render_Obj_From_Filename( name ); if ( model != NULL ) { HandAnimControl.Set_Model( model ); HandAnimControl.Set_Animation( (HAnimClass*)NULL ); HandsPhysObj->Set_Model( model ); model->Release_Ref(); } } } static void Release_Hands_Assets( void ) { HandAnimControl.Set_Model( NULL ); HandAnimControl.Set_Animation( (HAnimClass*)NULL ); if ( HandsPhysObj != NULL ) { if ( COMBAT_SCENE->Contains( HandsPhysObj ) ) { COMBAT_SCENE->Remove_Object( HandsPhysObj ); } HandsPhysObj->Release_Ref(); HandsPhysObj = NULL; } } /* ** */ static const char * BobNames[] = { "", // BOB_NONE "F_CA_IDLE", // BOB_IDLE "F_CA_WALK", // BOB_WALK "F_CA_RUN", // BOB_RUN }; static void Set_Bob( int bob_state ) { if ( BobState != bob_state ) { BobState = bob_state; // Release the old copys, if any BobHTree = NULL; REF_PTR_RELEASE( BobHAnim ); StringClass name(BobNames[ BobState ],true); // Debug_Say(( "New Camera Bob State %d \"%s\"\n", BobState, name )); if ( !name.Is_Empty() ) { BobHTree = WW3DAssetManager::Get_Instance()->Get_HTree( name ); StringClass anim(0,true); anim.Format( "%s.%s", name,name ); BobHAnim = WW3DAssetManager::Get_Instance()->Get_HAnim( anim ); } } } static void Set_Bob_Recoil( float amount ) { BobRecoil = amount; }