/*
**	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 <http://www.gnu.org/licenses/>.
*/

/*********************************************************************************************** 
 ***                            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; i<model->Get_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_<NAME> ie: F_GM_PIST
** The clip model is in the form F_CM_<NAME> 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_<NAME>_<STATE> 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_<NAME>_<STATE> 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; 
}

