/*
**	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/soldier.cpp                           $* 
 *                                                                                             * 
 *                      $Author:: Byon_g                                                      $* 
 *                                                                                             * 
 *                     $Modtime:: 3/29/02 4:58p                                               $* 
 *                                                                                             * 
 *                    $Revision:: 570                                                         $* 
 *                                                                                             * 
 *---------------------------------------------------------------------------------------------* 
 * Functions:                                                                                  * 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "soldier.h"
#include "debug.h"
#include "pscene.h"
#include "combat.h"
#include "weapons.h"
#include "damage.h"
#include "humanphys.h"
#include "animcontrol.h"
#include "rendobj.h"
#include "wwpacket.h"
#include "assets.h"
#include "gameobjmanager.h"
#include "vehicle.h"
#include "c4.h"
#include "objlibrary.h"
#include "physicalgameobj.h"
#include "persistfactory.h"
#include "combatchunkid.h"
#include "definition.h"
#include "simpledefinitionfactory.h"
#include "wwhack.h"
#include "surfaceeffects.h"
#include "weaponmanager.h"
#include "weaponbag.h"
#include "physcoltest.h"
#include "htree.h"
#include "animobj.h"
#include "wwaudio.h"
#include "soldierobserver.h"
#include "playertype.h"
#include "crandom.h"
#include "ccamera.h"
#include "bitpackids.h"
#include "translatedb.h"
#include "translateobj.h"
#include "hlod.h"
#include "audiblesound.h"
#include "wwprofile.h"
#include "globalsettings.h"
#include "colors.h"
#include "input.h"
#include "gametype.h"
#include "messagewindow.h"
#include "conversationmgr.h"
#include "activeconversation.h"
#include "bones.h"
#include "dynamicspeechanim.h"
#include "hudinfo.h"
#include "powerup.h"
#include "diaglog.h"
#include "vistable.h"
#include "crandom.h"
#include "transitioneffect.h"
#include "combatmaterialeffectmanager.h"
#include "explosion.h"
#include "playerdata.h"
#include "encyclopediamgr.h"
#include "apppackettypes.h"
#include "hud.h"
#include "unitcoordinationzonemgr.h"
#include "specialbuilds.h"
#include "weaponview.h"
#include "ffactory.h"
#include "realcrc.h"

/*
**
*/
#define		GUN_BONE_NAME				"GUNBONE"
#define		BACK_GUN_BONE_NAME		"BACKGUNBONE"

#define		POKE_ANGLE					DEG_TO_RAD( 22 )

#define		AMBUSH_DAMAGE_SCALE		1

const float EMOT_ICON_HEIGHT = 2.0F;

/*
** SoldierGameObjDef
*/
DECLARE_FORCE_LINK( Soldier )

SimplePersistFactoryClass<SoldierGameObjDef, CHUNKID_GAME_OBJECT_DEF_SOLDIER>	_SoldierGameObjDefPersistFactory;

DECLARE_DEFINITION_FACTORY(SoldierGameObjDef, CLASSID_GAME_OBJECT_DEF_SOLDIER, "Soldier") _SoldierGameObjDefDefFactory;

SoldierGameObjDef::SoldierGameObjDef( void ) :
	TurnRate( DEG_TO_RADF( 360.0f ) ),
	JumpVelocity( 2 ),
	SkeletonHeight( 0 ),
	SkeletonWidth( 0 ),
	UseInnateBehavior( true ),
	InnateAggressiveness( 0.5f ),
	InnateTakeCoverProbability( 0.5f ),
	InnateIsStationary( false ),
	HumanAnimOverrideDefID( 0 ),
	HumanLoiterCollectionDefID( 0 ),
	DeathSoundPresetID( 0 )
{
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_ANGLE, TurnRate );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, JumpVelocity );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, SkeletonHeight );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, SkeletonWidth );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_BOOL, UseInnateBehavior );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, InnateAggressiveness );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FLOAT, InnateTakeCoverProbability );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_BOOL, InnateIsStationary );
	EDITABLE_PARAM( SoldierGameObjDef, ParameterClass::TYPE_FILENAME,	FirstPersonHands );
	GENERIC_DEFID_PARAM( SoldierGameObjDef, HumanAnimOverrideDefID, CLASSID_GLOBAL_SETTINGS_DEF_HUMAN_ANIM_OVERRIDE );
	GENERIC_DEFID_PARAM( SoldierGameObjDef, HumanLoiterCollectionDefID, CLASSID_GLOBAL_SETTINGS_DEF_HUMAN_LOITER );
	GENERIC_DEFID_PARAM( SoldierGameObjDef, DeathSoundPresetID, CLASSID_SOUND );	

	MODEL_DEF_PARAM( SoldierGameObjDef, PhysDefID, "HumanPhysDef" );

	//
	//	We want soldiers to use innate conversations by default
	//
	AllowInnateConversations = true;
	return ;
}

uint32	SoldierGameObjDef::Get_Class_ID (void) const	
{ 
	return CLASSID_GAME_OBJECT_DEF_SOLDIER; 
}

PersistClass *	SoldierGameObjDef::Create( void ) const 
{
	SoldierGameObj * obj = new SoldierGameObj;
	obj->Init( *this );
	return obj;
}

enum	{
	CHUNKID_DEF_PARENT							=	909991656,
	CHUNKID_DEF_VARIABLES,
	CHUNKID_DEF_DIALOG_ENTRY,

	MICROCHUNKID_DEF_TURN_RATE					=	1,
	MICROCHUNKID_DEF_JUMP_VELOCITY,
	MICROCHUNKID_DEF_SKELETON_HEIGHT,
	MICROCHUNKID_DEF_SKELETON_WIDTH,
	MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR,
	MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS, 
	MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB, 
	XXXMICROCHUNKID_DEF_INNATE_ESCORT_ID, 
	XXXMICROCHUNKID_DEF_INNATE_ESCORT_RANGE, 
	MICROCHUNKID_DEF_FIRST_PERSON_HANDS, 
	XXXMICROCHUNKID_DEF_CORPSE_PERSIST_TIME,
	MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS,
	MICROCHUNKID_DEF_INNATE_IS_STATIONARY,
	MICROCHUNKID_DEF_ORATOR_TYPE,
	MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID,
	MICROCHUNKID_DEF_DEATH_SOUND_PRESET,
	MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID,
};

bool	SoldierGameObjDef::Save( ChunkSaveClass & csave )
{
	csave.Begin_Chunk( CHUNKID_DEF_PARENT );
		SmartGameObjDef::Save( csave );
	csave.End_Chunk();

	//
	//	Save the dialog entries
	//
	for (int index = 0; index < DIALOG_MAX; index ++) {
		csave.Begin_Chunk( CHUNKID_DEF_DIALOG_ENTRY );
			DialogList[index].Save (csave);
		csave.End_Chunk();	
	}	

	csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TURN_RATE, TurnRate );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_JUMP_VELOCITY, JumpVelocity );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SKELETON_HEIGHT, SkeletonHeight );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SKELETON_WIDTH, SkeletonWidth );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR, UseInnateBehavior );
		//WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS, UseInnateConversations );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS, InnateAggressiveness );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB, InnateTakeCoverProbability );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_INNATE_IS_STATIONARY, InnateIsStationary );
		WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_FIRST_PERSON_HANDS, FirstPersonHands );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID, HumanAnimOverrideDefID );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID, HumanLoiterCollectionDefID );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEATH_SOUND_PRESET, DeathSoundPresetID );		
		
	csave.End_Chunk();

	return true;
}

bool	SoldierGameObjDef::Load( ChunkLoadClass &cload )
{
	int dialog_index = 0;

	while (cload.Open_Chunk()) {
		switch(cload.Cur_Chunk_ID()) {

			case CHUNKID_DEF_PARENT:
				SmartGameObjDef::Load( cload );
				break;

			case CHUNKID_DEF_DIALOG_ENTRY:
				if (dialog_index < DIALOG_MAX) {
					DialogList[dialog_index++].Load (cload);
				}
				break;
								
			case CHUNKID_DEF_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TURN_RATE, TurnRate );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_JUMP_VELOCITY, JumpVelocity );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SKELETON_HEIGHT, SkeletonHeight );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SKELETON_WIDTH, SkeletonWidth );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_USE_INNATE_BEHAVIOR, UseInnateBehavior );
						//READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_USE_INNATE_CONVERSATIONS, UseInnateConversations );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_AGGRESSIVENESS, InnateAggressiveness );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_TAKE_COVER_PROB, InnateTakeCoverProbability );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_INNATE_IS_STATIONARY, InnateIsStationary );
						READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_FIRST_PERSON_HANDS, FirstPersonHands );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ORATOR_TYPE, OratorType );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_HUMAN_ANIM_OVERRIDE_DEF_ID, HumanAnimOverrideDefID );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_HUMAN_LOITER_COLLECTION_DEF_ID, HumanLoiterCollectionDefID );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DEATH_SOUND_PRESET, DeathSoundPresetID );		

						default:
							//Debug_Say(( "Unrecognized SoldierDef Variable chunkID\n" ));
							break;

					}
					cload.Close_Micro_Chunk();
				}
				break;

			default:
				Debug_Say(( "Unrecognized SoldierDef chunkID\n" ));
				break;

		}
		cload.Close_Chunk();
	}

	return true;
}

const PersistFactoryClass & SoldierGameObjDef::Get_Factory (void) const 
{ 
	return _SoldierGameObjDefPersistFactory; 
}


/*
** SolderGameObj
*/
SimplePersistFactoryClass<SoldierGameObj, CHUNKID_GAME_OBJECT_SOLDIER>	_SoldierGameObjPersistFactory;

const PersistFactoryClass & SoldierGameObj::Get_Factory (void) const 
{
	return _SoldierGameObjPersistFactory;
}

//------------------------------------------------------------------------------------
bool	SoldierGameObj::DisplayDebugBoxForGhostCollision = false;

//------------------------------------------------------------------------------------
SoldierGameObj::SoldierGameObj() :
	WeaponRenderModel( NULL ),
	BackWeaponRenderModel( NULL ),
	BackFlagRenderModel( NULL ),
	WeaponAnimControl( NULL ),
	TransitionCompletionData( NULL ),
	Vehicle( NULL ),
	LegFacing( 0 ),
	SyncLegs( false ),
	LastLegMode( 0 ),
	HeadLookDuration( 0 ),
	HeadRotation( 0,0,0 ),
	HeadLookTarget( 0,0,0 ),
	HeadLookAngle( 0,0,0 ),
	HeadLookAngleTimer( 0 ),
	InnateEnableBits( 0xFFFFFFFF ),
	InnateObserver( NULL ),
//	FlameTimer( 0 ),
	SpecialDamageMode(ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE),
	SpecialDamageTimer( 0 ),
	GenerateIdleFacialAnimTimer( 0 ),
	KeyRing( 0 ),	
	InFlyMode( false ),
	IsVisible( true ),
	CurrentSpeech( NULL ),
	AIState( AI_STATE_IDLE ),
	SpeechAnim( NULL ),
	HeadModel( NULL ),
	EmotIconModel( NULL ),
	EmotIconTimer( 0 ),
	LadderUpMask( false ),
	LadderDownMask( false ),
	SpecialDamageEffect( NULL ),
	HealingEffect( NULL ),
	ReloadingTilt(0),
	WaterWake(NULL),
	WeaponChanged( false )
{
	// All Humans need a HuamnAnimControl
	Set_Anim_Control( new HumanAnimControlClass );
	Set_App_Packet_Type(APPPACKETTYPE_SOLDIER);

	// create a water wake object
	WaterWake = SurfaceEffectsManager::Create_Persistant_Emitter();	
}

//------------------------------------------------------------------------------------
SoldierGameObj::~SoldierGameObj()
{
	if ( HealingEffect != NULL ) {
		Peek_Human_Phys()->Remove_Effect_From_Me( HealingEffect );
		REF_PTR_RELEASE( HealingEffect );
	}

	Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );
	Set_Emot_Icon (NULL, 0);

	REF_PTR_RELEASE( HeadModel );
	REF_PTR_RELEASE( SpeechAnim );
	REF_PTR_RELEASE( CurrentSpeech );

	if (CombatManager::I_Am_Server()) {
		CombatManager::On_Soldier_Death(this);
	}

	Set_Weapon_Model( NULL );

	if ( BackWeaponRenderModel != NULL ) {
		Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
		BackWeaponRenderModel->Release_Ref();
		BackWeaponRenderModel = NULL;
	}

	if ( BackFlagRenderModel != NULL ) {
		Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
		BackFlagRenderModel->Release_Ref();
		BackFlagRenderModel = NULL;
	}

	if ( WeaponAnimControl != NULL ) {
		delete WeaponAnimControl;
		WeaponAnimControl = NULL;
	}

	if (Vehicle != NULL) {
      // Remove myself from the vehicle !
		Vehicle->Remove_Occupant(this);
	}	

	COMBAT_SCENE->Remove_Object( Peek_Physical_Object() );

	if ( Is_Human_Controlled() ) {
		GameObjManager::Remove_Star( this );
	}

	Reset_RenderObjs();

	if (WaterWake != NULL) {
		SurfaceEffectsManager::Destroy_Persistant_Emitter( WaterWake );
		WaterWake = NULL;
	}

	return ;
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Init( void )
{
	Re_Init( Get_Definition() );
	return ;
}

void	SoldierGameObj::Init( const SoldierGameObjDef & definition )
{
	SmartGameObj::Init( definition );
	Copy_Settings( definition );
	return ;
}

void	SoldierGameObj::Copy_Settings( const SoldierGameObjDef & definition )
{
	HumanState.Init( Peek_Human_Phys() );
	HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() );  // Must set the anim control after the phys object
//HumanState.Set_Human_Anim_Override( "HAO Test" );
	if ( Get_Definition().HumanAnimOverrideDefID != 0 ) {
		HumanState.Set_Human_Anim_Override( Get_Definition().HumanAnimOverrideDefID );
	}

	if ( Get_Definition().HumanLoiterCollectionDefID != 0 ) {
		HumanState.Set_Human_Loiter_Collection( Get_Definition().HumanLoiterCollectionDefID );
	}

	Adjust_Skeleton( definition.SkeletonHeight, definition.SkeletonWidth );

	// All characters force their heads and hands to use the same LOD level as their body.
	RenderObjClass * model = Peek_Human_Phys()->Peek_Model();
	if (model != NULL) {
		model->Set_Sub_Objects_Match_LOD(true);
	}

	if (	InnateObserver == NULL && 
			Get_Definition().UseInnateBehavior && 
			Is_Controlled_By_Me () == false ) {
		InnateObserver = new SoldierObserverClass;
		Insert_Observer( InnateObserver );
	}

	//
	//	Copy the dialog entries from the definition
	//
	for( int index = 0; index < DIALOG_MAX; index ++ ) {
		DialogList[index] = definition.DialogList[index];
	}

	//
	//	Put the soldier in its own collision group 
	//
	Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );

	Prepare_Speech_Framework ();
	return ;
}


void	SoldierGameObj::Prepare_Speech_Framework( void )
{
	//
	//	Make sure we have an allocated animation to use for speech
	//
	if ( SpeechAnim == NULL) {		
		HeadModel = Find_Head_Model();

		//
		//	Dig out the skeleton name for the head model
		//
		if (HeadModel != NULL) {
			const HTreeClass *htree = HeadModel->Get_HTree ();
			if (htree != NULL) {
				StringClass skeleton_name = htree->Get_Name ();

				//
				//	Generate the animation
				//
				SpeechAnim = new DynamicSpeechAnimClass (skeleton_name);
				GenerateIdleFacialAnimTimer = 0;
			}
		}
	}

	return ;
}


void	SoldierGameObj::Re_Init( const SoldierGameObjDef & definition )
{
	if ( this == COMBAT_STAR ) {
		HUDClass::Force_Weapon_Chart_Update();
		WeaponViewClass::Reset();
	}

	//
	//	Remove the object from the world (just to be safe)
	//
	COMBAT_SCENE->Remove_Object( Peek_Physical_Object() );

	//
	//	Reset the weapon model
	//
	Set_Weapon_Model( NULL );

	if ( BackWeaponRenderModel != NULL ) {
		Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
		BackWeaponRenderModel->Release_Ref();
		BackWeaponRenderModel = NULL;
	}

	if ( BackFlagRenderModel != NULL ) {
		Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
		BackFlagRenderModel->Release_Ref();
		BackFlagRenderModel = NULL;
	}

	if ( WeaponAnimControl != NULL ) {
		delete WeaponAnimControl;
		WeaponAnimControl = NULL;
	}

	//
	//	Re-initialize the base class
	//
	SmartGameObj::Re_Init( definition );

	//
	//	Free some of the data we will be re-initializing
	//
	REF_PTR_RELEASE( HeadModel );
	REF_PTR_RELEASE( SpeechAnim );
	REF_PTR_RELEASE( CurrentSpeech );	

	HumanState.Reset();

	//
	//	Copy any internal settings from the definition
	//
	Copy_Settings( definition );

	//
	//	"Dirty" the object for networking
	//
	Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );

	// When class changes, update for new weapon
	if ( this == COMBAT_STAR ) {
		HUDClass::Reset();
	}

	return ;
}


//------------------------------------------------------------------------------------
const SoldierGameObjDef & SoldierGameObj::Get_Definition( void ) const
{
	return (const SoldierGameObjDef &)BaseGameObj::Get_Definition();
}

void SoldierGameObj::Set_Control_Owner(int control_owner)		
{ 
	if ( Is_Human_Controlled() ) {
		GameObjManager::Remove_Star( this );
	}
	SmartGameObj::Set_Control_Owner(control_owner);
	if ( Is_Human_Controlled() ) {
		GameObjManager::Add_Star( this );
	}
}



/*
** Soldier Save and Load
*/

enum	{
	CHUNKID_PARENT							=	909991656,
	CHUNKID_VARIABLES,
	CHUNKID_WEAPON_ANIM,
	CHUNKID_HUMAN_STATE,
	XXXCHUNKID_C4_TIMER,
	XXXCHUNKID_WEAPON_MODEL,
	CHUNKID_TRANSITION_COMPLETION_DATA,
	CHUNKID_DIALOG_ENTRY,
	CHUNKID_RENDER_OBJS,
	CHUNKID_SPECIAL_DAMAGE_DAMAGER, 

	MICROCHUNKID_DETONATE_C4			=	1,
	MICROCHUNKID_LEG_FACING,
	MICROCHUNKID_SYNC_LEGS,
	MICROCHUNKID_ANIMATION_NAME,
	MICROCHUNKID_VEHICLE,
	XXX_MICROCHUNKID_TRANSITION,
	XXX_MICROCHUNKID_FORCE_FACING,
	MICROCHUNKID_INNATE_ENABLE_BITS,
	MICROCHUNKID_INNATE_OBSERVER_PTR,
	MICROCHUNKID_LAST_LEG_MODE,
	MICROCHUNKID_HEAD_LOOK_DURATION,
	MICROCHUNKID_HEAD_ROTATION,
	MICROCHUNKID_LOOK_TARGET,
	XXXMICROCHUNKID_FLAME_TIMER,
	MICROCHUNKID_KEY_RING,
	MICROCHUNKID_AI_STATE,
	XXX_MICROCHUNKID_IN_CONVERSATION,
	MICROCHUNKID_LOOK_ANGLE,
	MICROCHUNKID_LOOK_ANGLE_TIMER,
	XXX_MICROCHUNKID_ACTIVE_CONVERSATION,
	MICROCHUNKID_WEAPON_MODEL,
	MICROCHUNKID_SPECIAL_DAMAGE_MODE, 
	MICROCHUNKID_SPECIAL_DAMAGE_TIMER,
	MICROCHUNKID_IS_USING_GHOST_COLLISION
};

//------------------------------------------------------------------------------------
bool	SoldierGameObj::Save( ChunkSaveClass & csave )
{
	csave.Begin_Chunk( CHUNKID_PARENT );
	SmartGameObj::Save( csave );
	csave.End_Chunk();

	//
	//	Save the dialog entries
	//
	for (int index = 0; index < DIALOG_MAX; index ++) {
		csave.Begin_Chunk( CHUNKID_DIALOG_ENTRY );
			DialogList[index].Save (csave);
		csave.End_Chunk();	
	}	

	csave.Begin_Chunk( CHUNKID_VARIABLES );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DETONATE_C4, DetonateC4 );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LEG_FACING, LegFacing );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SYNC_LEGS, SyncLegs );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_KEY_RING, KeyRing );		
//		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FORCE_FACING, ForceFacing );
		if ( Vehicle != NULL ) {
			WRITE_MICRO_CHUNK( csave, MICROCHUNKID_VEHICLE, Vehicle );
		}
		csave.Begin_Micro_Chunk( MICROCHUNKID_ANIMATION_NAME );
		char anim_string[80];
		strcpy( anim_string, AnimationName );
		csave.Write( anim_string, strlen( anim_string ) + 1);
		csave.End_Micro_Chunk();
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_INNATE_ENABLE_BITS, InnateEnableBits );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_INNATE_OBSERVER_PTR, InnateObserver );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LAST_LEG_MODE, LastLegMode );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HEAD_LOOK_DURATION, HeadLookDuration );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HEAD_ROTATION, HeadRotation );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_TARGET, HeadLookTarget );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_ANGLE, HeadLookAngle );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_LOOK_ANGLE_TIMER, HeadLookAngleTimer );		
//		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_FLAME_TIMER, FlameTimer );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPECIAL_DAMAGE_MODE, SpecialDamageMode );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SPECIAL_DAMAGE_TIMER, SpecialDamageTimer );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AI_STATE, AIState );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WEAPON_MODEL, WeaponRenderModel );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_USING_GHOST_COLLISION, IsUsingGhostCollision );
				
	csave.End_Chunk();

	if ( WeaponAnimControl ) {
		csave.Begin_Chunk( CHUNKID_WEAPON_ANIM );
		WeaponAnimControl->Save( csave );
		csave.End_Chunk();
	}

	csave.Begin_Chunk( CHUNKID_HUMAN_STATE );
	HumanState.Save( csave );
	csave.End_Chunk();

	if ( TransitionCompletionData ) {
		csave.Begin_Chunk( CHUNKID_TRANSITION_COMPLETION_DATA );
		TransitionCompletionData->Save( csave );
		csave.End_Chunk();
	}

	for ( int i = 0; i < RenderObjList.Count(); i++ ) {
		csave.Begin_Chunk( CHUNKID_RENDER_OBJS );
			csave.Begin_Chunk(RenderObjList[i]->Get_Factory().Chunk_ID());
				RenderObjList[i]->Get_Factory().Save( csave, RenderObjList[i] );
			csave.End_Chunk();
		csave.End_Chunk();
	}

	if ( SpecialDamageDamager.Get_Ptr() != NULL ) {
		csave.Begin_Chunk( CHUNKID_SPECIAL_DAMAGE_DAMAGER );
		SpecialDamageDamager.Save( csave );
		csave.End_Chunk();
	}

	return true;
}

//------------------------------------------------------------------------------------
bool	SoldierGameObj::Load( ChunkLoadClass &cload )
{
	char anim_string[80];
	int dialog_index = 0;

	WWASSERT( Vehicle == NULL );

	while (cload.Open_Chunk()) {
		switch(cload.Cur_Chunk_ID()) {

			case CHUNKID_PARENT:
				SmartGameObj::Load( cload );
				break;

			case CHUNKID_DIALOG_ENTRY:
				if (dialog_index < DIALOG_MAX) {
					DialogList[dialog_index++].Load (cload);
				}
				break;
								
			case CHUNKID_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DETONATE_C4, DetonateC4 );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_LEG_FACING, LegFacing );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_SYNC_LEGS, SyncLegs );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_VEHICLE, Vehicle );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_KEY_RING, KeyRing );
//						READ_MICRO_CHUNK( cload, MICROCHUNKID_FORCE_FACING, ForceFacing );

						case MICROCHUNKID_ANIMATION_NAME:
							cload.Read( anim_string, cload.Cur_Micro_Chunk_Length() );
							AnimationName = anim_string;
							break;

						case MICROCHUNKID_INNATE_OBSERVER_PTR:
							cload.Read( &InnateObserver, sizeof (InnateObserver) );
							if ( InnateObserver != NULL ) {
								REQUEST_POINTER_REMAP( (void **)&InnateObserver );
							}
							break;

						READ_MICRO_CHUNK( cload, MICROCHUNKID_INNATE_ENABLE_BITS, InnateEnableBits );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_LAST_LEG_MODE, LastLegMode );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_HEAD_LOOK_DURATION, HeadLookDuration );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_HEAD_ROTATION, HeadRotation );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_TARGET, HeadLookTarget );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_ANGLE, HeadLookAngle );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_LOOK_ANGLE_TIMER, HeadLookAngleTimer );		
//						READ_MICRO_CHUNK( cload, MICROCHUNKID_FLAME_TIMER, FlameTimer );		
						READ_MICRO_CHUNK( cload, MICROCHUNKID_SPECIAL_DAMAGE_MODE, SpecialDamageMode );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_SPECIAL_DAMAGE_TIMER, SpecialDamageTimer );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_AI_STATE, AIState );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_WEAPON_MODEL, WeaponRenderModel );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_USING_GHOST_COLLISION, IsUsingGhostCollision );

						default:
							Debug_Say(( "Unrecognized Soldier Variable chunkID\n" ));
							break;

					}
					cload.Close_Micro_Chunk();
				}
				if ( Vehicle != NULL ) {
					REQUEST_POINTER_REMAP( (void **)&Vehicle );
				}
				break;

			case CHUNKID_WEAPON_ANIM:
				Set_Weapon_Animation( NULL );	// Get the anim control built
				WeaponAnimControl->Load( cload );
				break;

			case CHUNKID_HUMAN_STATE:
				HumanState.Load( cload );
				break;

			case CHUNKID_RENDER_OBJS:
				cload.Open_Chunk();
				PersistFactoryClass * factory;
				factory = SaveLoadSystemClass::Find_Persist_Factory(cload.Cur_Chunk_ID());
				WWASSERT(factory != NULL);
				if (factory != NULL) {
					RenderObjClass * robj = (RenderObjClass *)factory->Load(cload);
					Add_RenderObj( robj );
					robj->Release_Ref();
				}
				cload.Close_Chunk();
				break;

			case CHUNKID_TRANSITION_COMPLETION_DATA:
				WWASSERT( TransitionCompletionData == NULL );
				TransitionCompletionData = new TransitionCompletionDataStruct();
				TransitionCompletionData->Load( cload );
				break;

			case CHUNKID_SPECIAL_DAMAGE_DAMAGER:
				SpecialDamageDamager.Load( cload );
				break;

			default:
				Debug_Say(( "Unrecognized SoldierGameObj chunkID\n" ));
				break;

		}
		cload.Close_Chunk();
	}

	if ( WeaponRenderModel != NULL ) {
		REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&WeaponRenderModel );
	}


	SaveLoadSystemClass::Register_Post_Load_Callback(this);

	return true;
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::On_Post_Load( void )
{
	HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() );  // Must set the anim control after the phys object

	SmartGameObj::On_Post_Load();

	if ( Peek_Model() && (WeaponRenderModel != NULL) ) {
		Peek_Model()->Add_Sub_Object_To_Bone( WeaponRenderModel, GUN_BONE_NAME );
	}

	Adjust_Skeleton( Get_Definition().SkeletonHeight, Get_Definition().SkeletonWidth );

	// Fixup BackWeaponRenderModel
	Update_Back_Gun();

	// Hide if in vehicle
	if ( Is_In_Vehicle() && Peek_Model() != NULL ) {
		Peek_Model()->Set_Hidden( true );
	}

	//
	//	Make sure we have an allocated animation to use for speech
	//
	Prepare_Speech_Framework ();
	return ;
}

//-----------------------------------------------------------------------------

CollisionReactionType SoldierGameObj::Collision_Occurred(const CollisionEventClass & event)
{
//	Debug_Say(( "Soldier Collision %p %p\n", this, event.OtherObj ));

	// Detect squishing...
	if ( event.OtherObj != NULL && event.OtherObj->Get_Observer() != NULL ) {
		PhysicalGameObj * obj = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_PhysicalGameObj();
		if ( obj && obj->As_VehicleGameObj() ) {
//			if ( !Is_Teammate( obj ) ) {		// never squish teammates
			if ( Is_Enemy( obj ) ) {		// only squish enemies
				Vector3 vel;
				if ( event.OtherObj->As_MoveablePhysClass() != NULL ) {
					event.OtherObj->As_MoveablePhysClass()->Get_Velocity( &vel );

					Vector3 my_pos;
					Vector3 vehicle_pos;
					Get_Position(&my_pos);
					obj->Get_Position(&vehicle_pos);

					// only squish if velocity is high enough, and velocity is towards the soldier
					if (	(vel.Length() > obj->As_VehicleGameObj()->Get_Squish_Velocity() ) &&
							(Vector3::Dot_Product(vel,my_pos - vehicle_pos) > 0.0f) )
					{	
						if ( HumanState.Get_State() != HumanStateClass::DEATH ) {
							// We need to pick a better warhead to damage with
							SmartGameObj * damager = obj->As_VehicleGameObj()->Get_Driver();
							if ( damager == NULL ) {
								damager = obj->As_VehicleGameObj();
							}
							OffenseObjectClass offense( 10000, 1, damager );
							Apply_Damage_Extended( offense, 10000.0f, vel, NULL ); 
							// Play the squish sound

							// Stats
							if (( obj->As_VehicleGameObj()->Get_Driver() != NULL ) && 
								( obj->As_VehicleGameObj()->Get_Driver()->Get_Player_Data() != NULL ) ) {
								obj->As_VehicleGameObj()->Get_Driver()->Get_Player_Data()->Stats_Add_Squish();
							}
						}
					}
				}
			}
		}
	}
	return COLLISION_REACTION_DEFAULT;
}

/*
**
*/
void	SoldierGameObj::Export_Creation( BitStreamClass &packet )
{
	//TSS091001
	//WWDEBUG_SAY((">>>>> SoldierGameObj::Export_Creation id %d\n", Get_ID()));

	SmartGameObj::Export_Creation( packet );
	return ;
}


/*
**
*/
void	SoldierGameObj::Import_Creation( BitStreamClass &packet )
{
	SmartGameObj::Import_Creation( packet );

	//TSS091001
	//WWDEBUG_SAY((">>>>> SoldierGameObj::Import_Creation id %d\n", Get_ID()));

	/*
	//
	//	Setup the speed settings for this soldier
	//
	float speed_factor = CombatManager::Get_Max_Speed_Pc( this ) / 100.0f;
	Set_Max_Speed( speed_factor * Get_Max_Speed() );
	*/
	return ;
}


/*
**
*/
void	SoldierGameObj::Export_Rare( BitStreamClass &packet )
{
	SmartGameObj::Export_Rare( packet );

	//
	//	Add our definition ID to the packet
	//
	uint32 definition_id = Get_Definition ().Get_ID ();
	packet.Add( definition_id );
}


/*
**
*/
void	SoldierGameObj::Import_Rare( BitStreamClass &packet )
{
	Set_Weapon_Model (NULL);
	// If we are changing to the same def, the above line made us lose our weapon model, and we won't get it back.
	// The next line makes us get it back.
 	Get_Weapon_Bag()->Force_Changed();

	SmartGameObj::Import_Rare( packet );

	//
	//	Read the definition ID from the packet
	//
	uint32 definition_id	= 0;
	packet.Get( definition_id );

	//
	//	Did our definition change?
	//
	if (definition_id != Get_Definition ().Get_ID ()) {
		DefinitionClass *definition = DefinitionMgrClass::Find_Definition (definition_id);

		//
		//	Did we find the right definition?
		//		
		if (definition != NULL && definition->Get_Class_ID() == CLASSID_GAME_OBJECT_DEF_SOLDIER) {
			SoldierGameObjDef *soldier_def = reinterpret_cast<SoldierGameObjDef *> (definition);
			
			//
			//	Re-initialize ourselves
			//
			Re_Init (*soldier_def);
		}		
	}

	return ;
}

/*
**
*/
void	SoldierGameObj::Export_Occasional( BitStreamClass &packet )
{
	SmartGameObj::Export_Occasional( packet );

	//
	// What weapon is being held?
	//
#if 0 //(gth) moving this back to Frequent to fix the game, re-optimize later...
	WeaponClass * p_weapon = Get_Weapon();
   bool has_weapon = (p_weapon != NULL);
   packet.Add(has_weapon);
   if (has_weapon) {
		packet.Add(p_weapon->Get_ID());
		packet.Add(p_weapon->Get_Total_Rounds());
   }
#endif

	WWASSERT(WeaponBag != NULL);
	WeaponBag->Export_Weapon_List(packet);

	//packet.Add(CtfTeamFlag, BITPACK_CTF_TEAM_FLAG);
}


/*
**
*/
void	SoldierGameObj::Import_Occasional( BitStreamClass &packet )
{
   WWASSERT(CombatManager::I_Am_Only_Client());

	SmartGameObj::Import_Occasional( packet );

	//
	// Held weapon
	//
#if 0 // (gth) moving back to "Frequent" to fix the game, re-optimize later?
	bool has_weapon = packet.Get(has_weapon);
   if (has_weapon) {
      WWASSERT(!packet.Is_Flushed());
	
		int weapon_id = packet.Get(weapon_id);
		int rounds = packet.Get(rounds);
		if ((Get_Weapon() == NULL) || (weapon_id != Get_Weapon()->Get_ID())) {
			WeaponBag->Select_Weapon_ID(weapon_id);
		}

		if (Get_Weapon() != NULL) {
			// If this weapon is currently being fired, ignore the server rounds count packet
			// This should help the jittery rounds count
			if ( !Get_Weapon()->Is_Triggered() ) {
				Get_Weapon()->Set_Total_Rounds( rounds );
			}
		}

   } else {
		if (Get_Weapon() != NULL) {
			Get_Weapon_Bag()->Deselect();
		}
	}
#endif

	//
	// Weapon list
	//
	WWASSERT(WeaponBag != NULL);
	WeaponBag->Import_Weapon_List(packet);


	/*
	//
	// Flag pickup/drop
	//
	int ctf_team_flag = packet.Get(ctf_team_flag, BITPACK_CTF_TEAM_FLAG);
	if (CtfTeamFlag != ctf_team_flag) {
		CombatManager::Change_Flag_Status(this, ctf_team_flag);
	}
	*/
}


/*
**
*/
void SoldierGameObj::Export_Frequent(BitStreamClass & packet)
{
	/**/
	//TSS101601
	bool in_vehicle = (Get_State() == HumanStateClass::IN_VEHICLE);
	packet.Add(in_vehicle);
	if (in_vehicle)
	{
		// Just do the control info
		SmartGameObj::Export_Frequent(packet);
		return;
	}
	/**/

	//
	// What weapon is being held?
	// (gth) moved this back into frequent to fix the game, re-optimize later
	//
	WeaponClass * p_weapon = Get_Weapon();
   bool has_weapon = (p_weapon != NULL);
   packet.Add(has_weapon);
   if (has_weapon) {
		packet.Add(p_weapon->Get_ID());
		packet.Add(p_weapon->Get_Total_Rounds());
	}

	//
	// Export Position and state
	//
	Vector3 position;
	Get_Position(&position);


#ifdef MULTIPLAYERDEMO
	//
	// Mix up the packet order to make demo/non-demo code more incompatible.
	//
	packet.Add(position.Y, BITPACK_WORLD_POSITION_Y);
	packet.Add(position.Z, BITPACK_WORLD_POSITION_Z);
	packet.Add(position.X, BITPACK_WORLD_POSITION_X);
#else
	packet.Add(position.X, BITPACK_WORLD_POSITION_X);
	packet.Add(position.Y, BITPACK_WORLD_POSITION_Y);
	packet.Add(position.Z, BITPACK_WORLD_POSITION_Z);
#endif

	
	packet.Add((int) HumanState.Get_State(), BITPACK_HUMAN_STATE);
	packet.Add(HumanState.Get_Sub_State(), BITPACK_HUMAN_SUB_STATE);


	if (HumanState.Get_State() == HumanStateClass::AIRBORNE) {
		// velocity is only needed for jumping
		Vector3 velocity;
		Get_Velocity(velocity);
		packet.Add(velocity.X);
		packet.Add(velocity.Y);
		packet.Add(velocity.Z);
	}

	if ((Get_State() == HumanStateClass::TRANSITION) || 
		 (Get_State() == HumanStateClass::ANIMATION) || 
		 (Get_State() == HumanStateClass::IN_VEHICLE)) {
      packet.Add_Terminated_String(AnimationName);
//		Debug_Say(( "In Transition %s\n", TransitionName ));
	}

	bool	is_special_damage = SpecialDamageMode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE;
	packet.Add(is_special_damage);
	if ( is_special_damage ) {
		packet.Add( SpecialDamageMode );
	}

   SmartGameObj::Export_Frequent(packet);
}

/*
**
*/
void	SoldierGameObj::Import_Frequent( BitStreamClass & packet )
{
   WWASSERT(CombatManager::I_Am_Only_Client());

	/**/
	//TSS101601
	bool in_vehicle = packet.Get(in_vehicle);
	if (in_vehicle)
	{
		// Just get control info
		SmartGameObj::Import_Frequent(packet);
		return;
	}
	/**/

	//
	// What weapon is being held? 
	// (gth) moved this back into frequent to fix the game, re-optimize later
	//
	bool has_weapon = packet.Get(has_weapon);
   if (has_weapon) {
      WWASSERT(!packet.Is_Flushed());
	
		int weapon_id = packet.Get(weapon_id);
		int rounds = packet.Get(rounds);
		if ((Get_Weapon() == NULL) || (weapon_id != Get_Weapon()->Get_ID())) {
			WeaponBag->Select_Weapon_ID(weapon_id);
		}

		if (Get_Weapon() != NULL) {
			// If this weapon is currently being fired, ignore the server rounds count packet
			// This should help the jittery rounds count
			if ( !Get_Weapon()->Is_Triggered() ) {
				Get_Weapon()->Set_Total_Rounds( rounds );
			}
		}

   } else {
		if (Get_Weapon() != NULL) {
			Get_Weapon_Bag()->Deselect();
		}
	}

	//
	// Position
	//
	Vector3 sc_position;


#ifdef MULTIPLAYERDEMO
	//
	// Mix up the packet order to make demo/non-demo code more incompatible.
	//
	packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
	packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
	packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
#else
	packet.Get(sc_position.X, BITPACK_WORLD_POSITION_X);
	packet.Get(sc_position.Y, BITPACK_WORLD_POSITION_Y);
	packet.Get(sc_position.Z, BITPACK_WORLD_POSITION_Z);
#endif


	// Bump Z up to the top of the possible values due to packing
	// we assume the max error is half of the resolution
	float max_error = cEncoderList::Get_Encoder_Type_Entry( BITPACK_WORLD_POSITION_Z ).Get_Resolution() / 2.0f;
	sc_position.Z += max_error;

	Interpret_Sc_Position_Data(sc_position);

	//
	// State and substate
	//
	int h_state = packet.Get(h_state, BITPACK_HUMAN_STATE);
	HumanStateClass::HumanStateType state = 
		(HumanStateClass::HumanStateType) h_state;
	int sub_state = packet.Get(sub_state, BITPACK_HUMAN_SUB_STATE);

	//
	// Velocity (if airborne)
	//
	Vector3 velocity;
	if (state == HumanStateClass::AIRBORNE) {
		packet.Get(velocity.X);
		packet.Get(velocity.Y);
		packet.Get(velocity.Z);
	}

	if (HumanState.Is_Locked()) {
      packet.Flush();
		return;
	}

#if 0
	if ( Get_State() == HumanStateClass::TRANSITION ) {
		Debug_Say(( "Ignoring Transitioning!\n" ));
      packet.Flush();
		return;
	}
#endif

	char trans_name[80] = "";
	if ( ( state == HumanStateClass::TRANSITION ) || 
		  ( state == HumanStateClass::ANIMATION ) || 
		  ( state == HumanStateClass::IN_VEHICLE ) ) {
      packet.Get_Terminated_String(trans_name, sizeof(trans_name));
	}

	Interpret_Sc_State_Data(state, sub_state, trans_name, velocity, sc_position);

	bool	is_special_damage;
	packet.Get(is_special_damage);
	int mode = ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE;
	if ( is_special_damage ) {
		packet.Get(mode);
	}
	if ( mode != SpecialDamageMode ) {
		Set_Special_Damage_Mode( (ArmorWarheadManager::SpecialDamageType)mode );
	}

   if (Get_State() == HumanStateClass::DIVE)	{
      packet.Flush();
	} else {
		SmartGameObj::Import_Frequent(packet);
	}

   WWASSERT(packet.Is_Flushed());
}

//-----------------------------------------------------------------------------
void SoldierGameObj::Import_State_Cs(BitStreamClass & packet)
{
	bool	is_sniping = packet.Get( is_sniping );
	if ( is_sniping != Is_Sniping() ) {
		// Take sniping state from client
//		Debug_Say(( "Fixing Sniper State\n" ));
		HumanState.Toggle_State_Flag( HumanStateClass::SNIPING_FLAG );
	}

	bool	checking = packet.Get( checking );
	if ( checking ) {
		int check = packet.Get( check );
		if ( check != Check() ) {
			// React to cheating
			if ( Get_Player_Data() ) {
				Get_Player_Data()->Inc_Punish_Timer( TimeManager::Get_Frame_Seconds() )	;
			}
		}
	}

	SmartGameObj::Import_State_Cs(packet);
}

//-----------------------------------------------------------------------------
void SoldierGameObj::Export_State_Cs(BitStreamClass & packet)
{
	bool	is_sniping = Is_Sniping();
	packet.Add( is_sniping );

	bool checking = Control.Get_Boolean( ControlClass::BOOLEAN_ACTION );
	packet.Add( checking );
	if ( checking ) {
		int check = Check();
		packet.Add( check );
	}

	SmartGameObj::Export_State_Cs(packet);
}

//-----------------------------------------------------------------------------
int SoldierGameObj::Check(void)
{
Matrix3D tm(1);
#define	UNINITIALLIZED_CRC	0x78629140
	static int crc = UNINITIALLIZED_CRC;
tm.Rotate_X( 1.4f );
	if ( crc == UNINITIALLIZED_CRC ) {
		char * filelist[] = {
		"laif`wp-gga",								//"objects.ddb",              
		"bqnlq-jmj",								//"armor.ini",                
		"almfp-jmj",								//"bones.ini",                
		"pvqeb`ffeef`wp-jmj",					//"surfaceeffects.ini",       
		"`bnfqbp-jmj",								//"cameras.ini",              
		"`\\mlg\\nd\\o3-t0g",						//"c_nod_mg_l0.w3d",          
		"`\\mlg\\qh\\o3-t0g",						//"c_nod_rk_l0.w3d",          
		"`\\mlg\\eo\\o3-t0g",						//"c_nod_fl_l0.w3d",          
		"`\\mlg\\fm\\o3-t0g",						//"c_nod_en_l0.w3d",          
		"`\\mlg\\ndl\\o3-t0g",						//"c_nod_mgo_l0.w3d",         
		"`\\mlg\\qhl\\o3-t0g",						//"c_nod_rko_l0.w3d",         
		"`\\mlg\\`kfnw\\o3-t0g",					//"c_nod_chemt_l0.w3d",       
		"`\\mlg\\pmjsfq\\o3-t0g",					//"c_nod_sniper_l0.w3d",      
		"`\\mlg\\qplog\\o3-t0g",					//"c_nod_rsold_l0.w3d",       
		"`\\mlg\\pwowk\\o3-t0g",					//"c_nod_stlth_l0.w3d",       
		"`\\mlg\\pbhv\\o3-t0g",						//"c_nod_saku_l0.w3d",        
		"`\\mlg\\pbhv1\\o3-t0g",					//"c_nod_saku2_l0.w3d",       
		"`\\mlg\\qbu\\o3-t0g",						//"c_nod_rav_l0.w3d",         
		"`\\mlg\\nqbu\\o3-t0g",						//"c_nod_mrav_l0.w3d",        
		"`\\mlg\\ngy\\o3-t0g",						//"c_nod_mdz_l0.w3d",         
		"`\\mlg\\ngy1\\o3-t0g",						//"c_nod_mdz2_l0.w3d",        
		"`\\mlg\\w`\\o3-t0g",						//"c_nod_tc_l0.w3d",          
		"`\\mlg\\nvwbmw\\o3-t0g",					//"c_nod_mutant_l0.w3d",      
		"`\\mlg\\npog\\o3-t0g",						//"c_nod_msld_l0.w3d",        
		"`\\mlg\\pplog\\o3-t0g",					//"c_nod_ssold_l0.w3d",       
		"`\\mlg\\sfwn\\o3-t0g",						//"c_nod_petm_l0.w3d",        
		"`\\mlg\\hbmf\\o3-t0g",						//"c_nod_kane_l0.w3d",        
		"`\\dgj\\nd\\o3-t0g",						//"c_gdi_mg_l0.w3d",          
		"`\\dgj\\qh\\o3-t0g",						//"c_gdi_rk_l0.w3d",          
		"`\\dgj\\dq\\o3-t0g",						//"c_gdi_gr_l0.w3d",          
		"`\\dgj\\fm\\o3-t0g",						//"c_gdi_en_l0.w3d",          
		"`\\dgj\\ndl\\o3-t0g",						//"c_gdi_mgo_l0.w3d",         
		"`\\dgj\\qhl\\o3-t0g",						//"c_gdi_rko_l0.w3d",         
		"`\\dgj\\pzg\\o3-t0g",						//"c_gdi_syd_l0.w3d",         
		"`\\dgj\\gfbg\\o3-t0g",						//"c_gdi_dead_l0.w3d",        
		"`\\dgj\\dvm\\o3-t0g",						//"c_gdi_gun_l0.w3d",         
		"`\\dgj\\sw`k\\o3-t0g",						//"c_gdi_ptch_l0.w3d",        
		"`\\kbul`\\o3-t0g",							//"c_havoc_l0.w3d",           
		"`\\kbul`m\\o3-t0g",						//"c_havocn_l0.w3d",          
		"`\\kbul`t\\o3-t0g",						//"c_havocw_l0.w3d",          
		"`\\kbul`g\\o3-t0g",						//"c_havocd_l0.w3d",          
		"`\\dgj\\pzg\\o3-t0g",						//"c_gdi_syd_l0.w3d",         
		"`\\dgj\\pzg1\\o3-t0g",						//"c_gdi_syd2_l0.w3d",        
		"`\\dgj\\nlaj\\o3-t0g",						//"c_gdi_mobi_l0.w3d",        
		"`\\dgj\\klwt\\o3-t0g",						//"c_gdi_hotw_l0.w3d",        
		"`\\dgj\\ow\\o3-t0g",						//"c_gdi_lt_l0.w3d",          
		"`\\mlg\\sfwq\\o3-t0g",						//"c_nod_petr_l0.w3d",        
		"`\\oldbm\\o3-t0g",							//"c_logan_l0.w3d",           
		"`\\dgj\\ol`hf\\o3-t0g",					//"c_gdi_locke_l0.w3d",       
		"u\\mlg\\avddz-t0g",						//"v_nod_buggy.w3d",          
		"u\\mlg\\bs`\\n-t0g",						//"v_nod_apc_m.w3d",          
		"u\\mlg\\bqwoqz-t0g",						//"v_nod_artlry.w3d",         
		"u\\mlg\\eobnf-t0g",						//"v_nod_flame.w3d",          
		"u\\mlg\\owbmh-t0g",						//"v_nod_ltank.w3d",          
		"u\\mlg\\pwowk-t0g",						//"v_nod_stlth.w3d",          
		"u\\mlg\\wqmpsw\\n-t0g",					//"v_nod_trnspt_m.w3d",       
		"u\\mlg\\bsb`kf\\n-t0g",					//"v_nod_apache_m.w3d",       
		"u\\`kbnfoflm-t0g",						//"v_chameleon.w3d",          
		"u\\dgj\\kvnuff-t0g",						//"v_gdi_humvee.w3d",         
		"u\\dgj\\bs`\\n-t0g",						//"v_gdi_apc_m.w3d",          
		"u\\dgj\\nqop-t0g",							//"v_gdi_mrls.w3d",           
		"u\\dgj\\nfgwmh-t0g",						//"v_gdi_medtnk.w3d",         
		"u\\dgj\\nbnnwk-t0g",						//"v_gdi_mammth.w3d",         
		"u\\sj`hvs32-t0g",							//"v_pickup01.w3d",           
		"u\\pfgbm32-t0g",							//"v_sedan01.w3d",            
		"u\\dgj\\lq`b\\n-t0g",						//"v_gdi_orca_m.w3d",         
		"u\\dgj\\wqmpsw\\n-t0g",					//"v_gdi_trnspt_m.w3d",       

		};
#define	NUM_CRC_FILES	(sizeof(	filelist ) / sizeof( filelist[0] ) )
		crc = 0;
tm.Rotate_Y( 2.8f );
		for ( int i = 0; i < NUM_CRC_FILES; i++ ) {
			StringClass name = filelist[i];
			// Obfuscate name
			char * n = &name[0];
			while ( *n ) *n++ ^= 0x3;
tm.Rotate_Z( 3.6f );
//			Debug_Say(( "		\"%s\",\n", name ));
			FileClass * file = _TheFileFactory->Get_File( name );
tm.Pre_Rotate_X( 0.3f );
			if ( file && file->Is_Available() ) {
				int size = file->Size();
tm.Pre_Rotate_Y( 0.4f );
				file->Open();
tm.Pre_Rotate_Z( 0.5f );
				while ( size > 0 ) {
					unsigned char buffer[ 4096 ];
					int amount = min( (int)size, (int)sizeof(buffer) );
tm.Translate_X( 3.1f );
					amount = file->Read( buffer, amount );
tm.Translate_Y( 4.6f );
					crc = CRC_Memory( buffer, amount, crc );
tm.Translate_Z( 8.2f );
					size -= amount;
				}
				file->Close();
			} else {
//				Debug_Say(( "***%s not found\n", name ));
			}

tm.Rotate_X( 1.4f );
		}
	}
	return crc;
}
//-----------------------------------------------------------------------------


/*
**
*/
bool	_UseLatencyInterpret = true;

//-----------------------------------------------------------------------------
void SoldierGameObj::Interpret_Sc_Position_Data( const Vector3 & sc_position)
{
	Vector3 position = sc_position;

	// Only use the latency code for the star
	if ( _UseLatencyInterpret && (this == COMBAT_STAR) ) {
		Peek_Human_Phys()->Network_Latency_State_Update(sc_position,Vector3(0,0,0));
		return;
	}

   WWASSERT(CombatManager::I_Am_Only_Client());

	if (Get_State() == HumanStateClass::TRANSITION) {
		//
		// Pop
		//
//		Set_Position(sc_position);
		Peek_Human_Phys()->Network_State_Update(sc_position,Vector3(0,0,0));
	} else {
		Vector3 current_position;
		Get_Position(&current_position);
		Vector3 pos_error = current_position - sc_position;

		//TSS081501
		if (Is_In_Elevator()) {
			position.Z = current_position.Z;
		}

//		Set_Position(sc_position);
		Peek_Human_Phys()->Network_State_Update(position,Vector3(0,0,0));
	}
}

//-----------------------------------------------------------------------------
void SoldierGameObj::Interpret_Sc_State_Data(
	HumanStateClass::HumanStateType state, int sub_state, 
	LPCSTR trans_name, const Vector3 & velocity, const Vector3 & sc_position)
{
   WWASSERT(CombatManager::I_Am_Only_Client());

	if ( ( Get_State() != state ) || (	Get_Sub_State() != sub_state ) ){
		if ( ( Get_State() == HumanStateClass::TRANSITION ) &&		// If in TRANSITION, don't switch to TRANSITION_COMPLETE
			  ( state == HumanStateClass::TRANSITION_COMPLETE ) ) {
			// do nothing
		} else if ( ( Get_State() == HumanStateClass::UPRIGHT ) && // If UPRIGHT, don't switch to LAND
						( state == HumanStateClass::LAND ) ) {	
			// do nothing
		} else {
			if (Is_Controlled_By_Me() &&
				 ((( Get_State() == HumanStateClass::UPRIGHT ) || ( Get_State() == HumanStateClass::AIRBORNE ) || ( Get_State() == HumanStateClass::LAND )) &&
				  (( state       == HumanStateClass::UPRIGHT ) || ( state       == HumanStateClass::AIRBORNE ) || ( state       == HumanStateClass::LAND )) )) {
				// do nothing
//			} else if (Is_Controlled_By_Me() == false) {
			} else {
				// take the new state
				if (state == HumanStateClass::TRANSITION) {
					Start_Transition_Animation( trans_name, NULL );
					AnimationName = trans_name;
				} else if (state == HumanStateClass::ANIMATION) {
					AnimationName = trans_name;
					HumanState.Start_Scripted_Animation( AnimationName, true, false );
				} else if (state == HumanStateClass::IN_VEHICLE) {
					HumanState.Force_Animation( trans_name, false );
					AnimationName = trans_name;
				}

				//TSS2001
//				if (state != HumanStateClass::IN_VEHICLE) {
					HumanState.Set_State( state, sub_state );
//				}
			}
		}
	}
		
	if (( state == HumanStateClass::AIRBORNE ) && ( this != COMBAT_STAR ) ) {
		Peek_Human_Phys()->Set_In_Contact( false );
		Peek_Human_Phys()->Set_Velocity( velocity );
		Peek_Human_Phys()->Set_Position( sc_position );
	}
}

//-----------------------------------------------------------------------------
int SoldierGameObj::Tally_Vis_Visible_Soldiers( void )
{
	int retcode = -1;

	Vector3 position;
	Get_Position(&position);
	position.Z += 1.5f;

	WWASSERT(COMBAT_SCENE != NULL);
	VisTableClass * pvs = COMBAT_SCENE->Get_Vis_Table(position);
	
	if (pvs != NULL) {

		retcode = 0;

		for (
			SLNode<SmartGameObj> * smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); 
			smart_objnode != NULL; 
			smart_objnode = smart_objnode->Next()) {

			SoldierGameObj * p_soldier = smart_objnode->Data()->As_SoldierGameObj();
			if (p_soldier != NULL) {

				PhysClass * phys_obj	= p_soldier->Peek_Physical_Object();
				if (phys_obj != NULL && pvs->Get_Bit(phys_obj->Get_Vis_Object_ID())) {
					retcode++;
				}
			}
		}
	}

	return retcode;
}

//-----------------------------------------------------------------------------
bool SoldierGameObj::Is_In_Elevator( void )
{
	HumanPhysClass	* p_human_phys = Peek_Human_Phys();

	return		
		p_human_phys != NULL && 
		p_human_phys->Peek_Carrier_Object() != NULL && 
		p_human_phys->Peek_Carrier_Object()->As_ElevatorPhysClass() != NULL;
}

//-----------------------------------------------------------------------------
float SoldierGameObj::Get_Max_Speed( void )
{
	return Peek_Human_Phys()->Get_Normalized_Speed();
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Set_Max_Speed( float speed )
{
	Peek_Human_Phys()->Set_Normalized_Speed( speed );
}

//------------------------------------------------------------------------------------
float	SoldierGameObj::Get_Turn_Rate( void )
{
	return Get_Definition().TurnRate;
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Generate_Control( void )
{
	switch( Get_State() ) {
		case HumanStateClass::DEATH:
//		case HumanStateClass::WOUNDED:
//		case HumanStateClass::DESTROY:
			Clear_Control();
			break;

		default:
			SmartGameObj::Generate_Control();
			break;
	}
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Apply_Control( void )
{
	// if gameplay not permitted, skip
	if ( !CombatManager::Is_Gameplay_Permitted() ) {
		Clear_Control();
		Controller.Reset();
		return;
	}

#ifdef WWDEBUG
	// Change the control before applying
	if ( IS_SOLOPLAY && Is_Human_Controlled() && Input::Get_State( INPUT_FUNCTION_DEBUG_RAPID_MOVE ) ) {
		if ( InFlyMode ) {
			Control.Set_Analog( ControlClass::ANALOG_MOVE_UP,
				Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) );
			Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );
		} else {
			Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD,
				Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) * 10 );
		}
	}
#endif

	if ( Get_State() == HumanStateClass::IN_VEHICLE ) {

		// Handle Vehicle Gunner Toggle
		if ( Get_Vehicle() && Get_Vehicle()->Get_Driver() == this ) {
			if ( Control.Get_Boolean( ControlClass::BOOLEAN_VEHICLE_TOGGLE_GUNNER ) ) {
				Get_Vehicle()->Toggle_Driver_Is_Gunner();
			}
		}		

		if (	CombatManager::I_Am_Server() )		// Server authoritative
//		if (	CombatManager::I_Am_Server() ||
//				(Is_Controlled_By_Me() && TransitionCompletionData == NULL) )
		{
			if ( Control.Get_Boolean( ControlClass::BOOLEAN_ACTION ) ) {
				TransitionManager::Check( this, true );
			}
		}
		return;
	}

	if ( ( Get_State() == HumanStateClass::TRANSITION ) || 
		  ( Get_State() == HumanStateClass::ANIMATION ) ||
		  ( Get_State() == HumanStateClass::DESTROY ) ||
		  ( Get_State() == HumanStateClass::DEATH ) ||
		  ( Get_State() == HumanStateClass::ON_FIRE ) ||
		  ( Get_State() == HumanStateClass::ON_CHEM ) ||
		  ( Get_State() == HumanStateClass::ON_CNC_FIRE ) ||
		  ( Get_State() == HumanStateClass::ON_CNC_CHEM ) ||
		  ( Get_State() == HumanStateClass::ON_ELECTRIC ) ) {

		// Force exit of the corpse mode	
		if ( Get_State() == HumanStateClass::DESTROY && Is_Human_Controlled() ) {
			if ( Control.Get_Boolean( ControlClass::BOOLEAN_WEAPON_FIRE_PRIMARY ) ) {
				HumanState.Set_State_Timer( 1000 );	// Done!
			}
		}

//		Debug_Say(( "No Control in transition\n" ));
		Clear_Control();
//		Controller.Reset();
//		return;
	}

	if ( Get_State() == HumanStateClass::DIVE ) {

		Clear_Control();

		SmartGameObj::Apply_Control();

		Vector3	forced_move(0,0,0);
		int sub_state = HumanState.Get_Sub_State();
		if ( sub_state & HumanStateClass::SUB_STATE_FORWARD )	forced_move = Vector3( 0.5f,0,0 );
		if ( sub_state & HumanStateClass::SUB_STATE_BACKWARD )	forced_move = Vector3( -0.5f,0,0 );
		if ( sub_state & HumanStateClass::SUB_STATE_LEFT )		forced_move = Vector3( 0,0.5f,0 );
		if ( sub_state & HumanStateClass::SUB_STATE_RIGHT )		forced_move = Vector3( 0,-0.5f,0 );

		Controller.Set_Move_Forward(	forced_move.X );
		Controller.Set_Move_Left(		forced_move.Y );
		Controller.Set_Move_Up(			forced_move.Z );
		return;
	}

#define	CLIMB_SCALE			0.3f

	// Skip all this if control is clear?!?!?!
	if ( Get_State() == HumanStateClass::LADDER ) {
		// Convert Forward motion to Up motion

		float up_down = CLIMB_SCALE * Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD );

		if ( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) == 0 ) {
			LadderUpMask = false;
			LadderDownMask = false;
		}

		if ( LadderUpMask ) {
			up_down = MIN( up_down, 0 );
		}
		if ( LadderDownMask ) {
			up_down = MAX( up_down, 0 );
		}

		// when on ladder, clear all velocity
		Control.Set_Analog( ControlClass::ANALOG_MOVE_UP, up_down );


		// Can't clear this, or we wont be able to climb next net frame!!!
//		Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );
		Control.Set_Analog( ControlClass::ANALOG_MOVE_LEFT, 0 );
		Control.Set_Analog( ControlClass::ANALOG_TURN_LEFT, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_JUMP, 0 );
	} else {
		LadderUpMask = false;
		LadderDownMask = false;

		// no move up

		if ( !InFlyMode ) {
			Control.Set_Analog( ControlClass::ANALOG_MOVE_UP, 0 );
		}
	}

#if 0
	if ( Control.Get_Boolean( ControlClass::BOOLEAN_CROUCH ) ) {
		if ( HumanState.Is_Sub_State_Adjustable() ) {
//			HumanState.Set_Sub_State( Get_Sub_State() ^ HumanStateClass::CROUCHED );
			HumanState.Toggle_State_Flag( HumanStateClass::CROUCHED_FLAG );

			if ( this == COMBAT_STAR ) {
				Vector3 pos;
				Get_Position( &pos );
				if ( Is_Crouched() ) {
					DIAG_LOG(( "COEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
				} else {
					DIAG_LOG(( "COEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
				}
			}
		}
	}
#else
	if ( HumanState.Is_Sub_State_Adjustable() ) {

		bool new_state = Control.Get_Boolean( ControlClass::BOOLEAN_CROUCH );
		bool old_state = HumanState.Get_State_Flag( HumanStateClass::CROUCHED_FLAG );

		if ( new_state != old_state ) {
	
			HumanState.Toggle_State_Flag( HumanStateClass::CROUCHED_FLAG );

			if ( this == COMBAT_STAR ) {
				Vector3 pos;
				Get_Position( &pos );
				if ( Is_Crouched() ) {
					DIAG_LOG(( "COEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
				} else {
					DIAG_LOG(( "COEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
				}
			}
		}
	}
#endif

	/*
	if (	CombatManager::I_Am_Server() && 
			Control.Get_Boolean(ControlClass::BOOLEAN_DROP_FLAG)	) {
		CombatManager::Soldier_Drops_Flag(this);
	}
	*/

	DetonateC4 = false;
	if ( Get_Weapon() && Control.Get_Boolean( ControlClass::BOOLEAN_WEAPON_USE ) ) {
		if ( Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4 ) {
			DetonateC4 = true;
		}

		if ( Get_Weapon()->Get_Can_Snipe() ) {
			if ( HumanState.Is_Sub_State_Adjustable() ) {
				HumanState.Toggle_State_Flag( HumanStateClass::SNIPING_FLAG );
			}
		}
	}

#if 0
	if ( Is_Sniping() ) {

		// No motion for snipers (I don't know if this should be in Soldier.cpp)
		// (But then, Sniper mode means nothing for plain soldiers, only for players)
		//	Don't clear the move forward, just don't allow the controller to have
		//	any move forward.  This was not alowing sniper zoom
//		Control.Set_Analog( ControlClass::ANALOG_MOVE_FORWARD, 0 );	 
		Control.Set_Analog( ControlClass::ANALOG_MOVE_LEFT, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_JUMP, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_CROUCH, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_LEFT, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_ACTION, 0 );		// stop ladder
	}
#endif

	if ( Is_Sniping() || Is_On_Ladder() ) {
		// No diving for snipers or on ladders
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_LEFT, 0 );
		Control.Set_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT, 0 );
	}

	bool dove = false;

	// Can only dive if upright
	if ( Get_State() == HumanStateClass::UPRIGHT ) {
		if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_FORWARD ) ) {
			HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_FORWARD );
			dove = true;
		}

		if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_BACKWARD ) ) {
			HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_BACKWARD );
			dove = true;
		}

		if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_LEFT ) ) {
			HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_LEFT );
			dove = true;
		}

		if ( Control.Get_Boolean( ControlClass::BOOLEAN_DIVE_RIGHT ) ) {
			HumanState.Set_State( HumanStateClass::DIVE, HumanStateClass::SUB_STATE_RIGHT );
			dove = true;
		}

		if ( dove && this == COMBAT_STAR ) {
			Vector3 pos;
			Get_Position( &pos );
			DefenseObjectClass * defense = Get_Defense_Object();
			const char * weapon_name = "";
			int ammo = 0;
			if ( Get_Weapon() != NULL )  {
				weapon_name = Get_Weapon()->Get_Definition()->Get_Name();
				ammo = Get_Weapon()->Get_Total_Rounds();
			}
			DIAG_LOG(( "ROUS", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), weapon_name, ammo ));
		}
	}

	// Handle facing 
	float amount = Control.Get_Analog( ControlClass::ANALOG_TURN_LEFT );
	if ( COMBAT_STAR == this ) {	// Star gets turning from the camera
		amount = 0;
	}
	if ( amount != 0 ) {
		float heading = Peek_Human_Phys()->Get_Heading();
		heading += amount * Get_Turn_Rate() * TimeManager::Get_Frame_Seconds();
		heading = WWMath::Wrap( heading, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
		Peek_Human_Phys()->Set_Heading(heading);
		Control.Set_Analog(ControlClass::ANALOG_TURN_LEFT,0.0f);
		if ( !Is_Human_Controlled() ) {	// human players don't use turn anims
  			HumanState.Set_Turn_Velocity( amount );	// play the leg turning anim
		}
	}

	// Let parent class handle the rest
	SmartGameObj::Apply_Control();

#if 01
	// Have to Move_Up after after Apply control, but BOOLEAN_JUMP is cleared
	if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
		if ( Get_State() != HumanStateClass::AIRBORNE ) {
			Controller.Set_Move_Up( Get_Definition().JumpVelocity );
		}

		if ( this == COMBAT_STAR ) {
			Vector3 pos;
			Get_Position( &pos );
			DefenseObjectClass * defense = Get_Defense_Object();
			const char * weapon_name = "";
			int ammo = 0;
			if ( Get_Weapon() != NULL )  {
				weapon_name = Get_Weapon()->Get_Definition()->Get_Name();
				ammo = Get_Weapon()->Get_Total_Rounds();
			}
			DIAG_LOG(( "JUUS", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), weapon_name, ammo ));
		}
	}
#else
	static float jump_timer = 0;

	// Have to Move_Up after after Apply control, but BOOLEAN_JUMP is cleared
	if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
		if ( Get_State() != HumanStateClass::AIRBORNE ) {
			HumanState.Set_State( HumanStateClass::AIRBORNE, HumanState.Get_Sub_State() );
			jump_timer = 0.2f;
		}
	}

	if ( jump_timer > 0 ) {
		jump_timer -= TimeManager::Get_Frame_Seconds();
		if ( jump_timer <= 0 ) {
			Controller.Set_Move_Up( Get_Definition().JumpVelocity );
		}
	}

#endif

#if 0
	if ( Is_Sniping() ) {
		// Dont allow move forward.  It may have been requested by the
		// Control, because we can't clear it if it must persist for
		// network sniper zoom.  So just clear it here.
		Controller.Set_Move_Forward( 0 );	
	}
#else
	if ( Is_Sniping() ) {
		// Dont allow move forward.  It may have been requested by the
		// Control, because we can't clear it if it must persist for
		// network sniper zoom.  So just clear it here.
		Controller.Set_Move_Forward( WWMath::Clamp( Controller.Get_Move_Forward(), -0.25f, 0.25f ) );	
		Controller.Set_Move_Left( WWMath::Clamp( Controller.Get_Move_Left(), -0.25f, 0.25f ) );	
	}
#endif

	if ( Get_State() == HumanStateClass::LADDER ) {
		// When on ladder, you can't move forward
		// Same rules as above wrt the network persistence
		Controller.Set_Move_Forward( 0 );	
	}

	if ( ( Get_State() == HumanStateClass::UPRIGHT ) && Is_Crouched() ) {
		float crouch_speed = GlobalSettingsDef::Get_Global_Settings ()->Get_Soldier_Crouch_Speed();
		Controller.Set_Move_Forward( Controller.Get_Move_Forward() * crouch_speed );	
		Controller.Set_Move_Left( Controller.Get_Move_Left() * crouch_speed );	
	} else if ( Control.Get_Boolean( ControlClass::BOOLEAN_WALK ) ) {
		float walk_speed = GlobalSettingsDef::Get_Global_Settings ()->Get_Soldier_Walk_Speed();
		Controller.Set_Move_Forward( Controller.Get_Move_Forward() * walk_speed );	
		Controller.Set_Move_Left( Controller.Get_Move_Left() * walk_speed );	
	}

	if ( this == COMBAT_STAR ) {
		static bool was_walking = false;
		if ( Control.Get_Boolean( ControlClass::BOOLEAN_WALK )  != was_walking ) {
			Vector3 pos;
			Get_Position( &pos );
			if ( !was_walking ) {
				DIAG_LOG(( "WAEN", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
			} else {
				DIAG_LOG(( "WAEX", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
			}
			was_walking = Control.Get_Boolean( ControlClass::BOOLEAN_WALK );
		}

	}

	if ( this == COMBAT_STAR && Control.Get_Boolean( ControlClass::BOOLEAN_ACTION ) ) {
		Vector3 pos;
		COMBAT_STAR->Get_Position( &pos );
		DIAG_LOG(( "ACPR", "%1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z ));
	}


	if (	CombatManager::I_Am_Server() ||
			(Is_Controlled_By_Me() && TransitionCompletionData == NULL) )
	{
		bool action_triggered = Control.Get_Boolean( ControlClass::BOOLEAN_ACTION );

		/// Transitions only trigger on server
		bool transition_triggered = false;
		if ( CombatManager::I_Am_Server() ) {
			transition_triggered = TransitionManager::Check( this, action_triggered );
		}

		if ( !transition_triggered ) {
			if ( action_triggered ) {

				//
				//	Lookup the current target
				//
//				DamageableGameObj *damageable_target = HUDInfo::Get_Weapon_Target_Object();
				DamageableGameObj *damageable_target = HUDInfo::Get_Info_Object();
				if ( damageable_target != NULL ) {
					PhysicalGameObj *physical_target = damageable_target->As_PhysicalGameObj();
					if ( physical_target != NULL ) {
						Vector3 target_pos;
						physical_target->Get_Position( &target_pos );
						Vector3 my_pos;
						Get_Position( &my_pos );

						//
						//	Check to see if the target is within the "poke" range
						//
						if ( (target_pos - my_pos).Length() <= 2 ) {

							//
							//	Notify all the observers
							//
							if ( CombatManager::I_Am_Server () ) {
								const GameObjObserverList & observer_list = physical_target->Get_Observers();
								for( int index = 0; index < observer_list.Count(); index++ ) {
									observer_list[ index ]->Poked( physical_target, this );
								}
							}

							//
							//	Was the player the soldier who did the poking?
							//
							if ( COMBAT_STAR == this ) {

								//
								//	Display this object in the encyclopedia
								//
								EncyclopediaMgrClass::Reveal_Object( damageable_target );
								
								//
								//	Check for player terminal access
								//
								if ( physical_target->As_SimpleGameObj () != NULL ) {
									
									//
									//	Is this a player terminal?
									//
									PlayerTerminalClass::TYPE type = PlayerTerminalClass::TYPE_NONE;
									type = (physical_target->As_SimpleGameObj())->Get_Definition().Get_Player_Terminal_Type();
									if (type != PlayerTerminalClass::TYPE_NONE) {

										//
										//	Show the player terminal
										//
										PlayerTerminalClass::Get_Instance()->Display_Terminal( this, type );
									}
								}
							}
						}
					}
				}
			}
		} else {
			// We started a transition, clear the control
			Controller.Reset();
		}
	}

	// Override the following controls
	if ( IS_SOLOPLAY && Is_Human_Controlled() && Input::Get_State( INPUT_FUNCTION_DEBUG_RAPID_MOVE ) ) {
		if ( Control.Get_Boolean( ControlClass::BOOLEAN_JUMP ) ) {
			Controller.Set_Move_Up( Get_Definition().JumpVelocity );
		}
	}

}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Handle_Legs( void )
{
#if 0
	if ( Get_State() != HumanStateClass::UPRIGHT ) {
		LegFacing = Get_Facing();
		SyncLegs = false;
	}

	// If Moving, clear LegFacing
	if ((Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f ) ||
		 (Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ) != 0.0f )) {

		LegFacing = Get_Facing();
		SyncLegs = false;

	} else {

#define	CLAMP_DEGREES	60
#define	SYNC_DEGREES	35
#define	SYNC_RATE		75
#define	FLIP_RATE		720

		// find the leg difference;
		LegFacing = WWMath::Wrap( LegFacing, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );

		float	legs_rotation = Get_Facing() - LegFacing;

		legs_rotation = WWMath::Wrap( legs_rotation, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );

		// Clamp to with 90
		legs_rotation = WWMath::Clamp( legs_rotation, DEG_TO_RADF( -CLAMP_DEGREES ), DEG_TO_RADF( CLAMP_DEGREES ) );
		LegFacing = Get_Facing() - legs_rotation; 		
		LegFacing = WWMath::Wrap( LegFacing, DEG_TO_RADF( -180 ), DEG_TO_RADF( 180 ) );

		// if legs are more than 30 degrees off, start correcting
		if ( WWMath::Fabs( legs_rotation ) > DEG_TO_RAD( SYNC_DEGREES ) ) {
			SyncLegs = true;
		}

SyncLegs = true;

		// if syncing, start moving legs to match rotation
		if ( SyncLegs ) {
			float move = DEG_TO_RAD( SYNC_RATE ) * TimeManager::Get_Frame_Seconds() * 
								WWMath::Sign( legs_rotation );
			if ( WWMath::Fabs( move ) >= WWMath::Fabs( legs_rotation ) ) {
				move = legs_rotation;	// Complete syncing
				SyncLegs = false;
			} else {
				LegFacing += move;
				if ( !Is_Human_Controlled() ) {	// human players don't use turn anims
					HumanState.Set_Turn_Velocity( move );	// Also, play the leg turning anim
				}
			}
		}
	}

	if ( !SyncLegs ) {
		HumanState.Set_Turn_Velocity( 0 );
	}

	// I'm making this staic for now, because all human
	// skeletons have the bone at the same index
	static int  root_bone = -1;
	if ( root_bone == -1 ) {			// Get root bone index
		root_bone = Peek_Model()->Get_Bone_Index( "root" );
	}

	static int  torso_bone = -1;
	if ( torso_bone == -1 ) {			// Get torso bone index
		torso_bone = Peek_Model()->Get_Bone_Index( "thorax" );
	}
	// Update the model
	float	legs_rotation = Get_Facing() - LegFacing;
	if ( legs_rotation ) {

		WWASSERT( root_bone != -1 );
		WWASSERT( torso_bone != -1 );

		if ( !Peek_Model()->Is_Bone_Captured( root_bone ) ) {
			Peek_Model()->Capture_Bone( root_bone );
		}
		if ( !Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
			Peek_Model()->Capture_Bone( torso_bone );
		}

		// LOOK INTO RELATIVE_CONTROL_BONE
		Matrix3D	root_adjust(1);				// adjust it
		root_adjust.Rotate_Z( -legs_rotation );
		Peek_Model()->Control_Bone( root_bone, root_adjust );

		Matrix3D	legs_adjust(1);				// adjust it
		legs_adjust.Rotate_Z( legs_rotation );
		Peek_Model()->Control_Bone( torso_bone, legs_adjust );
	} else {	// no adjustment, release

	 	if ( Peek_Model()->Is_Bone_Captured( root_bone ) ) {
			Peek_Model()->Release_Bone( root_bone );
		}
	 	if ( Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
			Peek_Model()->Release_Bone( torso_bone );
		}
	}

#else

#if 0
	float	legs_rotation = 0;

	// Compare the facing to the motion, set leg_racing to the difference
	Vector3 move( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ), Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ), 0 );
	if ( move.Length() > 0 ) {
		float move_direction = ::WWMath::Atan2( -move.Y, move.X );
		float diff = move_direction;
		diff += 2*DEG_TO_RADF( 360 ) + DEG_TO_RADF( 45 );
		diff -= WWMath::Floor( diff / DEG_TO_RADF( 90 ) ) * DEG_TO_RADF( 90 );
		diff -= DEG_TO_RADF( 45 );
		legs_rotation = diff;
		Debug_Say(( "Move (%1.1f)  %1.1f %1.1f %1.1f   %1.1f\n", RAD_TO_DEG(diff), move.X, move.Y, move.Z, RAD_TO_DEG( move_direction ) ));
	}


	// I'm making this staic for now, because all human
	// skeletons have the bone at the same index
	static int  root_bone = -1;
	if ( root_bone == -1 ) {			// Get root bone index
		root_bone = Peek_Model()->Get_Bone_Index( "c spine" );
	}

	static int  torso_bone = -1;
	if ( torso_bone == -1 ) {			// Get torso bone index
		torso_bone = Peek_Model()->Get_Bone_Index( "c spine1" );
	}

	if ( legs_rotation ) {

		WWASSERT( root_bone != -1 );
		WWASSERT( torso_bone != -1 );

		if ( !Peek_Model()->Is_Bone_Captured( root_bone ) ) {
			Peek_Model()->Capture_Bone( root_bone );
		}
		if ( !Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
			Peek_Model()->Capture_Bone( torso_bone );
		}

		Matrix3D	root_adjust(1);				// adjust it
		root_adjust.Rotate_X( legs_rotation );
		Peek_Model()->Control_Bone( root_bone, root_adjust );

		Matrix3D	legs_adjust(1);				// adjust it
		legs_adjust.Rotate_X( -legs_rotation );
		Peek_Model()->Control_Bone( torso_bone, legs_adjust );
	} else {	// no adjustment, release

	 	if ( Peek_Model()->Is_Bone_Captured( root_bone ) ) {
			Peek_Model()->Release_Bone( root_bone );
		}
	 	if ( Peek_Model()->Is_Bone_Captured( torso_bone ) ) {
			Peek_Model()->Release_Bone( torso_bone );
		}
	}

#endif

#endif

	bool do_steps = false;
	if ( Is_On_Ladder() ) {
		do_steps = (Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f );
	} else {
		do_steps = ((Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f ) ||
			 (Control.Get_Analog( ControlClass::ANALOG_MOVE_LEFT ) != 0.0f ));
	}

	// Footsteps
	if (!Is_Sniping() && !InFlyMode && CombatManager::Is_Gameplay_Permitted() && do_steps && Is_Control_Enabled() ) {

		bool leg_mode = HumanState.Get_Leg_Mode();

		// Watch for the legs to reach certain percentage completions

		if ( leg_mode != LastLegMode ) {

			int my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_RUN;
			if ( Is_Crouched() ) {
				my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_CROUCHED;
			} else if ( HumanState.Get_Sub_State() & HumanStateClass::SUB_STATE_SLOW ) {
				my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_WALK;
			}

			int ground_type = Peek_Human_Phys()->Get_Contact_Surface_Type();

			// Try effects on ladders again BMG
			/**/if ( Is_On_Ladder() ) {
				my_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_LADDER;
				ground_type = SURFACE_TYPE_LIGHT_METAL;
			}/**/

			Matrix3D tm;
			tm = Get_Transform();
			if ( Control.Get_Analog( ControlClass::ANALOG_MOVE_FORWARD ) != 0.0f )  {
				tm.Rotate_Z( DEG_TO_RAD( -90 ) );
			}
			float offset = leg_mode ? -0.15f : 0.1f;
			tm.Translate( Vector3( offset, 0.3f, 0 ) );
			
			bool allow_emitters = !Is_Stealthed();
			SurfaceEffectsManager::Apply_Effect( ground_type, my_type, tm, NULL, this, true, allow_emitters);
			LastLegMode = leg_mode;
		}

	}
}

static float	_shake_delay = 0;
static float	_cry_delay = 0;

static char * _profile_name = "Soldier Think";

//------------------------------------------------------------------------------------
void	SoldierGameObj::Think( void )
{
	{	WWPROFILE( _profile_name );

		if ( this == COMBAT_STAR ) {
			_shake_delay -= TimeManager::Get_Frame_Seconds();
			_cry_delay -= TimeManager::Get_Frame_Seconds();
		}
		/*
		ActionClass * p_action = Get_Action();
		WWASSERT(p_action != NULL);
		*/
		
		//
		//	Simply check to see if this soldier has entered a coordination zone (which bound
		// the entrances/exits of ladders and elevators).  If so, then disable
		// collision between them and all other soldiers.
		//
		Vector3 position;
		{
			WWPROFILE("Coordination Zone");
			Get_Position( &position );
			if ( UnitCoordinationZoneMgr::Is_Unit_In_Zone( position ) ) {
				Enable_Ghost_Collision( true );
			} else if ( Is_Safe_To_Disable_Ghost_Collision( position ) ) {
				Enable_Ghost_Collision( false );
			}
		}

		//
		//	Display a debug box to show the ghosted collision as ncessary
		//
		if (	DisplayDebugBoxForGhostCollision &&
				(Peek_Physical_Object ()->Get_Collision_Group() == SOLDIER_GHOST_COLLISION_GROUP))
		{
			WWPROFILE("Add_Debug_AABox");
			AABoxClass soldier_box;
			soldier_box.Center = position + Vector3( 0, 0, 1.0F );
			soldier_box.Extent.Set( 0.3F, 0.3F, 1.0F );
			PhysicsSceneClass::Get_Instance ()->Add_Debug_AABox( soldier_box, Vector3( 1.0F, 0.0F, 0.25F ) );
		}

		// Stats
		if ( Get_Player_Data() != NULL ) {
			WWPROFILE("Stats");
			Get_Player_Data()->Stats_Add_Game_Time( TimeManager::Get_Frame_Seconds() );
			Get_Player_Data()->Stats_Set_Final_Health( Get_Defense_Object()->Get_Health() );
			if ( Get_Vehicle() != NULL ) {
				Get_Player_Data()->Stats_Add_Vehicle_Time( TimeManager::Get_Frame_Seconds() );
			}
		}

		//
		//	Update the soldier's facing
		//
		{
			WWPROFILE("Update_Locked_Facing");
			Update_Locked_Facing();
		}

		/*
		//
		// In CTF a flag-bearer may not be permitted to use weapons
		// This code will deselect the weapon if that is the case.
		// 
		if (CombatManager::I_Am_Server() && Get_Weapon() != NULL &&
			!CombatManager::Is_Firing_Enabled(this)) {
			Get_Weapon_Bag()->Deselect();
		}
		*/

		// Handle_Legs moved form Apply_Control because clients don't run it for server objects
		{
			WWPROFILE("Handle_Legs");
			Handle_Legs();
		}

		/*	
		if (CombatManager::I_Am_Server()) {
			TransitionManager::Check( this );		
		}*/
	}

	{
		WWPROFILE( "Embedded smart think in soldier" );
		SmartGameObj::Think(); 	// Perform smart object thinking	( apply controls )
	}

{
	WWPROFILE( _profile_name );

	if ( CombatManager::I_Am_Server() ) {
		WWPROFILE("Handle C4");
		// Handle C4 in a special way.  When C4 is fired, the human plays
		// a crouching animation, and starts a c4 placement timer.
		if (  Get_Weapon() && 
			  (Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4) && 
			  (Get_Weapon()->Is_Firing()) && 
			  (Get_State() == HumanStateClass::UPRIGHT) ) {

			AnimationName = "s_a_human.h_a_j12c";
			HumanState.Start_Scripted_Animation( AnimationName, true, false );
		}
	}

	if ( Get_State() != HumanStateClass::IN_VEHICLE ) {
		WWPROFILE("Vehicle");
		// This may try to change controls, so can't be in Post_Think
		HumanState.Update_Weapon( Get_Weapon(), WeaponBag->Is_Changed() );

		if ( WeaponBag->Is_Changed() ) {
			WeaponChanged = true;
			WeaponBag->Reset_Changed();
		}

//		HumanState.Update_Aiming( Get_Weapon_Tilt(), Get_Weapon_Turn() );
		HumanState.Update_State(); 

		// Remove C4 from hand if empty
		if ( Get_Weapon() && WeaponRenderModel ) {
			if ( Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_C4 || Get_Weapon()->Get_Style() == WEAPON_HOLD_STYLE_BEACON ) {
				WeaponRenderModel->Set_Hidden( !Get_Weapon()->Is_Loaded() );
			} else {
				WeaponRenderModel->Set_Hidden( false );
			}
		}

	} else {
//		Debug_Say(( "No state update in vehicle\n" ));
	}

	/*
	**  Special Death
	*/
//	ArmorWarheadManager::SpecialDamageType			SpecialDamageMode;
//	float						SpecialDamageTimer;
	if ( SpecialDamageMode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE && 
			SpecialDamageTimer > 0 ) {
		WWPROFILE("Special Damage");

		float previous_timer = SpecialDamageTimer;

		SpecialDamageTimer -= TimeManager::Get_Frame_Seconds();

		// Once a second, make an explosion
		if ( (int)SpecialDamageTimer != (int)previous_timer ) {
			StringClass explosion_name = ArmorWarheadManager::Get_Special_Damage_Explosion( SpecialDamageMode );
			if ( !explosion_name.Is_Empty() ) {
				ExplosionDefinitionClass * def = (ExplosionDefinitionClass *)DefinitionMgrClass::Find_Typed_Definition( explosion_name, CLASSID_DEF_EXPLOSION );
				if ( def != NULL) {
					Vector3 pos = Get_Bullseye_Position();
					ExplosionManager::Create_Explosion_At( def->Get_ID(), pos, NULL );
				}
			}
		}

		// Fix to allow taking fire damage in CNC
//		if ( Get_Defense_Object()->Get_Health() > 0 && Allow_Special_Damage_State_Lock () ) {
		if ( Get_Defense_Object()->Get_Health() > 0 && 
				(Allow_Special_Damage_State_Lock () || Is_Human_Controlled() ) ) {
			// do damage
			WarheadType warhead = ArmorWarheadManager::Get_Special_Damage_Warhead( SpecialDamageMode );
			float damage = ArmorWarheadManager::Get_Special_Damage_Scale( SpecialDamageMode ) * 
									TimeManager::Get_Frame_Seconds();
			OffenseObjectClass off( damage, warhead, (ArmedGameObj*)SpecialDamageDamager.Get_Ptr()	);
//			Apply_Damage( off, 1 );
			Apply_Damage_Extended( off, 1 );
		}

		if ( HumanState.Get_State() != HumanStateClass::DEATH ) {

			// if dead, play death
			if ( Get_Defense_Object()->Get_Health() <= 0 ) {
				HumanState.Set_State( HumanStateClass::DEATH, 
					SpecialDamageMode - ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE + HumanStateClass::OUCH_FIRE );
			}

			if ( SpecialDamageTimer <= 0 ) {		// Stop the flame
//				HumanState.Set_State( HumanStateClass::UPRIGHT );
				Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );
			}
		}
	}

	if (Get_State() == HumanStateClass::DESTROY) {
		if ( HumanState.Get_State_Timer() > CORPSE_PERSIST_TIME ) {
			// If Human Controlled, drop a backpack
			if ( Is_Human_Controlled() ) {	
//				PowerUpGameObj::Create_Backpack( this );	// No more backpacks
			}

			Set_Delete_Pending();
		}
	}

	// ClientOnlys doen't handle TRANSITION_COMPLETE
	if ( !CombatManager::I_Am_Only_Client() || Is_Controlled_By_Me()) {
		if ( Get_State() == HumanStateClass::TRANSITION_COMPLETE ) {
			if ( TransitionCompletionData != NULL ) {
	//			Debug_Say(( "Ended Transition %p\n", Transition ));
				// We just completed a transition, notify
				TransitionInstanceClass::End( this, TransitionCompletionData );
				TransitionCompletionData = NULL;
			} else {
	//			Debug_Say(( "Ended Unknown Transition %p\n", Transition ));
				HumanState.Set_State( HumanStateClass::UPRIGHT );
			}
		}
	}

	//
	//	Remove the emot icon (if necessary)
	//
	{
		WWPROFILE("EmotIcon");
		EmotIconTimer -= TimeManager::Get_Frame_Seconds();
		if ( EmotIconTimer <= 0 ) {

			//
			//	Remove the emoticon
			//
			if ( EmotIconModel != NULL && EmotIconModel->Is_In_Scene() ) {
				EmotIconModel->Remove();
				REF_PTR_RELEASE( EmotIconModel );
			}

		} else if ( EmotIconModel != NULL  ) {

			//
			//	Put the icon over the soldier's head
			//
			Matrix3D tm = Get_Transform();
			tm.Set_Translation (tm.Get_Translation() + Vector3 (0, 0, EMOT_ICON_HEIGHT));
			EmotIconModel->Set_Transform( tm );
		}
	}

	//
	//	Should we change the idle facial animation?
	//
	GenerateIdleFacialAnimTimer -= TimeManager::Get_Frame_Seconds();
	if ( GenerateIdleFacialAnimTimer <= 0 ) {
		WWPROFILE("FacialAnims");
		
		//
		//	Change the facial animation
		//
		if ( HeadModel != NULL ) {
			// Don't stop a playing script animation
			if ( ( HeadModel->Peek_Animation() == NULL ) || 
				  (HeadModel->Peek_Animation() == SpeechAnim ) ) {
				SpeechAnim->Generate_Idle_Animation( 20, 0.5F );
				HeadModel->Set_Animation( SpeechAnim, 0, RenderObjClass::ANIM_MODE_LOOP );
			}
		}

		//
		//	Reset the timer
		//
		GenerateIdleFacialAnimTimer = 100.0F;
	}

	/*
	if (!Is_Destroy()) {
	   CombatManager::Set_Soldier_Tint(this);
	}
	*/

	// Apply Surface damage
	{
		WWPROFILE("Apply_Damage");
		if ( Peek_Human_Phys() ) {
			SurfaceEffectsManager::Apply_Damage( Peek_Human_Phys()->Get_Contact_Surface_Type(), this );
		}
	}

	// Update the water wake 
	// - If our feet are not on "underwater dirt" we don't even check for water
	// - Otherwise, do a ray cast to find the water surface, if it hits
	{
		WWPROFILE("Water");
		bool in_water = false;
		Vector3 p0 = Get_Transform().Get_Translation();
		Vector3 p1 = Get_Bullseye_Position();

		int hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_RUN;
		if ( Is_Crouched() ) {
			hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_CROUCHED;
		} else if ( HumanState.Get_Sub_State() & HumanStateClass::SUB_STATE_SLOW ) {
			hit_type = SurfaceEffectsManager::HITTER_TYPE_FOOTSTEP_WALK;
		}

		Vector3 vel;
		Peek_Human_Phys()->Get_Velocity(&vel);

		if ((Peek_Human_Phys()->Get_Contact_Surface_Type() == SURFACE_TYPE_UNDERWATER_DIRT) &&
			 (vel.Length2() > 0.1f))
		{
			
			// Raycast to find water surface
			CastResultStruct res;
			LineSegClass ray(p0,p1);
			PhysRayCollisionTestClass raytest(ray,&res,BULLET_COLLISION_GROUP,COLLISION_TYPE_PROJECTILE);
			
			Peek_Human_Phys()->Inc_Ignore_Counter();
			COMBAT_SCENE->Cast_Ray(raytest);
			Peek_Human_Phys()->Dec_Ignore_Counter();

			// if found update emitter
			if ((res.Fraction < 1.0f) && (res.SurfaceType == SURFACE_TYPE_WATER_PERMEABLE)) {

				Vector3 point;
				ray.Compute_Point(res.Fraction,&point);

				SurfaceEffectsManager::Update_Persistant_Emitter(	WaterWake,
																					res.SurfaceType, 
																					hit_type, 
																					Matrix3D(point));
				in_water = true;
			}
		}
		
		if (!in_water) {
				SurfaceEffectsManager::Update_Persistant_Emitter(	WaterWake,
																					SURFACE_TYPE_DEFAULT, 
																					hit_type, 
																					Matrix3D(p0));
		}
	}


	// Punish update
	if ( Get_Player_Data() && Get_Player_Data()->Get_Punish_Timer() > 0 ) {
		Get_Player_Data()->Inc_Punish_Timer( TimeManager::Get_Frame_Seconds() )	;
	}

}
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Post_Think( void )
{
	if ( Get_State() == HumanStateClass::IN_VEHICLE ) {
		SmartGameObj::Post_Think();
		return;
	}

{	WWPROFILE( "Soldier PostThink" );

	HumanState.Post_Think(); 

	bool update_weapon = false;

	if ( WeaponChanged ) {
		WeaponChanged = false;
		Update_Back_Gun();
		update_weapon = true;
	}

	if ( ( Get_Weapon() != NULL ) && ( Get_Weapon()->Is_Model_Update_Needed() ) ) {
		update_weapon = true;
		Get_Weapon()->Reset_Model_Update();			// reset model update needed
	}


	if ( update_weapon ) {
		if ( Get_Weapon() != NULL ) {
			Set_Weapon_Model( Get_Weapon()->Get_Model_Name() );
		} else {
			Set_Weapon_Model( NULL );
		}
	}

//			if ( Get_Weapon()->Is_Anim_Update_Needed() ) {
//				Set_Weapon_Animation( Get_Weapon()->Get_Anim_Name() );
//				Get_Weapon()->Reset_Anim_Update();		// reset model anim update needed
//			}

	if (WeaponAnimControl) {
		WeaponAnimControl->Update( TimeManager::Get_Frame_Seconds() );			// update the animation control
	}


	Handle_Head_look();

	if ( Get_Weapon() != NULL && Get_State() == HumanStateClass::ON_FIRE ) {
		Get_Weapon()->Set_Primary_Triggered( false );
		Get_Weapon()->Set_Secondary_Triggered( false );
	}

}
	SmartGameObj::Post_Think();

	Update_Healing_Effect();

}

//------------------------------------------------------------------------------------
const Matrix3D & SoldierGameObj::Get_Muzzle( int index )
{
	static Matrix3D _muzzle(1);
	if ( WeaponRenderModel != NULL ) {
		Matrix3D true_muzzle = WeaponRenderModel->Get_Bone_Transform( "muzzlea0" );
		Vector3 muzzle_pos = true_muzzle.Get_Translation();
		_muzzle.Obj_Look_At( muzzle_pos, Get_Targeting_Pos(), 0 );

		if ( !Is_Human_Controlled() ) {
			// If the bullet is not close to going down the muzzle, force it to be
			Vector3	to_target = _muzzle.Get_X_Vector();
			Vector3	down_muzzle = true_muzzle.Get_X_Vector();
			float cos = Vector3::Dot_Product( to_target, down_muzzle );
			if ( cos < WWMath::Cos( DEG_TO_RADF( 20 ) ) ) {
				_muzzle = true_muzzle;
			}
		}

	}
	return _muzzle;
}

static int head_bone = -1;
static int neck_bone = -1;

//------------------------------------------------------------------------------------
Matrix3D SoldierGameObj::Get_Look_Transform(void)  
{
	if ( head_bone != -1 ) {
		// Convert from CS head convention back to normal
		Matrix3D tm = Peek_Model()->Get_Bone_Transform( head_bone );
		tm.Rotate_Z( DEG_TO_RAD( 90 ) );
		tm.Rotate_X( DEG_TO_RAD( 90 ) );
		return tm;
	}
	return Get_Transform();
}

//------------------------------------------------------------------------------------
#define	HEAD_TURN_LIMIT	DEG_TO_RADF( 70 )
#define	HEAD_TILT_LIMIT	DEG_TO_RADF( 20 )

void	SoldierGameObj::Look_Random( float time )
{
	if ( time == 0 && HeadLookDuration != 0 ) {
		HeadLookDuration = 0.0001f;		// maybe done next time...
	} else {
		HeadLookDuration = time;
	}
	HeadLookAngleTimer = 0;
	HeadLookAngle = Vector3( 1,1,1 );
}

////////////////////////////////////////////////////////////////
//	Clamp_Angle
////////////////////////////////////////////////////////////////
static inline float
Clamp_Angle (float angle, float min_angle, float max_angle)
{
	//
	//	Make sure all the parameters are in the same range
	//
	angle			= WWMath::Wrap (angle,		0, DEG_TO_RADF (360));
	min_angle	= WWMath::Wrap (min_angle,	0, DEG_TO_RADF (360));
	max_angle	= WWMath::Wrap (max_angle,	0, DEG_TO_RADF (360));

	float result = angle;

	if (min_angle <= max_angle) {
		
		//
		//	Handle the 'typical' case where there is no 360-mark wrap-around
		//
		if (angle < min_angle) {
			result = min_angle;
		} else if (angle > max_angle) {		
			result = max_angle;
		}

	} else {
		
		//
		//	Handle the 360-mark wrap-around case
		//
		if (angle < min_angle && angle > max_angle) {
			float min_delta = min_angle - angle;
			float max_delta = angle - max_angle;

			//
			//	Which edge is the angle closer to?
			//
			if (min_delta < max_delta) {
				result = min_angle;
			} else {
				result = max_angle;
			}
		}
	}

	return result;
}


void	SoldierGameObj::Handle_Head_look( void )
{
	if ( Peek_Model ()->Get_HTree () == NULL ) {
		return ;
	}

	//
	//	Get the head bone
	//
	if ( head_bone == -1 || neck_bone == -1) {
		head_bone = Peek_Model()->Get_Bone_Index( "C HEAD" );
		neck_bone = Peek_Model()->Get_Bone_Index( "C NECK" );
		WWASSERT( head_bone != -1 );
		WWASSERT( neck_bone != -1 );
	}

	if ( HeadLookDuration > 0 ) {

		HeadLookDuration -= TimeManager::Get_Frame_Seconds();

		// Should we be returning to look ahead?
		bool returning = HeadLookDuration < 0;

		//Matrix3D	head(1);

		Vector3	desired_head_rotation( 0,0,0 );
		if ( !returning ) {

			if ( HeadLookAngle.Length() > 0.001f ) {
				HeadLookAngleTimer -= TimeManager::Get_Frame_Seconds();
				if ( HeadLookAngleTimer < 0 ) {
					HeadLookAngle = Vector3(
						FreeRandom.Get_Float( -HEAD_TURN_LIMIT, HEAD_TURN_LIMIT ),
						FreeRandom.Get_Float( -HEAD_TILT_LIMIT, HEAD_TILT_LIMIT )	,0); 
//					Debug_Say(( "New Look Turn %f Tilt %f\n", RAD_TO_DEG(HeadLookAngle.X), RAD_TO_DEG(HeadLookAngle.Y) ));
					HeadLookAngleTimer = FreeRandom.Get_Float( 2, 5 );
				}
				desired_head_rotation = HeadLookAngle;

			} else {

				//
				//	Release the captured bone, this will have the effect of causing the Get_Bone_Transform ()
				// methods to return the un-modified (due to being controlled) transform of the bone.
				//
				/*if ( Peek_Model()->Is_Bone_Captured( head_bone ) ) {
					Peek_Model()->Release_Bone( head_bone );
				}*/

				///
				//	Get the transform that has been used to modify the head bone...
				//
				const HTreeClass *htree = Peek_Model()->Get_HTree();
				WWASSERT( htree != NULL );

				Matrix3D bone_control_tm( 1 );
				htree->Get_Bone_Control( head_bone, bone_control_tm );

				//
				//	Get the inverse of the head-bone transform 
				//
				Matrix3D inv_bone_control_tm;
				bone_control_tm.Get_Orthogonal_Inverse (inv_bone_control_tm);

				//
				//	Get the head to world and neck to world transforms
				//
				Matrix3D cur_head = Peek_Model()->Get_Bone_Transform( head_bone );
				Matrix3D cur_neck = Peek_Model()->Get_Bone_Transform( neck_bone );

				//
				//	Strip off the control transform from last frame
				//
				cur_head = cur_head * inv_bone_control_tm;

				//
				//	Get the world to neck transform
				//
				Matrix3D world_to_neck_tm;
				cur_neck.Get_Orthogonal_Inverse (world_to_neck_tm);

				//
				//	Build a head to neck transform
				//
				Matrix3D head_to_neck_tm = world_to_neck_tm * cur_head;				

				//
				//	Get the target relative to the head
				//
				Vector3 relative_head_target;
				Matrix3D::Inverse_Transform_Vector( cur_head, HeadLookTarget, &relative_head_target );

				//
				//	Determine the 'twist' and lookup/down angles.
				// Note:  Currently in the head bone coordinate system, the X axis is the same
				// as the Z axis in object space, the Y axis is the same as the X axis in object space,
				// and the Z axis is the same as the Y axis in object space.
				//
				desired_head_rotation.X = WWMath::Atan2( relative_head_target.Z, relative_head_target.Y );
				desired_head_rotation.Z = -WWMath::Fast_Asin( relative_head_target.X / relative_head_target.Length() );
				desired_head_rotation.Y = 0;

				//
				//	Determine how far to allow the character to turn and tilt his/her head.
				// These boundaries are based on the "absolute" amount the person can turn
				// their head, this has to take into consideration the amount that the current
				// animation is turning the head and the amount we need to turn to look at the target.
				//
				Vector3 temp_vec = head_to_neck_tm.Get_Y_Vector ();
				float curr_rot_x  = ::atan2 (temp_vec.Z, temp_vec.Y);
				float curr_rot_z  = ::atan2 (temp_vec.X, temp_vec.Y);
				
				float min_twist = ((-HEAD_TURN_LIMIT) - curr_rot_x);
				float max_twist = (HEAD_TURN_LIMIT - curr_rot_x);

				float min_tilt = ((-HEAD_TILT_LIMIT) - curr_rot_z);
				float max_tilt = (HEAD_TILT_LIMIT - curr_rot_z);

				//
				//	Clamp the rotations
				//
				desired_head_rotation.X = WWMath::Clamp( desired_head_rotation.X, min_twist, max_twist);
				desired_head_rotation.Z = WWMath::Clamp( desired_head_rotation.Z, min_tilt, max_tilt);
			}
		}

#define	HEAD_TURN_RATE		(DEG_TO_RAD( 360 )/2)
#define	HEAD_TILT_RATE		(DEG_TO_RAD( 180 )/2)
		float max_turn = HEAD_TURN_RATE * TimeManager::Get_Frame_Seconds();
		float max_tilt = HEAD_TILT_RATE * TimeManager::Get_Frame_Seconds();
		HeadRotation.X += WWMath::Clamp( (desired_head_rotation.X - HeadRotation.X), -max_turn, max_turn );
		HeadRotation.Z += WWMath::Clamp( (desired_head_rotation.Z - HeadRotation.Z), -max_tilt, max_tilt );

		Matrix3D	head(1);		
		head.Rotate_X( HeadRotation.X );
		head.Rotate_Z( HeadRotation.Z );
		if ( !Peek_Model()->Is_Bone_Captured( head_bone ) ) {
			Peek_Model()->Capture_Bone( head_bone );
		}
		WWASSERT( Peek_Model()->Is_Bone_Captured( head_bone ) );
		if ( Peek_Model()->Is_Bone_Captured( head_bone ) ) {
			Peek_Model()->Control_Bone( head_bone, head );
		}

		HeadRotation.Z = 0;
		if ( returning && HeadRotation.Length() > 0.001f ) {
			HeadLookDuration = 0.0001f;		// maybe done next time...
		}
	}
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Set_Blended_Animation( const char *animation_name, bool looping, float frame_offset, bool play_backwards )
{
	if ( animation_name == NULL ) {
		HumanState.Stop_Scripted_Animation();
		return;
	}

	// Humans ignore the start_frame parameter for now

	AnimationName = animation_name;
	HumanState.Start_Scripted_Animation( animation_name, true, looping );
	Get_Anim_Control()->Set_Mode( looping ? ANIM_MODE_LOOP : ANIM_MODE_ONCE, frame_offset );

	if ( play_backwards ) {
		HAnimClass *anim = Get_Anim_Control()->Peek_Animation();
		if ( anim != NULL ) {
			int frame_count = anim->Get_Num_Frames();
			Get_Anim_Control()->Set_Mode( ANIM_MODE_TARGET, frame_count - 1 );
			Get_Anim_Control()->Set_Target_Frame( 0 );
		}
	}

	return ;
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Set_Animation( const char *animation_name, bool looping, float start_frame )
{
	if ( animation_name == NULL ) {
//		Debug_Say(( "Stoping Scripted Human Animation\n" ));
		HumanState.Stop_Scripted_Animation();
// FIX		AnimControl->UnLock_Animation();
		return;
	}

	// Humans ignore the start_frame parameter for now

//	Debug_Say(( "Starting Scripted Human Animation %s\n", animation_name ));
	AnimationName = animation_name;
	HumanState.Start_Scripted_Animation( animation_name, false, looping );
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Start_Transition_Animation( const char * anim_name, TransitionCompletionDataStruct *data )
{
//	Debug_Say(( "Starting Human Transition Animation %s\n", anim_name ));
	//WWASSERT( TransitionCompletionData == NULL );
	TransitionCompletionData = data;
	AnimationName = anim_name;
	HumanState.Start_Transition_Animation( anim_name, false );
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Weapon_Model( const char *model_name )
{
	if ( WeaponRenderModel != NULL ) {		// remove old gun model
		if ( Peek_Model() != NULL ) {
			Peek_Model()->Remove_Sub_Object(WeaponRenderModel);		// Clean the bone
		}
		WeaponRenderModel->Release_Ref();
		WeaponRenderModel = NULL;
	}

	if ( (model_name != NULL) && (*model_name != 0) ) {
		//Debug_Say(( "Updating soldier Weapon model to %s!!!\n", model_name ));

		StringClass stripped_name(true);
		Get_Render_Obj_Name_From_Filename( stripped_name, model_name );
		WeaponRenderModel = SoldierGameObj::Find_RenderObj( stripped_name );
		if ( WeaponRenderModel == NULL ) {
			WeaponRenderModel = Create_Render_Obj_From_Filename( model_name );
			WWASSERT( WeaponRenderModel );
			SET_REF_OWNER( WeaponRenderModel );
			Add_RenderObj( WeaponRenderModel );
			WeaponRenderModel->Release_Ref();
		}
		WeaponRenderModel->Add_Ref();

		// Create and add new gun model

		if ( Peek_Model() != NULL ) {
			if ( WeaponRenderModel->Get_Container() != Peek_Model() ) {
				Peek_Model()->Add_Sub_Object_To_Bone( WeaponRenderModel, GUN_BONE_NAME );
			}
		}

		assert( Get_Weapon() );
		Get_Weapon()->Set_Model( WeaponRenderModel );	// let the weapon know who's firing him
	}
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Weapon_Animation( const char *anim_name )
{
	if ( WeaponAnimControl == NULL ) {
		WeaponAnimControl = new SimpleAnimControlClass;
		if (WeaponRenderModel != NULL ) {
			// Update Anim Control
			WeaponAnimControl->Set_Model( WeaponRenderModel );
		}
	}
	if ( WeaponAnimControl != NULL ) {
//		Debug_Say(( "Updating soldier gun anim to \"%s\"!!!\n", anim_name ));
		if ( ( anim_name != NULL ) && ( *anim_name != 0 ) ) {
			WeaponAnimControl->Set_Animation( anim_name );
		}
	}
}

void SoldierGameObj::Set_Back_Weapon_Model( const char *model_name )
{
	if ( BackWeaponRenderModel != NULL ) {		// remove old gun model
		if (Peek_Model() != NULL ) {
			Peek_Model()->Remove_Sub_Object(BackWeaponRenderModel);
		}
		BackWeaponRenderModel->Release_Ref();
		BackWeaponRenderModel = NULL;
	}

	if ( (model_name != NULL) && (*model_name != 0) ) {
		
		// Create and add back gun model
		StringClass stripped_name(true);
		Get_Render_Obj_Name_From_Filename( stripped_name, model_name );
		BackWeaponRenderModel = SoldierGameObj::Find_RenderObj( stripped_name );
		if ( BackWeaponRenderModel == NULL ) {
			BackWeaponRenderModel = Create_Render_Obj_From_Filename( model_name );
//			WWASSERT( BackWeaponRenderModel );
			if ( BackWeaponRenderModel != NULL ) {
				SET_REF_OWNER( BackWeaponRenderModel );
				Add_RenderObj( BackWeaponRenderModel );
				BackWeaponRenderModel->Release_Ref();
			}
		}

		if ( BackWeaponRenderModel != NULL ) {
			BackWeaponRenderModel->Add_Ref();

			if (Peek_Model() != NULL ) {
				Peek_Model()->Add_Sub_Object_To_Bone( BackWeaponRenderModel, BACK_GUN_BONE_NAME );
			}
		} else {
			Debug_Say(( "Missing Back Model %s", model_name ));
		}
	}
}

void SoldierGameObj::Set_Back_Flag_Model(const char *model_name,
	const Vector3 & tint)
{
	if ( BackFlagRenderModel != NULL ) {		// remove old gun model
		if (Peek_Model() != NULL ) {
			Peek_Model()->Remove_Sub_Object(BackFlagRenderModel);
		}
		BackFlagRenderModel->Release_Ref();
		BackFlagRenderModel = NULL;
	}

	if ( (model_name != NULL) && (*model_name != 0) ) {
		
		// Create and add back gun model
		BackFlagRenderModel = Create_Render_Obj_From_Filename( model_name );
		if ( BackFlagRenderModel != NULL ) {
			SET_REF_OWNER( BackFlagRenderModel );

			/*
			extern void Tint(RenderObjClass *robj, const Vector3 & color);
			Tint(BackFlagRenderModel, tint);
			*/

			if (Peek_Model() != NULL ) {
				Peek_Model()->Add_Sub_Object_To_Bone( BackFlagRenderModel, BACK_GUN_BONE_NAME );
			}
		} else {
			Debug_Say(( "Missing Back Model %s", model_name ));
		}
	}
}

void	SoldierGameObj::Update_Back_Gun( void )
{
	//Debug_Say(("CommandoGameObj::Update_Back_Gun\n"));

	WeaponClass	*next_weapon = WeaponBag->Get_Next_Weapon();
	if ( next_weapon && ( next_weapon != Get_Weapon() ) ) {
		Set_Back_Weapon_Model( next_weapon->Get_Back_Model_Name() );
	} else {
		Set_Back_Weapon_Model( NULL );
	}
}

//------------------------------------------------------------------------------------
float		SoldierGameObj::Get_Weapon_Height( void )
{
	float height = 1.62f;		// set to be at about eye level
	if ( Is_Crouched() ) {
		height -= 0.56f;
	}
	return height;		// verticle offset, move to weapon
}

//------------------------------------------------------------------------------------
float		SoldierGameObj::Get_Weapon_Length( void )
{
	return 0.8f;		// forward offset, move to weapon
}


//------------------------------------------------------------------------------------
bool	SoldierGameObj::Internal_Set_Targeting( const Vector3 & target_pos, bool do_tilt )
{
	WWPROFILE( "Soldier Set Targeting" );

	if ( CombatManager::Is_Skeleton_Slider_Demo_Enabled() ) {
		return false;
	}

	SmartGameObj::Set_Targeting( target_pos );

	if ( ( Get_State() == HumanStateClass::DEATH ) ||
		  ( Get_State() == HumanStateClass::DESTROY ) ||
		  ( Get_State() == HumanStateClass::TRANSITION ) ||
		  ( Get_State() == HumanStateClass::LADDER ) ) {
//		Debug_Say(( "Ignore Set Target in Transition \n" ));
		return false;
	}

	if ( Get_State() == HumanStateClass::IN_VEHICLE ) {
		if ( Vehicle != NULL ) {
			if ( Vehicle->Get_Driver_Is_Gunner() ) {
				if ( Vehicle->Get_Driver() == this ) {
					Vehicle->Set_Targeting( target_pos );
				}
			} else {
				if ( Vehicle->Get_Driver() == this && Vehicle->Get_Gunner() == NULL ) {
					Vehicle->Set_Targeting( target_pos );
				} else if ( Vehicle->Get_Gunner() == this ) {
					Vehicle->Set_Targeting( target_pos );
				}
			}
		}
		return false;
	}

	Vector3 muzzle_pos;
	Get_Position( &muzzle_pos );
	muzzle_pos.Z += Get_Weapon_Height();
	Vector3	rel_target_pos = target_pos - muzzle_pos;
	
	// Set Tilt
	float dist = rel_target_pos.Length();
	float	tilt = 0;
	if ( dist && do_tilt ) {
		tilt = WWMath::Fast_Asin( rel_target_pos.Z / dist );
	}

//	tilt = 0;
//	Debug_Say(( "Tilt %f", tilt ));

	bool	is_complete = true;

	// Set Facing
	float cur_facing = Peek_Human_Phys()->Get_Heading();
	float facing = WWMath::Atan2( rel_target_pos.Y, rel_target_pos.X );
	float facing_dif = facing - cur_facing;
	if ( WWMath::Fabs(facing_dif) > 0.001f ) {
		WWPROFILE( "Facing" );
		facing_dif = WWMath::Wrap( facing_dif, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
//		Debug_Say(( "Must change %1.3f, from facing %1.3f to facing %1.3f\n", RAD_TO_DEG( facing_dif ), RAD_TO_DEG( cur_facing ), RAD_TO_DEG( facing ) ));

		float change = facing_dif;
		if ( !Is_Human_Controlled() ) {	// human players don't use turn limits
			float max_change = Get_Turn_Rate() * TimeManager::Get_Frame_Seconds();

			// Turn more slowly when turning a small amount
			if ( WWMath::Fabs( change ) < DEG_TO_RAD( 20 ) ) {
				max_change *= 0.3f;
			}

			change = WWMath::Clamp( facing_dif, -max_change, max_change );
		}

		if ( !Is_Human_Controlled() ) {	// human players don't use turn anims
  			HumanState.Set_Turn_Velocity( change );	// play the leg turning anim
		}

		is_complete = (change == facing_dif);	// are we facing?

		facing = cur_facing + change;
		facing = WWMath::Wrap( facing, DEG_TO_RADF( -180.0f ), DEG_TO_RADF( 180.0f ) );
		Peek_Human_Phys()->Set_Heading( facing );
	}

	if ( Is_Human_Controlled() && Get_State() != HumanStateClass::IN_VEHICLE ) {

#define		TILT_DOWN_SPEED		4.0
		float direction = -1;
		if ( Get_Weapon() && Get_Weapon()->Is_Reloading() ) {
			direction = 1;
		}

		ReloadingTilt += direction * TimeManager::Get_Frame_Seconds() * TILT_DOWN_SPEED;
		ReloadingTilt = WWMath::Clamp( ReloadingTilt, 0, 1 );

		if ( ReloadingTilt > 0 ) {
			tilt = WWMath::Lerp(	(float)tilt, DEG_TO_RADF( -90 ), ReloadingTilt );
		}
	}

	HumanState.Update_Aiming( tilt, 0 );	// no turn

//	facing = HumanState.Peek_Physical_Object()->Get_Heading();
//	Debug_Say(( "Soldier Facing %f Tilt %f\n", RAD_TO_DEG( facing ), RAD_TO_DEG( WeaponTilt ) ));
//	Debug_Say(( "rel_target_pos %f %f %f\n", rel_target_pos.X, rel_target_pos.Y, rel_target_pos.Z ));

	return is_complete;
}


//------------------------------------------------------------------------------------
bool	SoldierGameObj::Set_Targeting( const Vector3 & target_pos, bool do_tilt )
{
	bool retval = false;

	//
	//	Don't do the targetting if we are locked on an object
	//
	if ( FacingObject == NULL ) {
		retval = Internal_Set_Targeting( target_pos, do_tilt );
	}

	return retval;
}

//------------------------------------------------------------------------------------
RenderObjClass *	SoldierGameObj::Find_Head_Model( void )
{
	RenderObjClass *head_model = NULL;

	//
	//	Get a pointer to the HLod this object is using
	//
	RenderObjClass *model = Peek_Model ();
	if ( model != NULL && model->Class_ID () == RenderObjClass::CLASSID_HLOD ) {
		HLodClass *lod = reinterpret_cast<HLodClass *> ( model );
		
		//
		//	Try to find the head-bone
		//
		int bone_index	= lod->Get_Bone_Index ( "C HEAD" );
		int obj_count	= lod->Get_Num_Sub_Objects_On_Bone(bone_index);

		//
		//	Try to find a model on the head bone that uses the human head skeleton
		//
		for (int index = 0; index < obj_count; index ++) {
			RenderObjClass *render_obj = lod->Get_Sub_Object_On_Bone ( index, bone_index );
			if (render_obj != NULL) {
				const HTreeClass *htree = render_obj->Get_HTree();
				if ( htree != NULL ) {

					//
					//	Is this really a head model?
					//
					if ( ::stricmp (htree->Get_Name(), "S_A_HEAD") == 0 ||
						  ::stricmp (htree->Get_Name(), "S_B_HEAD") == 0)
					{
						head_model = render_obj;
						break;
					}
				}
				render_obj->Release_Ref ();
			}
		}
	}

	return head_model;
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Set_Emot_Icon ( const char *model_name, float duration )
{
	//
	//	Free the old emot icon
	//
	if ( EmotIconModel != NULL ) {

		if ( EmotIconModel->Is_In_Scene() ) {
			EmotIconModel->Remove ();
		}

		REF_PTR_RELEASE( EmotIconModel );
	}
	
	//
	//	Try to load the new emot icon model
	//
	if ( model_name != NULL ) {

		//
		//	Create the new model
		//
		EmotIconModel = ::Create_Render_Obj_From_Filename( model_name );
		if ( EmotIconModel != NULL ) {

			//
			//	Determine what the animation name for the model is
			//
			const char *name = EmotIconModel->Get_Name ();
			StringClass anim_name;
			anim_name.Format ("%s.%s", name, name);

			//
			//	Animate the model
			//
			HAnimClass *anim = WW3DAssetManager::Get_Instance ()->Get_HAnim( anim_name );
			if ( anim != NULL ) {
				EmotIconModel->Set_Animation( anim, 0.0F, RenderObjClass::ANIM_MODE_LOOP );
				REF_PTR_RELEASE( anim );
			}
			
			//
			//	Put the icon a few meters above the soldier's head
			//
			Matrix3D tm = Get_Transform();
			tm.Set_Translation (tm.Get_Translation() + Vector3 (0, 0, EMOT_ICON_HEIGHT));
			EmotIconModel->Set_Transform( tm );
			COMBAT_SCENE->Add_Render_Object( EmotIconModel );
			EmotIconTimer = duration;
		}
	}
	
	return ;
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Say_Dialogue( int dialog_id )
{
	WWASSERT( dialog_id >= 0 && dialog_id < DIALOG_MAX );

	//
	//	Lookup the conversation we are starting
	//	
	int conversation_id = DialogList[dialog_id].Get_Conversation();
	if ( conversation_id > 0 ) {

		//
		//	Start the conversation
		//
		ActiveConversationClass *conversation = ConversationMgrClass::Start_Conversation( this, conversation_id, true );
		REF_PTR_RELEASE (conversation);
	}
	
	return ;
}

//------------------------------------------------------------------------------------
float SoldierGameObj::Say_Dynamic_Dialogue
(
	int						text_id,
	SoldierGameObj *		speaker,
	AudibleSoundClass **	sound_obj_to_return 
)
{
	float duration	= 2.0F;
		
	//
	//	Lookup the translation object from the strings database
	//
	TDBObjClass *translate_obj = TranslateDBClass::Find_Object( text_id );
	if (translate_obj != NULL) {
		
		const WCHAR *string		= translate_obj->Get_String ();
		int sound_def_id			= (int)translate_obj->Get_Sound_ID ();
		const char *anim_name	= translate_obj->Get_Animation_Name ();

		//
		//	If this soldier was still talking from a previos call, then
		// kill the old sound
		//
		if ( speaker != NULL ) {
			speaker->Stop_Current_Speech();
		}

		//
		//	Play the sound effect
		//
		bool display_text = true;
		if ( sound_def_id > 0 ) {
			
			//
			//	Create the sound object
			//
			AudibleSoundClass *speech = WWAudioClass::Get_Instance ()->Create_Sound( sound_def_id );
			if ( speech != NULL ) {
				duration = (speech->Get_Duration() / 1000.0F);

				//
				//	Return the sound object to the caller as necessary
				//
				if ( sound_obj_to_return != NULL ) {
					(*sound_obj_to_return) = speech;
					(*sound_obj_to_return)->Add_Ref();
				}

				//
				//	Either play the sound at the speaker's location
				// or just play it as a 2D sample.
				//
				if ( speaker != NULL ) {
					speech->Set_Transform( speaker->Get_Transform() );
					speech->Add_To_Scene();
					display_text = (speech->Is_Sound_Culled() == false);
				} else {
					
					//
					//	Play the sound
					//
					speech->Play();
				}
			}

			//
			//	Update the speaker's internal sound object pointer
			//
			if ( speaker != NULL ) {
				speaker->CurrentSpeech = speech;
			} else {
				REF_PTR_RELEASE (speech);
			}
		}

		//
		//	Check to see if this string is commented out.
		//
		if( string != NULL && string[0] == L'/' && string[1] == L'/' ) {
			string			+= 2;
			display_text	= false;
		}

		//
		//	Display the text on the screen
		//
		if ( display_text && string != NULL && string[0] != 0 ) {
			float message_duration = max( duration, 5.0F );
			CombatManager::Get_Message_Window ()->Add_Message( string, Vector3 (1, 1, 1),
																speaker, message_duration );
		}
		
		//
		//	Play the facial animation (if one exists)
		//
		if ( speaker != NULL && speaker->HeadModel != NULL) {
			HAnimClass *head_anim = NULL;

			//
			//	Dig out the skeleton name for the head model
			//
			StringClass anim_root_name;
			const HTreeClass *htree = speaker->HeadModel->Get_HTree ();
			if (htree != NULL) {
				anim_root_name = htree->Get_Name ();
				anim_root_name += ".";
			}
			
			//
			//	Determine if we should use a pre-canned animation, or
			// if we should build one dynamically
			//
			if ( anim_name == NULL || anim_name[0] == 0 ) {
				
				//
				//	Build an animation dynamically
				//
				const char *english_string = translate_obj->Get_English_String();
				if ( english_string != NULL && english_string[0] != 0 ) {
					if (speaker->SpeechAnim->Generate_Animation( english_string, duration )) {
						head_anim = speaker->SpeechAnim;
					}
				}
				
			} else {
				
				//
				//	Build a string containing the fully qualified animation name
				//
				StringClass full_anim_name;
				if ( ::strchr( anim_name, '.' ) == 0 ) {
					full_anim_name	= anim_root_name;
					full_anim_name	+= anim_name;
				} else {
					full_anim_name = anim_name;
				}

				head_anim = WW3DAssetManager::Get_Instance()->Get_HAnim( full_anim_name );
			}

			//
			//	Recreate the facial animation when the speech is complete
			//
			speaker->GenerateIdleFacialAnimTimer = duration + 1.0F;
			
			//
			//	Play the animation on the head model (if one exists)
			//
			if ( speaker->HeadModel != NULL && head_anim != NULL ) {
				speaker->HeadModel->Set_Animation( head_anim, 0, RenderObjClass::ANIM_MODE_ONCE );
			}			
		}
	}

	return duration;
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Stop_Current_Speech( void )
{
	//
	//	Kill the current sound we are making (speech, scream, grunt, etc)
	//
	if ( CurrentSpeech != NULL ) {
		GenerateIdleFacialAnimTimer = 0;
		CurrentSpeech->Stop();
		CurrentSpeech->Remove_From_Scene();
		CurrentSpeech->Release_Ref();
		CurrentSpeech = NULL;
	}
	
	return ;
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin )
{
	if ( !Is_In_Vehicle() ) {
		SmartGameObj::Apply_Damage( damager, scale, alternate_skin );
	}
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Apply_Damage_Extended( const OffenseObjectClass & damager, float scale,
			const	Vector3 & direction, const char * collision_box_name )
{
	// If negative damage, just apply
	if ( Get_Defense_Object()->Is_Repair( damager ) ) {
//	if ( damager.Get_Damage() * scale < 0 ) {
		SmartGameObj::Apply_Damage_Extended( damager, scale, direction, collision_box_name );

		// Add electric effect
		if ( HealingEffect == NULL ) {
			HealingEffect = CombatMaterialEffectManager::Get_Health_Effect();
			if ( HealingEffect != NULL ) {
				Peek_Human_Phys()->Add_Effect_To_Me( HealingEffect );
			}
		}

		if ( HealingEffect != NULL ) {
			HealingEffect->Set_Target_Parameter(0.495f);	// go almost halfway
		}

		return;
	}

	// Do this after the repair effect
/*	if ( !CombatManager::I_Am_Server() ) {		Clients can apply damage
		return;
	}*/

	if ( Is_In_Vehicle() ) {
		return;
	}

	bool ambushed = false;
	// Ambush Kill
	if ( ( Get_AI_State() == AI_STATE_IDLE ) || ( Get_AI_State() == AI_STATE_SECONDARY_IDLE ) ) {
		// Only ambush if there is an owner to the damage, and the target is human controlled
		if ( damager.Get_Owner() != NULL && !Is_Human_Controlled() ) {
			scale *= AMBUSH_DAMAGE_SCALE;
			ambushed = true;
//			Debug_Say(( "Ambush Damage!!\n" ));
		}
	}

	bool anim_ok = true;

	Reset_Hibernating();		// Wake up when damaged

	int ouch_type = Get_Ouch_Type( direction, collision_box_name );

	// Stats
	PlayerDataClass * damager_data = NULL;
	if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() ) {
		damager_data = damager.Get_Owner()->As_SoldierGameObj()->Get_Player_Data();
	}

	// Dont add stats for "non-directional" damage.  E.g. standing in a tiberium
	// field should not count as thousands of hits.
	if (direction.Length2() > WWMATH_EPSILON) {
		switch( ouch_type ) {
			case HumanStateClass::HEAD_FROM_BEHIND:
			case HumanStateClass::HEAD_FROM_FRONT:
				if ( Get_Player_Data() != NULL ) {
					Get_Player_Data()->Stats_Add_Head_Hit();
				}
				if ( damager_data != NULL ) {
					damager_data->Stats_Add_Head_Shot();
				}
				break;

			case HumanStateClass::TORSO_FROM_BEHIND:
			case HumanStateClass::TORSO_FROM_FRONT:
				if ( Get_Player_Data() != NULL ) {
					Get_Player_Data()->Stats_Add_Torso_Hit();
				}
				if ( damager_data != NULL ) {
					damager_data->Stats_Add_Torso_Shot();
				}
				break;

			case HumanStateClass::LEFT_ARM_FROM_BEHIND:
			case HumanStateClass::LEFT_ARM_FROM_FRONT:
			case HumanStateClass::RIGHT_ARM_FROM_BEHIND:
			case HumanStateClass::RIGHT_ARM_FROM_FRONT:
				if ( Get_Player_Data() != NULL ) {
					Get_Player_Data()->Stats_Add_Arm_Hit();
				}
				if ( damager_data != NULL ) {
					damager_data->Stats_Add_Arm_Shot();
				}
				break;

			case HumanStateClass::LEFT_LEG_FROM_BEHIND:
			case HumanStateClass::LEFT_LEG_FROM_FRONT:
			case HumanStateClass::RIGHT_LEG_FROM_BEHIND:
			case HumanStateClass::RIGHT_LEG_FROM_FRONT:
				if ( Get_Player_Data() != NULL ) {
					Get_Player_Data()->Stats_Add_Leg_Hit();
				}
				if ( damager_data != NULL ) {
					damager_data->Stats_Add_Leg_Shot();
				}
				break;

			case HumanStateClass::GROIN:
				if ( Get_Player_Data() != NULL ) {
					Get_Player_Data()->Stats_Add_Crotch_Hit();
				}
				if ( damager_data != NULL ) {
					damager_data->Stats_Add_Crotch_Shot();
				}
				break;
		}
	}

	//	Determine what (if any) dialogue to say
	int dialogue_id = -1;
	ArmedGameObj *damage_owner = damager.Get_Owner();
	if( damage_owner != NULL ) {
		
		//	Were we injured by a friend or foe?
		if ( Is_Enemy( damage_owner ) ) {
			dialogue_id = DIALOG_ON_TAKE_DAMAGE_FROM_ENEMY;
		} else {
			dialogue_id = DIALOG_ON_TAKE_DAMAGE_FROM_FRIEND;
		}		
	}

	float health_before = Get_Defense_Object()->Get_Health();
	float armor_before = Get_Defense_Object()->Get_Shield_Strength();

	if ( collision_box_name != NULL ) {
		char * start = ::strchr( collision_box_name, '.' );
		if ( start != NULL ) {
			start++;
			float bone_scale = BonesManager::Get_Bone_Damage_Scale( start );
			scale *= bone_scale;
		}
	}

	// Update damage indicators
	if ( this == COMBAT_STAR ) {
		if ( Get_Defense_Object()->Would_Damage( damager ) && !damager.ForceServerDamage ) {
			if ( direction.Length() == 0 ) {
				for ( int i = 0; i < 8; i++ ) {
					CombatManager::Show_Star_Damage_Direction( i );
				}
			// Set all
			} else {
				Vector3 relative_direction = Get_Transform().Inverse_Rotate_Vector( direction );
				// Convert direction into 0 .. 7
				float angle = ::WWMath::Atan2 ( relative_direction.Y, -relative_direction.X);
				int dir = (int)(8.0f * angle / DEG_TO_RAD( 360 ) + 8.5f);
				CombatManager::Show_Star_Damage_Direction( dir & 7 );
			}
		}
	}


	SmartGameObj::Apply_Damage_Extended( damager, scale, direction, collision_box_name );
	float health = Get_Defense_Object()->Get_Health();

	if ( this == COMBAT_STAR ) {
		Vector3 pos;
		COMBAT_STAR->Get_Position( &pos );
		int hitter_id = 0;
		const char * weapon_name = "";
		if ( damager.Get_Owner() ) {
			hitter_id = damager.Get_Owner()->Get_ID();
			if ( damager.Get_Owner()->Get_Weapon() ) {
				weapon_name = damager.Get_Owner()->Get_Weapon()->Get_Definition()->Get_Name();
			}
		}
		const char * body_part = ( collision_box_name != NULL ) ? collision_box_name : "";
		float armor = Get_Defense_Object()->Get_Shield_Strength();
	   DIAG_LOG(( "DRCE", "%s; %d; %s; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", weapon_name, hitter_id, body_part, armor, health, pos.X, pos.Y, pos.Z ));
	}

	if (( damager.Get_Owner() == COMBAT_STAR ) && ( COMBAT_STAR != NULL )) {
		Vector3 pos;
		COMBAT_STAR->Get_Position( &pos );
		const char * weapon_name = "";
		int ammo = 0;
		if ( COMBAT_STAR->Get_Weapon() ) {
			weapon_name = COMBAT_STAR->Get_Weapon()->Get_Definition()->Get_Name();
			ammo = COMBAT_STAR->Get_Weapon()->Get_Total_Rounds();
		}
		int hittee_id = Get_ID();
		const char * body_part = ( collision_box_name != NULL ) ? collision_box_name : "";
		float armor = Get_Defense_Object()->Get_Shield_Strength();
		Vector3 victim_pos;
		Get_Position( &victim_pos );
		const char * team_name = Player_Type_Name( Get_Player_Type() );
	   DIAG_LOG(( "DEFE", "%1.2f; %1.2f; %1.2f; %s; %d; %d; %s; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s ", pos.X, pos.Y, pos.Z, weapon_name, ammo, hittee_id, body_part, armor, health, victim_pos.X, victim_pos.Y, victim_pos.Z, team_name ));
	}

	if ( ambushed ) {
		int victim_id = Get_ID();
		Vector3 victim_pos;
		Get_Position( &victim_pos );
		Vector3 shooter_pos;
		damager.Get_Owner()->Get_Position( &shooter_pos );
		float armor = Get_Defense_Object()->Get_Shield_Strength();
		DIAG_LOG(( "AMGE", "%1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", victim_pos.X, victim_pos.Y, victim_pos.Z, victim_id, shooter_pos.X, shooter_pos.Y, shooter_pos.Z, armor, health ));
	}


	if (health == health_before && Get_Defense_Object()->Get_Shield_Strength() == armor_before) {
		//
		// No damage was applied. 
		// This is a hack. 
		//

		// Handle zero health bug
		if ( health > 0 || 
				HumanState.Get_State() == HumanStateClass::DEATH || 
				HumanState.Get_State() == HumanStateClass::DESTROY ) {
			return;
		} else {
			Debug_Say(( "Solving Zero health Bug %f %d\n", health, HumanState.Get_State() ));
		}
	}

	// Can't start special damage unless damage was done.

	// Check for special damage
	int warhead = damager.Get_Warhead();
	ArmorWarheadManager::SpecialDamageType special_damage = ArmorWarheadManager::Get_Special_Damage_Type( warhead );
	if ( special_damage != 0 ) {
		float probability = ArmorWarheadManager::Get_Special_Damage_Probability( warhead );
		if (health <= 0) {
			probability = 1;
		}
		if ( FreeRandom.Get_Float() < probability ) {
			// if skin is not impervious to the damage
			int skin = Get_Defense_Object()->Get_Skin();
			ArmorWarheadManager::SpecialDamageType special_damage = ArmorWarheadManager::Get_Special_Damage_Type( warhead );
			if ( !ArmorWarheadManager::Is_Skin_Impervious( special_damage, skin ) ) {
				Set_Special_Damage_Mode( special_damage, damager.Get_Owner() );
			}
		}
	}

	if ( this == COMBAT_STAR ) {
		if ( 	_shake_delay < 0 ) {
			_shake_delay = 0.6f;		// don't shake/sound too fast

			// And Shake the camera
			Vector3	pos = COMBAT_CAMERA->Get_Transform().Get_Translation();
			COMBAT_SCENE->Add_Camera_Shake(	pos,	1, 0.5f, 0.05f );
		}

		anim_ok = false;
		if ( 	_cry_delay < 0 ) {
			_cry_delay = 2;		// don't shake/sound too fast
			anim_ok = true;

			// Play ouch sound
			AudibleSoundClass * sound = WWAudioClass::Get_Instance()->Create_Sound( "Take_Damage_Sound" );
			if ( sound ) {
				sound->Play();
				sound->Release_Ref();
			}
		}

	}


	if ( health <= 0 ) {

		// Check for creating visceroids
		if ( IS_MISSION ) {
			int warhead = damager.Get_Warhead();
			float visceroid_probability = ArmorWarheadManager::Get_Visceroid_Probability( warhead );
			if ( !Is_Human_Controlled() && visceroid_probability != 0 ) {
				if ( FreeRandom.Get_Float() < visceroid_probability ) {

					#define	VISCEROID_NAME			"Visceroid"
					// Not from other visceroids
					if ( ::stricmp( Get_Definition().Get_Name(), VISCEROID_NAME ) != 0 ) {
						// Create a visceroid
						PhysicalGameObj * vis = ObjectLibraryManager::Create_Object( VISCEROID_NAME );
						if ( vis != NULL ) {
							vis->Set_Transform( Get_Transform() );
							vis->Start_Observers ();
							Set_Delete_Pending();		// Kill me
							Debug_Say(( "Made Visceroid\n" ));
						}
					}
				}
			}
		}

		Enable_Hibernation( false );

		// Stats
		SoldierGameObj * damager_owner = NULL;
		if ( damager.Get_Owner() && damager.Get_Owner()->As_SoldierGameObj() ) {
			damager_owner = damager.Get_Owner()->As_SoldierGameObj();
		}
		if ( damager_owner && damager_owner->Get_Player_Data() ) {

			if ( Is_Teammate( damager_owner ) ) {
				damager_owner->Get_Player_Data()->Stats_Add_Ally_Killed();
			}

			if ( Is_Enemy( damager_owner ) ) {
				damager_owner->Get_Player_Data()->Stats_Add_Enemy_Killed();
			}

			if ( damager_owner->Get_Vehicle() ) {
				damager_owner->Get_Player_Data()->Stats_Add_Kill_From_Vehicle();
			}
		}

		if ( HumanState.Get_State() != HumanStateClass::DEATH ) {

			if ( this == COMBAT_STAR ) {
				CombatManager::Register_Star_Killer( damager.Get_Owner() );
			}

			// Play death animation
			HumanState.Set_State( HumanStateClass::DEATH, ouch_type );

			//
			//	Stop any sound we WERE making
			//
			Stop_Current_Speech ();

			int death_sound_id = Get_Definition ().DeathSoundPresetID;
			if (death_sound_id == 0) {

				//
				//	Lookup the global death sound
				//
				GlobalSettingsDef *global_settings = GlobalSettingsDef::Get_Global_Settings ();
				if (global_settings != NULL) {
					death_sound_id = global_settings->Get_Death_Sound_ID();
				}				
			}

			//
			//	Reveal this object to the player's encyclopedia
			//
			if ( damager.Get_Owner () == COMBAT_STAR ) {
				EncyclopediaMgrClass::Reveal_Object( this );
			}

			//
			//	Play the death sound
			//
			if (death_sound_id != 0) {
				Matrix3D tm = Get_Transform();
				RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference(this);
				WWAudioClass::Get_Instance()->Create_Instant_Sound( death_sound_id, tm, owner_ref );
				REF_PTR_RELEASE(owner_ref);
			}


			// Play death explosion, since we don't call Completely_Damaged
			if ( Get_Definition().KilledExplosion != 0 ) {
				Vector3 pos;
				Get_Position(&pos);
				WWASSERT(pos.Is_Valid());// most likely candidate for explosion damage bug....?
				ExplosionManager::Create_Explosion_At( Get_Definition().KilledExplosion, pos, damager.Get_Owner() );	 // no one gets credit for this
			}

			//
			//	Queue the guy who killed us...
			//
			if (	damage_owner != NULL &&
					damage_owner != this &&
					damage_owner->As_SoldierGameObj () != NULL )
			{
				SoldierGameObj *our_killer = damage_owner->As_SoldierGameObj  ();
				
				//
				//	Were we killed by a friend or foe?
				//
				if ( Is_Enemy( damage_owner ) ) {			
					our_killer->Say_Dialogue( DIALOG_ON_KILLED_ENEMY );
				} else {
					our_killer->Say_Dialogue( DIALOG_ON_KILLED_FRIEND );
				}		
			}

		}

	} else {
		// Play wound animation
		// If state is interruptable.....
		if ( Get_State() == HumanStateClass::UPRIGHT && anim_ok ) {
			HumanState.Set_State( HumanStateClass::WOUNDED, ouch_type );
		}
	}

	if( CurrentSpeech == NULL && dialogue_id >= 0 ) {
		Say_Dialogue( dialogue_id );
	}
}


//------------------------------------------------------------------------------------
void	SoldierGameObj::Exit_Ladder( void ) 
{
	HumanState.Set_State( HumanStateClass::UPRIGHT );
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Enter_Ladder( bool top ) 
{
	HumanState.Set_State( HumanStateClass::LADDER );

	if ( Is_Human_Controlled() ) {
		if ( top ) {
			LadderUpMask = true;
		} else {
			LadderDownMask = true;
		}
	}
}

//------------------------------------------------------------------------------------
HumanPhysClass	*SoldierGameObj::Peek_Human_Phys( void ) const 
{ 
	WWASSERT( Peek_Physical_Object() ); 
	WWASSERT( Peek_Physical_Object()->As_HumanPhysClass() ); 
	return Peek_Physical_Object()->As_HumanPhysClass(); 
}

//------------------------------------------------------------------------------------
/*
** Vehicles
*/
void	SoldierGameObj::Enter_Vehicle( VehicleGameObj * vehicle, const char * anim_name )
{
//	WWASSERT( Get_State() != HumanStateClass::IN_VEHICLE );

	Vehicle = vehicle;
	HumanState.Set_State( HumanStateClass::IN_VEHICLE );

	AnimationName = anim_name;
	HumanState.Force_Animation( anim_name, false );

	if ( this == COMBAT_STAR && vehicle != NULL ) {
		Vector3 pos;
		Vehicle->Get_Position( &pos );
		int ammo = 0;
		if ( vehicle->Get_Weapon() ) {
			ammo = vehicle->Get_Weapon()->Get_Total_Rounds();
		}
		DefenseObjectClass * defense = vehicle->Get_Defense_Object();
		DIAG_LOG(( "ENVE", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), ammo ));
	}
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Exit_Vehicle( void ) 
{
	if ( this == COMBAT_STAR && Vehicle  != NULL ) {
		Vector3 pos;
		Vehicle->Get_Position( &pos );
		int ammo = 0;
		if ( Vehicle->Get_Weapon() ) {
			ammo = Vehicle->Get_Weapon()->Get_Total_Rounds();
		}
		DefenseObjectClass * defense = Vehicle->Get_Defense_Object();
		DIAG_LOG(( "EXVE", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %d", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health(), ammo ));
	}

	Vehicle = NULL;
	HumanState.Set_State( HumanStateClass::UPRIGHT );
}

void	SoldierGameObj::Exit_Destroyed_Vehicle( int seat_num, const Vector3 & vehicle_pos )
{
	Vehicle = NULL;
	HumanState.Set_State( HumanStateClass::UPRIGHT );

	Vector3 extent = Peek_Human_Phys()->Get_Collision_Box().Extent;
	float width = MAX( extent.X, extent.Y ) * 1.1f;

	if ( Peek_Model() ) {
		Peek_Model()->Set_Hidden( false );
	}

	Vector3 offsets[4] = {
		Vector3( -1.0f, -1.0f, 0 ),
		Vector3( -1.0f,  1.0f, 0 ),
		Vector3(  1.0f, -1.0f, 0 ),
		Vector3(  1.0f,  1.0f, 0 ),
	};

	Vector3 pos = vehicle_pos + offsets[ seat_num & 3 ] * width;

	Matrix3D tm( pos );
	if ( Peek_Human_Phys()->Can_Teleport( tm ) ) {
		Peek_Human_Phys()->Set_Position( pos );
	} else {
		Vector3 new_pos = pos;
		Peek_Human_Phys()->Find_Teleport_Location( pos, 4, &new_pos );
		Peek_Human_Phys()->Set_Position( new_pos );
	}

}

//------------------------------------------------------------------------------------
bool SoldierGameObj::Is_Permitted_To_Enter_Vehicle(void)
{
	if (!CombatManager::Is_Gameplay_Permitted()) {
		//
		// You can't enter a vehicle if there are no opponents, etc
		//
		return false;
	}

	/*
	//
	// A soldier cannot enter a vehicle if he is carrying a flag
	// 	
	if (CtfTeamFlag == NO_FLAG) {
		return true;
	} else {
		return false;
	}
	*/

	return true;
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Get_Velocity(Vector3 & vel)
{
	Peek_Human_Phys()->Get_Velocity(&vel);
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Velocity(Vector3 & vel)
{
	Peek_Human_Phys()->Set_Velocity(vel);
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Give_All_Weapons(void)
{
   //WWASSERT(CombatManager::I_Am_Server());
//	WWASSERT(PlayerType != PLAYERTYPE_NEUTRAL);

	// For all weapon defs with the AGiveWeaponsWeapon checked, Give It!
	for ( WeaponDefinitionClass *weapon_def = (WeaponDefinitionClass *)DefinitionMgrClass::Get_First( CLASSID_DEF_WEAPON );
			weapon_def != NULL;
			weapon_def = (WeaponDefinitionClass *)DefinitionMgrClass::Get_Next( weapon_def, CLASSID_DEF_WEAPON ) ) {
		if ( weapon_def->AGiveWeaponsWeapon ) {
			WeaponBag->Add_Weapon( weapon_def->Get_Name(), -1 );
		}
	}
}

//------------------------------------------------------------------------------------
VehicleGameObj	*	SoldierGameObj::Get_Profile_Vehicle( void )
{
	if (( Get_State() == HumanStateClass::IN_VEHICLE ) && Vehicle ) {
		return Vehicle;
	}

	if (( Get_State() == HumanStateClass::TRANSITION ) && 
		( TransitionCompletionData != NULL )) {
		if ( TransitionCompletionData->Type == TransitionDataClass::VEHICLE_ENTER ) {
			return (VehicleGameObj *)TransitionCompletionData->Vehicle.Get_Ptr();
		}
	}

	return NULL;
}

bool	SoldierGameObj::Use_Ladder_View( void )
{
	if ( Get_State() == HumanStateClass::LADDER ) {
			return true;
	}

	if (( Get_State() == HumanStateClass::TRANSITION ) && 
		( TransitionCompletionData != NULL )) {
		if (( TransitionCompletionData->Type == TransitionDataClass::LADDER_ENTER_TOP ) ||
			 ( TransitionCompletionData->Type == TransitionDataClass::LADDER_ENTER_BOTTOM )) {
			return true;
		}
	}

	return false;
}

//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Model(const char *model_name)
{
	Peek_Physical_Object()->Set_Model_By_Name(model_name);
	HumanState.Set_Anim_Control( (HumanAnimControlClass *)Get_Anim_Control() );  // Must set the anim control after the phys object
}

//------------------------------------------------------------------------------------

Vector3	SoldierGameObj::Get_Bullseye_Position( void )
{
	if ( Get_Vehicle() != NULL ) {
		return Get_Vehicle()->Get_Bullseye_Position();
	}

	Vector3 pos;
	Get_Position(&pos);
	if ( Is_Crouched() ) {
		pos.Z += 0.5f;
	} else {
		pos.Z += Get_Bullseye_Offset_Z();
	}
//	pos.Z += 1.2f;
	return pos;
}

//------------------------------------------------------------------------------------

bool SoldierGameObj::Can_See(SoldierGameObj * p_soldier)
{
	WWASSERT(p_soldier != NULL);

	Vector3 ray_start = Get_Bullseye_Position();
	Vector3 ray_end = p_soldier->Get_Bullseye_Position();	// This may a big high

	Vector3 path = ray_end - ray_start;
	if (path.Length() > 1) {
		Vector3 offset = path;
		offset.Normalize();
		ray_start += offset;
	}

	LineSegClass ray(ray_start, ray_end);
	CastResultStruct result;
	PhysRayCollisionTestClass raytest(ray, &result, 
		BULLET_COLLISION_GROUP, COLLISION_TYPE_PHYSICAL);
	WWASSERT(COMBAT_SCENE != NULL);
{ WWPROFILE( "Cast Ray" );
	COMBAT_SCENE->Cast_Ray(raytest);
}

	bool can_see = false;
	
	if (raytest.CollidedPhysObj != NULL) {
		SmartGameObj * p_blocker = NULL;

		if ( raytest.CollidedPhysObj->Get_Observer() != NULL ) {
			PhysicalGameObj * p_obj = ((CombatPhysObserverClass *)raytest.CollidedPhysObj->Get_Observer())->As_PhysicalGameObj();
			if ( p_obj != NULL ) {
				p_blocker = p_obj->As_SmartGameObj();
			}
		}


		if (p_blocker != NULL && p_blocker->Get_Control_Owner() == 
			p_soldier->Get_Control_Owner()) {
			can_see = true;
		}
	}

	return can_see;
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Adjust_Skeleton( float height, float width )
{
//	Debug_Say(( "Height %f, width %f\n", height, width ));

	// Only adjust male skeletons
	Animatable3DObjClass * robj = (Animatable3DObjClass *)Peek_Model();
	if ( !robj || !robj->Get_HTree() || robj->Get_HTree()->Get_Name()[2] != 'A' ) {
		return;
	}

	HTreeClass	* tree_base = NULL;
	HTreeClass	* tree_tall = NULL;
	HTreeClass	* tree_wide = NULL;

	if ( tree_base == NULL ) {
		tree_base = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_human" );
		tree_tall = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_tall" );
		tree_wide = WW3DAssetManager::Get_Instance()->Get_HTree( "s_a_wide" );
	}

	if ( ( tree_base != NULL ) && ( tree_tall != NULL ) && ( tree_wide != NULL ) ) {

		HTreeClass *tree = HTreeClass::Create_Interpolated( tree_base, tree_tall, tree_wide,
																			 height, width );
		if ( tree ) {
			robj->Set_HTree( tree );
			delete tree;
		}
	}
}

/*
//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Ctf_Team_Flag(int team) 
{
	WWASSERT(
		team == NO_FLAG			|| 
		team == PLAYERTYPE_NOD	||
		team == PLAYERTYPE_GDI);

	CtfTeamFlag = team;

	Set_Object_Dirty_Bit( NetworkObjectClass::BIT_OCCASIONAL, true );

	if (team == NO_FLAG) {
		Set_Back_Flag_Model(NULL);
	} else {
		//Set_Back_Flag_Model("O_Flag.w3d", Get_Team_Color());
		Set_Back_Flag_Model("O_Flag.w3d", Get_Color_For_Team(team));
	}

}
*/

/*
//------------------------------------------------------------------------------------
void SoldierGameObj::Set_Precision(void)
{
	cEncoderList::Set_Precision(BITPACK_CTF_TEAM_FLAG,	NO_FLAG, (int) PLAYERTYPE_GDI);
}
*/

//------------------------------------------------------------------------------------
SoldierObserverClass	*	SoldierGameObj::Get_Innate_Controller( void )
{
	if ( Get_Definition().UseInnateBehavior ) {
		const GameObjObserverList & observer_list = Get_Observers();
		for( int index = 0; index < observer_list.Count(); index++ ) {
			if ( !strcmp( observer_list[ index ]->Get_Name(), "Innate Soldier" )  ) {
				return (SoldierObserverClass	*)observer_list[ index ];
			}
		}
	}

	return NULL;
}

//------------------------------------------------------------------------------------
const char * SoldierGameObj::Get_First_Person_Hands_Model_Name( void )
{
	return Get_Definition().FirstPersonHands;
}

//------------------------------------------------------------------------------------
void	SoldierGameObj::Get_Information( StringClass & string )
{
	SmartGameObj::Get_Information( string );

	HumanState.Get_Information( string );

	SoldierObserverClass	*	innate = Get_Innate_Controller();
	if ( innate ) {
		innate->Get_Information( string );

		StringClass disabled;
		if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_BULLET_HEARD)) {
			disabled += "B";
		}
		if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_GUNSHOT_HEARD)) {
			disabled += "G";
		}
		if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_FOOTSTEP_HEARD)) {
			disabled += "F";
		}
		if (!Is_Innate_Enabled(SOLDIER_INNATE_EVENT_ENEMY_SEEN)) {
			disabled += "S";
		}
		if (!Is_Innate_Enabled(SOLDIER_INNATE_ACTIONS)) {
			disabled += "A";
		}

		if ( !disabled.Is_Empty() ) {
			string += "Disabled:";
			string += disabled;
			string += "\n";
		}
	}
}

//------------------------------------------------------------------------------------
//void SoldierGameObj::Get_Extended_Information(StringClass & description)
void SoldierGameObj::Get_Description(StringClass & description)
{
	StringClass line;

	line.Format("ID:    %d\n", Get_ID());
	description += line;

   line.Format("NAME:  %s\n", Get_Definition().Get_Name());
	description += line;

   line.Format("TEAM:  %d\n", Get_Player_Type());
	description += line;

   line.Format("CONTR: %d\n", Get_Control_Owner());
	description += line;

	Vector3 position;
   Get_Position(&position);
	line.Format("POS:   %-5.2f, %-5.2f, %-5.2f\n", position.X, position.Y, position.Z);
	description += line;

	Vector3 target = Get_Targeting_Pos();
	line.Format("TGT:   %-5.2f, %-5.2f, %-5.2f\n", target.X, target.Y, target.Z);
	description += line;

	Vector3 velocity;
	Get_Velocity(velocity);
	line.Format("VEL:   %-5.2f, %-5.2f, %-5.2f\n", velocity.X, velocity.Y, velocity.Z);
	description += line;

	WeaponClass	* p_weapon = Get_Weapon();
	if (p_weapon != NULL) {
		line.Format("WEAP:  %s\n", p_weapon->Get_Name());
		description += line;

		line.Format("TRNDS: %d\n", p_weapon->Get_Total_Rounds());
		description += line;
   }

	line.Format("HLTH:  %-5.2f\n", DefenseObject.Get_Health());
	description += line;

	line.Format("HMAX:  %-5.2f\n", DefenseObject.Get_Health_Max());
	description += line;

	HumanPhysClass * p_human_phys = Peek_Human_Phys();
	if (p_human_phys != NULL) {
		line.Format("FACE:  %-5.2f\n", p_human_phys->Get_Facing());
		description += line;
	}

	line.Format("STATE: %s", Get_State_Name());
	if (Is_Crouched()) {
		line += " (Crouched)";
	}
	line += "\n";
	description += line;

	line.Format("ANIM:  %s\n", AnimationName);
	description += line;

   if (Vehicle != NULL) {
		line.Format("VEH:   %d\n", Vehicle->Get_ID());
		description += line;
   }

	const GameObjObserverList & observer_list = Get_Observers();
	line.Format("#OBSV: %d\n", observer_list.Count());
	description += line;

	for (int index = 0; index < observer_list.Count(); index++) {
		line.Format("       %s\n", observer_list[index]->Get_Name());
		description += line;
	}
	
	line.Format("INNAT: %d\n", Is_Innate_Enabled());
	description += line;

	line.Format("HIB:   %d\n", Is_Hibernating());
	description += line;

	line.Format("CTRL:  %d, %d, %5.2f, %5.2f, %5.2f, %5.2f\n", 
		Control.Get_One_Time_Boolean_Bits(), 
		Control.Get_Continuous_Boolean_Bits(),
		Control.Get_Analog(ControlClass::ANALOG_MOVE_FORWARD),
		Control.Get_Analog(ControlClass::ANALOG_MOVE_LEFT),
		Control.Get_Analog(ControlClass::ANALOG_MOVE_UP),
		Control.Get_Analog(ControlClass::ANALOG_TURN_LEFT));
	description += line;

	ActionClass * p_action = Get_Action();
	if (p_action != NULL)
	{
		line.Format("ACTCT: %d\n", p_action->Get_Act_Count());
		description += line;

		Vector3 move_loc = p_action->Get_Parameters().MoveLocation;
		line.Format("MVLOC: (%5.2f, %5.2f, %5.2f)\n", move_loc.X, move_loc.Y, move_loc.Z);
		description += line;
	}

	line.Format("stlth: %d\n", Is_Stealth_Enabled());
	description += line;

	line.Format("   on: %d\n", Is_Stealthed());
	description += line;

	line.Format("ISC:   %d\n", Get_Import_State_Count());
	description += line;
}

//------------------------------------------------------------------------------------

struct {
	ArmorWarheadManager::SpecialDamageType Mode;
	const char * EmitterName;
	const char * BoneName;
} _SpecialDamageEmitters[] = {
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE,	"AG_FLAME01",		"C SPINE" },

	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C SPINE" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C L UPPERARM" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C R UPPERARM" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C L CALF" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C R CALF" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE,	"AG_FLAME01",		"C NECK" },
//	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE,	"E_FLAME01",		"C L UPPERARM" },
//	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE,	"E_FLAME01",		"C R UPPERARM" },
	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM,	"e_tib_dump",		"C SPINE" },
//	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM,	"e_tib_dump",		"C L UPPERARM" },
//	{ ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_CHEM,	"e_tib_dump",		"C R UPPERARM" },
};
#define NUM_SPECIAL_DAMAGE_EMITTERS	(sizeof(_SpecialDamageEmitters)/sizeof(_SpecialDamageEmitters[0]))

void	SoldierGameObj::Set_Special_Damage_Mode( ArmorWarheadManager::SpecialDamageType mode, ArmedGameObj * damager )
{
	// Check for turning off
	if ( mode == ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE ) {

		// remove all subobjs from the bones used
		for ( int emitter = 0; emitter < NUM_SPECIAL_DAMAGE_EMITTERS; emitter++ ) {
			if ( _SpecialDamageEmitters[emitter].Mode == SpecialDamageMode ) {
				int bone = Peek_Model()->Get_Bone_Index( _SpecialDamageEmitters[emitter].BoneName );
				int sub_count = Peek_Model()->Get_Num_Sub_Objects_On_Bone( bone );
				while ( --sub_count >= 0 ) {
					RenderObjClass * sub = Peek_Model()->Get_Sub_Object_On_Bone( sub_count, bone );
					if ( ::stricmp(sub->Get_Name(), _SpecialDamageEmitters[emitter].EmitterName ) == 0 ) {
						Peek_Model()->Remove_Sub_Object( sub );
					}
					sub->Release_Ref();
				}
			}
		}

		if ( SpecialDamageEffect != NULL ) {
			SpecialDamageEffect->Set_Target_Parameter(0);
			SpecialDamageEffect->Enable_Remove_On_Complete(true);
			REF_PTR_RELEASE( SpecialDamageEffect );
		}

		if ( !Is_Human_Controlled() && IS_MISSION ) {
			if ( HumanState.Get_State() >= HumanStateClass::ON_FIRE && 
				HumanState.Get_State() <= HumanStateClass::ON_CNC_CHEM ) {
				HumanState.Set_State( HumanStateClass::UPRIGHT );
			}
		}

		SpecialDamageDamager = NULL;

	} else {

		if ( mode != SpecialDamageMode ) {

			// Remove old mode
			Set_Special_Damage_Mode( ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_NONE );

			if ( Allow_Special_Damage_State_Lock() && IS_MISSION && mode != ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_SUPER_FIRE ) {
				HumanState.Set_State( (HumanStateClass::HumanStateType)( mode - 
							ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_FIRE + HumanStateClass::ON_FIRE ) );
			}

			// Add new emitters
			for ( int emitter = 0; emitter < NUM_SPECIAL_DAMAGE_EMITTERS; emitter++ ) {
				if ( _SpecialDamageEmitters[emitter].Mode == mode ) {
					RenderObjClass * ro = WW3DAssetManager::Get_Instance()->Create_Render_Obj( 
																		_SpecialDamageEmitters[emitter].EmitterName );
					if ( ro != NULL ) {
						Peek_Model()->Add_Sub_Object_To_Bone( ro, _SpecialDamageEmitters[emitter].BoneName );
						ro->Release_Ref();
					}
				}
			}

			// Add electric effect
			if ( mode == ArmorWarheadManager::SPECIAL_DAMAGE_TYPE_ELECTRIC ) {
				WWASSERT( SpecialDamageEffect == NULL );
				SpecialDamageEffect = CombatMaterialEffectManager::Get_Electrocution_Effect();
				if ( SpecialDamageEffect != NULL ) {
					SpecialDamageEffect->Set_Target_Parameter(0.49f);	// go almost halfway
					Peek_Human_Phys()->Add_Effect_To_Me( SpecialDamageEffect );
				}
			}

			// Save Damager
			SpecialDamageDamager = damager;
		}

	}

	SpecialDamageMode = mode;
	SpecialDamageTimer = ArmorWarheadManager::Get_Special_Damage_Duration( SpecialDamageMode );

}

//-----------------------------------------------------------------------------
void SoldierGameObj::Perturb_Position(float max_perturb)
{
	//
	// Jiggle the soldier to a non-intersecting position
	//
	Vector3 initial_position;
	Get_Position(&initial_position);
	Vector3 new_position;
	WWASSERT(Peek_Human_Phys() != NULL);
	bool succeeded = Peek_Human_Phys()->Find_Teleport_Location(
		initial_position, max_perturb, &new_position);
	if (succeeded) {
		Set_Position(new_position);
	}
}

/*
**
*/
void	SoldierGameObj::Toggle_Fly_Mode( void )
{
	InFlyMode = !InFlyMode;

	if ( InFlyMode ) {
		HumanState.Set_State( HumanStateClass::DEBUG_FLY );
		Peek_Physical_Object()->Set_Collision_Group( UNCOLLIDEABLE_GROUP );
	} else {
		HumanState.Set_State( HumanStateClass::UPRIGHT );
		Peek_Physical_Object()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );
	}
}


/*
**
*/
void	SoldierGameObj::Set_AI_State( SoldierAIState state )
{
	//
	//	Is the soldier changing states?
	//
	if ( AIState != state ) {		

		//
		//	Kill the conversation (if necessary)
		//
		if (	state >= AI_STATE_SEARCH && state > AIState &&
				ActiveConversation != NULL &&
				ActiveConversation->Is_Interruptable ())
		{
			ActiveConversation->Stop_Conversation (ACTION_COMPLETE_CONVERSATION_INTERRUPTED);
		}

		AIState = state;
	}

	return ;
}


/*
** Soldiers maintain all RenderObjs they have created for later use and saving
** This is used for weapon models
*/
void	SoldierGameObj::Add_RenderObj( RenderObjClass * obj )
{
	RenderObjList.Add( obj );
	obj->Add_Ref();
}

RenderObjClass *	SoldierGameObj::Find_RenderObj( const char * name )
{
	for ( int i = 0; i < RenderObjList.Count(); i++ ) {
		if ( !stricmp( RenderObjList[i]->Get_Name(), name ) ) {
			return RenderObjList[i];
		}
	}
	return NULL;
}

void	SoldierGameObj::Reset_RenderObjs( void )
{
	while ( RenderObjList.Count() ) {
		RenderObjList[0]->Release_Ref();
		RenderObjList.Delete( 0 );
	}
}






























































/*
//-----------------------------------------------------------------------------
void SoldierGameObj::Change_Flag_Status(int ctf_team_flag)
{
   WWASSERT(CombatManager::I_Am_Only_Client());

	if (ctf_team_flag == NO_FLAG) {
		//
		// We dropped a flag
		//
		WWASSERT(WWAudioClass::Get_Instance() != NULL);
		WWAudioClass::Get_Instance()->Create_Instant_Sound("Drop_Flag", Get_Transform());

		float speed_factor = 100 / (float) CombatManager::Get_Max_Speed_Pc(this);
		Set_Max_Speed(speed_factor * Get_Max_Speed());

		Set_Ctf_Team_Flag(ctf_team_flag);

	} else {
		//
		// We picked up a flag
		//
		WWASSERT(WWAudioClass::Get_Instance() != NULL);
		WWAudioClass::Get_Instance()->Create_Instant_Sound("Pick_Up_Flag", Get_Transform());

		Set_Ctf_Team_Flag(ctf_team_flag);

		float speed_factor = CombatManager::Get_Max_Speed_Pc(this) / 100.0f;
		Set_Max_Speed(speed_factor * Get_Max_Speed());
	}
}
*/


/*
void SoldierGameObj::Interpret_Sc_Position_Data(Vector3 & sc_position)
{
   WWASSERT(CombatManager::I_Am_Only_Client());

	switch (CombatManager::Get_Client_Interpolation_Model()) {

		case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE:
			//
			// Simplest case - pop to location given by server
			//
			//Set_Position(sc_position);

			if (Get_State() == HumanStateClass::TRANSITION) {
				//
				// Pop
				//
				Set_Position(sc_position);
				if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
					Debug_Say(("Popping own soldier at time %s\n", cMiscUtil::Get_Text_Time()));
				}
			} else {
				Vector3 current_position;
				Get_Position(&current_position);
				Vector3 pos_error = current_position - sc_position;
				if (pos_error.Length() > 2.0f) {
					Set_Position(sc_position);
					if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
						Debug_Say(("Popping own soldier at time %s\n", cMiscUtil::Get_Text_Time()));
					}
				}
			}

			break;

		case CLIENT_INTERPOLATION_SERVER_AUTHORITATIVE_WITH_BLENDING:
			if (Get_State() == HumanStateClass::TRANSITION) {
				//
				// Pop
				//
				Set_Position(sc_position);
			} else {
				//
				// Blend to position given by server
				//
				Vector3 current_position;
				Get_Position(&current_position);
				Set_Position(0.50 * current_position + 0.50 * sc_position);
			}
			break;

		case CLIENT_INTERPOLATION_PATHFIND: {

			Vector3 current_position;
			Get_Position(&current_position);
			Vector3 pos_error = current_position - sc_position;

			if (pos_error.Length() > 0.02f) {

				if (Get_Control_Owner() == CombatManager::Get_My_Id()) {
					if (pos_error.Length() > 2) {
						//
						// Client is authoritative over his own guy, unless error
						// becomes significant. In this case, pop.
						//
						Set_Position(sc_position);
					}
				} else {

					//if (pos_error.Length() > 20) {
					//	//
					//	// Pop
					//	//
					//	Set_Position(sc_position);
					//} else {
      
						//
						// For NPC's the server could transmit the final destination as well as
						// the proper location. Pop if appropriate to get as close to 
						// proper dest as possible, but pathfind instead to the final dest.
						//
						ActionParamsStruct parameters;
						parameters.Priority = 1;
						parameters.MoveLocation = sc_position;
						parameters.MoveArrivedDistance = 0.1f;
						WWASSERT(Get_Action() != NULL);
						Get_Action()->Goto(parameters);
					//}
				}
			}
			break;
		}

		default:
			WWASSERT(0);
	}
}
*/

/*
void	SoldierGameObj::Think_Pathfind( void )
{
	// CLIENT_PATHFINDING

   //
   // Pop pathfinding client-side soldiers at the earliest opportunity - 
   // as soon as we find them out of the camera fulcrum.
   //
   if (CombatManager::I_Am_Only_Client() && 
      Get_Control_Owner() != CombatManager::Get_My_Id()) {
		   
      Vector3 current_position;
		Get_Position(&current_position);

		ActionClass * p_action = Get_Action();
		WWASSERT(p_action != NULL);

		const Vector3 height_offset(0, 0, 0.9f);

		Vector3 from_position = current_position + height_offset;
		Vector3 to_position = p_action->MovementLocation + height_offset;

		//
		// We can only pop if he is not presently in the camera fulcrum.
		//
		if (p_action->MovementAction != NULL &&
         !CombatManager::Is_In_Camera_Frustrum(from_position)) {

			if (!CombatManager::Is_In_Camera_Frustrum(to_position)) {
				//
				// If the correct position is also out of the fulcrum, pop
				// all the way there.
				//
				Set_Position(p_action->MovementLocation);
				p_action->Set_Movement(NULL);
			} else {

				//
				// We are crossing into the fulcrum. Pop as close to the edge
				// of the fulcrum as possible and pathfind from there.
				//
				
				//Debug_Say(("FRAME\n"));
				bool is_finished = false;
				do {

					Vector3 next_pos;
					p_action->Movement_Act(next_pos);
					
					//if (CombatManager::Is_In_Camera_Frustrum(next_pos + height_offset)) {
					//	is_finished = true;
					//	
					//	//
					//	// Logically it would be better if the next line could be omitted.
					//	// However this seems to screw things up.
					//	//
					//	Set_Position(next_pos);
					//
					//	//Debug_Say(("Finished skipping."));
					//} else {
					//	Set_Position(next_pos);
					//
					//	//Debug_Say(("Skipping to (%5.2f, %5.2f, %5.2f)\n", 
					//	//	next_pos.X, next_pos.Y, next_pos.Z));
					//}
					
					Set_Position(next_pos);

					Vector3 adjusted_next_pos = next_pos + height_offset;
					if (CombatManager::Is_In_Camera_Frustrum(adjusted_next_pos)) {
						is_finished = true;
					}

				} while ((p_action->MovementAction != NULL) && !is_finished);			
			}
      }	
	}
}

void SoldierGameObj::Think_Pathfind(void)
{
   //
   // Pop pathfinding client-side soldiers at the earliest opportunity - 
   // as soon as we find them out of the camera fulcrum.
   //

   if ((CombatManager::Get_Client_Interpolation_Model() == CLIENT_INTERPOLATION_PATHFIND) &&
		CombatManager::I_Am_Only_Client() && 
      Get_Control_Owner() != CombatManager::Get_My_Id()) {
		   
      Vector3 current_position;
		Get_Position(&current_position);

		ActionClass * p_action = Get_Action();
		WWASSERT(p_action != NULL);

		ActionParamsStruct parameters = p_action->Get_Parameters();

		const Vector3 height_offset(0, 0, 0.9f);

		Vector3 from_position = current_position + height_offset;
		Vector3 to_position = parameters.MoveLocation + height_offset;

		//
		// We can only pop if he is not presently in the camera fulcrum.
		//
		if (!CombatManager::Is_In_Camera_Frustrum(from_position)) {

			if (!CombatManager::Is_In_Camera_Frustrum(to_position)) {
				//
				// If the correct position is also out of the fulcrum, pop
				// all the way there.
				//
				Set_Position(to_position);

			} else {

				//
				// Keep popping until we are almost in the fulcrum.
				//
				Vector3 current_pos;
				Get_Position(&current_pos);
				Vector3 last_pos;
				Vector3 test_pos;
				do {

					last_pos = current_pos;
					p_action->Act();
					Get_Position(&current_pos);
					test_pos = current_pos + height_offset;

				} while ((current_pos != last_pos) &&
					!CombatManager::Is_In_Camera_Frustrum(test_pos));

				Set_Position(last_pos);
			}
      }	
	}
}
*/

void	SoldierGameObj::Update_Healing_Effect( void )
{
	if ( HealingEffect != NULL ) {

		if (( HealingEffect->Get_Target_Parameter() >= 0.49f ) && 
			( HealingEffect->Get_Parameter() >= 0.49f ) ) {
			HealingEffect->Set_Target_Parameter( 0 );
		}

		if (( HealingEffect->Get_Target_Parameter() == 0 ) && 
			( HealingEffect->Get_Parameter() == 0 ) ) {
			Peek_Human_Phys()->Remove_Effect_From_Me( HealingEffect );
			REF_PTR_RELEASE( HealingEffect );
		}
	}
}


void	SoldierGameObj::Enable_Ghost_Collision( bool onoff )
{
	bool is_using_ghost_collision = (Peek_Physical_Object ()->Get_Collision_Group() == SOLDIER_GHOST_COLLISION_GROUP);
	if ( onoff == is_using_ghost_collision ) {
		return ;
	}

	//
	//	Change the collision group as necessary
	//
	if ( onoff ) {
		Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_GHOST_COLLISION_GROUP );
	} else {
		Peek_Physical_Object ()->Set_Collision_Group( SOLDIER_COLLISION_GROUP );
	}

	return ;
}


bool	SoldierGameObj::Is_Safe_To_Disable_Ghost_Collision( const Vector3 &curr_pos )
{
	const Vector3 PERSONAL_SPACE_BOX_SIZE (1.5F, 1.5F, 1.0F);
	const float HUMAN_HALF_HEIGHT = 1.0F;
	bool retval = true;

	//
	//	Build a box that represents the "personal" space around the soldier
	//
	Vector3 box_pos = curr_pos + Vector3 (0, 0, HUMAN_HALF_HEIGHT);
	AABoxClass box (box_pos, PERSONAL_SPACE_BOX_SIZE);

	//
	//	Collect all the dynamic objects in this box
	//
	NonRefPhysListClass obj_list;
	PhysicsSceneClass::Get_Instance ()->Collect_Objects (box, false, true, &obj_list);

	//
	//	Loop over all the collected objects
	//
	NonRefPhysListIterator it (&obj_list);
	for (it.First(); !it.Is_Done(); it.Next()) {
		PhysClass *phys_obj = it.Peek_Obj ();
		PhysicalGameObj *game_obj = NULL;

		if ( phys_obj->As_HumanPhysClass () && phys_obj->Get_Observer() != NULL ) {
			game_obj = ((CombatPhysObserverClass *)phys_obj->Get_Observer())->As_PhysicalGameObj();
		}

		//
		//	Is this a living soldier?
		//
		if (	game_obj != NULL && game_obj != this &&
				game_obj->As_SoldierGameObj() != NULL &&
				game_obj->As_SoldierGameObj()->Is_Destroyed () == false)
		{
			Vector3 block_pos;
			game_obj->Get_Position (&block_pos);

			//
			//	Lets make sure the boxes really intersect
			//
			AABoxClass block_box (block_pos + Vector3 (0, 0, 1.0F), Vector3 (0.3F, 0.3F, 1.0F));
			if (CollisionMath::Overlap_Test (box, block_box) != CollisionMath::OUTSIDE) {

				//
				//	Is the boxes intersect, then it is NOT SAFE to disable ghost collision
				// mode.
				//
				retval = false;
				break;
			}
		}
	}

	return retval;
}


bool	SoldierGameObj::Is_Soldier_Blocked( const Vector3 &curr_pos )
{
	//
	//	Only do this for soldiers who meet our criteria
	//
	if (	(Get_Action () != NULL && Get_Action ()->Is_Busy ()) ||
			Is_Destroyed () || (IS_SOLOPLAY == false))
	{
		return false;
	}

	const Vector3 PERSONAL_SPACE_BOX_SIZE (1.5F, 1.5F, 1.0F);
	const float HUMAN_HALF_HEIGHT = 1.0F;

	//
	//	Build a box that represents the "personal" space around the soldier
	//
	Vector3 box_pos = curr_pos + Vector3 (0, 0, HUMAN_HALF_HEIGHT);
	AABoxClass box (box_pos, PERSONAL_SPACE_BOX_SIZE);

	//
	//	Collect all the dynamic objects in this box
	//
	NonRefPhysListClass obj_list;
	PhysicsSceneClass::Get_Instance ()->Collect_Objects (box, false, true, &obj_list);

	uint32 my_id			= Get_ID ();
	uint32 smallest_id	= my_id;

	//
	//	Loop over all the collected objects
	//
	NonRefPhysListIterator it (&obj_list);
	for (it.First(); !it.Is_Done(); it.Next()) {
		PhysClass *phys_obj = it.Peek_Obj ();
		PhysicalGameObj *game_obj = NULL;

		if ( phys_obj->As_HumanPhysClass () && phys_obj->Get_Observer() != NULL ) {
			game_obj = ((CombatPhysObserverClass *)phys_obj->Get_Observer())->As_PhysicalGameObj();
		}

		//
		//	Is this a living soldier?
		//
		if (	game_obj != NULL && game_obj != this &&
				game_obj->As_SoldierGameObj() != NULL &&
				game_obj->As_SoldierGameObj()->Is_Destroyed () == false)
		{
			Vector3 block_pos;
			game_obj->Get_Position (&block_pos);

			//
			//	Lets make sure the boxes really intersect
			//
			AABoxClass block_box (block_pos + Vector3 (0, 0, 1.0F), Vector3 (0.3F, 0.3F, 1.0F));
			if (CollisionMath::Overlap_Test (box, block_box) != CollisionMath::OUTSIDE) {

				if (game_obj->As_SmartGameObj ()->Is_Human_Controlled () == false) {
					
					//
					//	Is this soldier moving?
					//
					ActionClass *curr_action = game_obj->As_SmartGameObj ()->Get_Action ();
					Vector3 dest_pos	= curr_action->Get_Parameters ().MoveLocation;
					float distance		= (block_pos - dest_pos).Length ();
					if (distance > curr_action->Get_Parameters ().MoveArrivedDistance) {					

						//
						//	Is this the smallest ID so far?
						//
						uint32 curr_id = game_obj->Get_ID ();
						if (curr_id < smallest_id) {
							smallest_id = curr_id;
						}

						//
						//	Push the other guy out of the way (a little)
						//
						/*if (Is_Human_Controlled () || curr_id > my_id) {
							Vector3 delta_vector = block_pos - curr_pos;
							delta_vector.Normalize ();
							SoldierGameObj *soldier = game_obj->As_SoldierGameObj ();
							if (soldier->Peek_Human_Phys () != NULL) {
								soldier->Peek_Human_Phys ()->Collide (delta_vector * TimeManager::Get_Frame_Seconds ());
							}
						}*/
					}
				}
			}
		}
	}

	return (smallest_id != my_id);
}



bool		SoldierGameObj::Is_Targetable( void ) const					
{ 
	return (!(((SoldierGameObj*)this)->Is_In_Vehicle())) && 
		SmartGameObj::Is_Targetable(); 
}


float		SoldierGameObj::Get_Stealth_Fade_Distance(void) const
{
	if (IS_MISSION) {
		return GlobalSettingsDef::Get_Global_Settings()->Get_Stealth_Distance_Human();
	} else {
		return GlobalSettingsDef::Get_Global_Settings()->Get_MP_Stealth_Distance_Human();
	}


}


void	SoldierGameObj::Lock_Facing( PhysicalGameObj * game_obj, bool turn_body )
{
	FacingObject			= game_obj;
	FacingAllowBodyTurn	= turn_body;

	//
	//	Stop facing if there's nothing to look at
	//
	if ( game_obj == NULL ) {
		Cancel_Look_At();
	}

	return ;
}


void	SoldierGameObj::Update_Locked_Facing( void )
{
	if ( FacingObject != NULL ) {

		//
		//	Get the position of the object we're "looking" at.
		//
		Vector3 pos;
		FacingObject.Get_Ptr ()->Get_Position( &pos );

		//
		//	If the object is a soldier, then look at his head
		//
		if ( FacingObject.Get_Ptr ()->As_PhysicalGameObj ()->As_SoldierGameObj () != NULL ) {
			const float SOLDIER_HEIGHT = 1.7F;
			pos.Z += SOLDIER_HEIGHT;
		}

		//
		//	Look at the object
		//
		Look_At( pos, 100.0F );
		
		//
		//	If we can turn to face the object, do so...
		//
		if ( FacingAllowBodyTurn ) {
			Internal_Set_Targeting( pos, false );
		}
	}

	return ;
}






























	/*
	if (CombatManager::I_Am_Server()) {
		CombatManager::Soldier_Dies(this);
	}
	*/

