/*
**	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/powerup.cpp                           $*
 *                                                                                             *
 *                      $Author:: Byon_g                                                      $*
 *                                                                                             *
 *                     $Modtime:: 12/17/01 10:57a                                             $*
 *                                                                                             *
 *                    $Revision:: 93                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "powerup.h"
#include "debug.h"
#include "combat.h"
#include "phys.h"
#include "persistfactory.h"
#include "combatchunkid.h"
#include "simpledefinitionfactory.h"
#include "wwhack.h"
#include "weaponbag.h"
#include "damage.h"
#include "gameobjmanager.h"
#include "colmath.h"
#include "movephys.h"
#include "wwaudio.h"
#include "audiblesound.h"
#include "hanim.h"
#include "timemgr.h"
#include "assetmgr.h"
#include "animcontrol.h"
#include "soldier.h"
#include "wwprofile.h"
#include "objlibrary.h"
#include "gameobjobserver.h"
#include "diaglog.h"
#include "weaponmanager.h"
#include "weapons.h"
#include "hud.h"
#include "playerdata.h"
#include "physinttest.h"
#include "encyclopediamgr.h"
#include "vehicle.h"
#include "combatmaterialeffectmanager.h"
#include "transitioneffect.h"
#include "apppackettypes.h"
#include "hudinfo.h"
#include "string_ids.h"
#include "translatedb.h"

/*
** PowerUpGameObjDef
*/
DECLARE_FORCE_LINK( PowerUp )

SimplePersistFactoryClass<PowerUpGameObjDef, CHUNKID_GAME_OBJECT_DEF_POWERUP>	_PowerUpGameObjDefPersistFactory;

DECLARE_DEFINITION_FACTORY(PowerUpGameObjDef, CLASSID_GAME_OBJECT_DEF_POWERUP, "PowerUp") _PowerUpGameObjDefDefFactory;

PowerUpGameObjDef::PowerUpGameObjDef( void ) :
	GrantShieldType( 0 ),
	GrantShieldStrength( 0 ),
	GrantShieldStrengthMax( 0 ),
	GrantHealth( 0 ),
	GrantHealthMax( 0 ),
	GrantWeaponID( 0 ),
	GrantWeapon( true ),
	GrantWeaponClips( false ),
	GrantWeaponRounds( 0 ),
	Persistent( false ),
	//IsCaptureTheFlag( false ),
	GrantKey( 0 ),
	GrantSoundID( 0 ),
	IdleSoundID( 0 ),
	AlwaysAllowGrant( false )
{
#ifdef PARAM_EDITING_ON
//	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_INT,	GrantShieldType );
	EnumParameterClass *param;
	param = new EnumParameterClass( &GrantShieldType );
	param->Set_Name ( "GrantShieldType" );
	for ( int param_counter = 0; param_counter < ArmorWarheadManager::Get_Num_Armor_Types(); param_counter++ ) {
		param->Add_Value(ArmorWarheadManager::Get_Armor_Name(param_counter), param_counter);
	}
	GENERIC_EDITABLE_PARAM( PowerUpGameObjDef, param )

	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_FLOAT, GrantShieldStrength );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_FLOAT, GrantShieldStrengthMax );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_FLOAT, GrantHealth );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_FLOAT, GrantHealthMax );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_WEAPONOBJDEFINITIONID,	GrantWeaponID );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_BOOL,	GrantWeapon );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_BOOL,	GrantWeaponClips );	
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_INT,	GrantWeaponRounds );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_BOOL,	Persistent );
	//EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_BOOL,	false );//IsCaptureTheFlag );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_INT,	GrantKey );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_BOOL,	AlwaysAllowGrant );

	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_STRING, GrantAnimationName );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_STRING, IdleAnimationName );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, GrantSoundID );
	EDITABLE_PARAM( PowerUpGameObjDef, ParameterClass::TYPE_SOUNDDEFINITIONID, IdleSoundID );
#endif	//PARAM_EDITING_ON
}

uint32	PowerUpGameObjDef::Get_Class_ID (void) const
{
	return CLASSID_GAME_OBJECT_DEF_POWERUP;
}

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

enum	{
	CHUNKID_DEF_PARENT							=	909991656,
	CHUNKID_DEF_VARIABLES,

	XXXMICROCHUNKID_DEF_PARAMETERS				=	1,
	MICROCHUNKID_DEF_PERSISTENT,
	MICROCHUNKID_DEF_GRANT_SHIELD_TYPE,
	MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH,
	XXXMICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH_MAX,
	MICROCHUNKID_DEF_GRANT_HEALTH,
	XXXMICROCHUNKID_DEF_GRANT_HEALTH_MAX,
	MICROCHUNKID_DEF_GRANT_WEAPON_ID,
	MICROCHUNKID_DEF_GRANT_WEAPON,
	MICROCHUNKID_DEF_GRANT_WEAPON_ROUNDS,
	XXXMICROCHUNKID_DEF_IS_CAPTURE_THE_FLAG,
	XXXMICROCHUNKID_DEF_GRANT_KEY_MASK,

	MICROCHUNKID_DEF_GRANT_ANIMATION_NAME,
	MICROCHUNKID_DEF_GRANT_SOUNDID,
	MICROCHUNKID_DEF_IDLE_ANIMATION_NAME,
	MICROCHUNKID_DEF_IDLE_SOUNDID,
	MICROCHUNKID_DEF_GRANT_KEY,
	MICROCHUNKID_DEF_ALWAYS_ALLOW_GRANT,
	MICROCHUNKID_DEF_GRANT_WEAPON_CLIPS,

	MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH_MAX,
	MICROCHUNKID_DEF_GRANT_HEALTH_MAX,

};

bool	PowerUpGameObjDef::Save( ChunkSaveClass & csave )
{
	csave.Begin_Chunk( CHUNKID_DEF_PARENT );
		SimpleGameObjDef::Save( csave );
	csave.End_Chunk();

	csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_SHIELD_TYPE,			GrantShieldType );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH,		GrantShieldStrength );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH_MAX,GrantShieldStrengthMax );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_HEALTH,					GrantHealth );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_HEALTH_MAX,			GrantHealthMax );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_WEAPON_ID,				GrantWeaponID );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_WEAPON,					GrantWeapon );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_WEAPON_CLIPS,			GrantWeaponClips );		
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_WEAPON_ROUNDS,		GrantWeaponRounds );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_PERSISTENT,					Persistent );
		//WRITE_MICRO_CHUNK( csave, XXXMICROCHUNKID_DEF_IS_CAPTURE_THE_FLAG,		IsCaptureTheFlag );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_KEY,						GrantKey );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_GRANT_SOUNDID,				GrantSoundID );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_IDLE_SOUNDID,					IdleSoundID );
		WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_GRANT_ANIMATION_NAME,		GrantAnimationName );
		WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_IDLE_ANIMATION_NAME,		IdleAnimationName );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ALWAYS_ALLOW_GRANT,			AlwaysAllowGrant );
	csave.End_Chunk();

	return true;
}

bool	PowerUpGameObjDef::Load( ChunkLoadClass &cload )
{
	while (cload.Open_Chunk()) {
		switch(cload.Cur_Chunk_ID()) {

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

			case CHUNKID_DEF_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_SHIELD_TYPE,			GrantShieldType );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH,		GrantShieldStrength );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_SHIELD_STRENGTH_MAX,	GrantShieldStrengthMax );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_HEALTH,					GrantHealth );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_HEALTH_MAX,				GrantHealthMax );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_WEAPON_ID,				GrantWeaponID );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_WEAPON,					GrantWeapon );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_WEAPON_CLIPS,			GrantWeaponClips );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_WEAPON_ROUNDS,			GrantWeaponRounds );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_PERSISTENT,						Persistent );
						//READ_MICRO_CHUNK( cload, XXXMICROCHUNKID_DEF_IS_CAPTURE_THE_FLAG,			IsCaptureTheFlag );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_KEY,						GrantKey );

						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_GRANT_SOUNDID,					GrantSoundID );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_IDLE_SOUNDID,					IdleSoundID );
						READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_GRANT_ANIMATION_NAME,	GrantAnimationName );
						READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_IDLE_ANIMATION_NAME,		IdleAnimationName );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ALWAYS_ALLOW_GRANT,			AlwaysAllowGrant );

						default:
							Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
							break;

					}
					cload.Close_Micro_Chunk();
				}
				break;

			default:
				Debug_Say(("Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;

		}
		cload.Close_Chunk();
	}

	return true;
}

const PersistFactoryClass & PowerUpGameObjDef::Get_Factory (void) const
{
	return _PowerUpGameObjDefPersistFactory;
}

bool	PowerUpGameObjDef::Grant( SmartGameObj * obj, PowerUpGameObj * p_powerup, bool hud_display ) const
{
	int no_grant_message = 0;

	bool granted = false;

	WWASSERT(CombatManager::I_Am_Server());

	DefenseObjectClass * defense = obj->Get_Defense_Object();
	// Grant the shield
	if ( GrantShieldType != 0 ) {
		if ( GrantShieldType > (int)defense->Get_Shield_Type() ) {
			defense->Set_Shield_Type( GrantShieldType );
			granted = true;
		} else {
			no_grant_message = IDS_M00DSGN_DSGN1015I1DSGN_TXT; //"You are already at full shield."
		}
	}

	if ( GrantShieldStrengthMax != 0 ) {
		float add = GrantShieldStrengthMax * (float)obj->Get_Definition().Get_DefenseObjectDef().ShieldStrengthMax;

		switch ( CombatManager::Get_Difficulty_Level() ) {
			case 0:	add *= 2.0f;	break;
			case 2:	add *= 0.75f;	break;
		};

		// Round up to next int
		add = (int)(add + 0.95f);

		defense->Set_Shield_Strength_Max( defense->Get_Shield_Strength_Max() + add );
		granted = true;

		if ( hud_display && obj == COMBAT_STAR ) {
			HUDClass::Add_Shield_Upgrade_Grant( add );
		}

	}

	if ( GrantShieldStrength != 0 ) {
		if ( (defense->Get_Shield_Strength() < defense->Get_Shield_Strength_Max()) ) {
			defense->Add_Shield_Strength( GrantShieldStrength );
			granted = true;

			if ( obj == COMBAT_STAR ) {
				Vector3 pos;
				obj->Get_Position( &pos );
				DIAG_LOG(( "AMPU", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health() ));
			}
		} else {
			no_grant_message = IDS_M00DSGN_DSGN1015I1DSGN_TXT; //"You are already at full shield."
		}
	}

	if ( granted && hud_display ) {
		if ( obj == COMBAT_STAR ) {
			if ( GrantShieldStrengthMax == 0 ) {
				HUDClass::Add_Shield_Grant( GrantShieldStrength );
			}
		}
	}

	// Grant the Health
	if ( GrantHealthMax != 0 ) {
		float add = GrantHealthMax * (float)obj->Get_Definition().Get_DefenseObjectDef().HealthMax;

		switch ( CombatManager::Get_Difficulty_Level() ) {
			case 0:	add *= 2.0f;	break;
			case 2:	add *= 0.75f;	break;
		};

		// Round up to next int
		add = (int)(add + 0.95f);

		defense->Set_Health_Max( defense->Get_Health_Max() + add );
		granted = true;

		if ( hud_display && obj == COMBAT_STAR ) {
			HUDClass::Add_Health_Upgrade_Grant( add );
		}
	}

	if ( GrantHealth != 0 ) {
		if ( defense->Get_Health() < defense->Get_Health_Max() ) {
			defense->Add_Health( GrantHealth );
			granted = true;

			if ( obj == COMBAT_STAR && hud_display  ) {
				if ( GrantHealthMax == 0 ) {
					HUDClass::Add_Health_Grant( GrantHealth );
				}
			}

			if ( obj == COMBAT_STAR ) {
				Vector3 pos;
				obj->Get_Position( &pos );
				DIAG_LOG(( "HEPU", "%1.2f; %1.2f; %1.2f; %1.2f; %1.2f", pos.X, pos.Y, pos.Z, defense->Get_Shield_Strength(), defense->Get_Health() ));
			}
		} else {
			no_grant_message = IDS_M00DSGN_DSGN1014I1DSGN_TXT; //"You are already at full health."
		}
	}

	// Grant the Weapon
	if ( GrantWeaponID != 0 ) {

		if ( ( GrantWeapon && !obj->Get_Weapon_Bag()->Is_Weapon_Owned( GrantWeaponID ) ) ||
			  ( !obj->Get_Weapon_Bag()->Is_Ammo_Full( GrantWeaponID ) ) ) {

			if ( obj == COMBAT_STAR && hud_display ) {
				if ( GrantWeapon && !obj->Get_Weapon_Bag()->Is_Weapon_Owned( GrantWeaponID ) )  {
					HUDClass::Add_Powerup_Weapon( GrantWeaponID, GrantWeaponRounds );
				} else {
					if ( !obj->Get_Weapon_Bag()->Is_Ammo_Full( GrantWeaponID ) ) {
						HUDClass::Add_Powerup_Ammo( GrantWeaponID, GrantWeaponRounds );
					}
				}
			}

			obj->Get_Weapon_Bag()->Add_Weapon( GrantWeaponID, GrantWeaponRounds, GrantWeapon );
			granted = true;

			if ( obj == COMBAT_STAR ) {
				Vector3 pos;
				obj->Get_Position( &pos );
				const char * grant_name = "";
				const WeaponDefinitionClass * def = WeaponManager::Find_Weapon_Definition( GrantWeaponID );
				if ( def ) {
					grant_name = def->Get_Name();
				}
				const char * weapon_name = "";
				int ammo = 0;
				if ( obj->Get_Weapon() ) {
					weapon_name = obj->Get_Weapon()->Get_Definition()->Get_Name();
					ammo = obj->Get_Weapon()->Get_Total_Rounds();
				}
				DIAG_LOG(( "WEPU", "%1.2f; %1.2f; %1.2f; %s; %d; %s; %d", pos.X, pos.Y, pos.Z, grant_name, GrantWeaponRounds, weapon_name, ammo ));
			}
		} else {
			no_grant_message = IDS_M00DSGN_DSGN1016I1DSGN_TXT; //"Your weapon is full."
		}

	} else if ( GrantWeaponClips ) {
		
		//
		//	Loop over all the weapons in the owner's bag
		//
		WeaponBagClass *weapon_bag = obj->Get_Weapon_Bag();
		for ( int weapon_index = 0; weapon_index < weapon_bag->Get_Count(); weapon_index ++ ) {
			WeaponClass	*weapon = weapon_bag->Peek_Weapon( weapon_index );
			if( weapon != NULL && weapon->Get_Definition ()->CanReceiveGenericCnCAmmo ) {

				//
				//	Grant "x" number of clips to the weapon
				//
				int clip_rounds = weapon->Get_Definition()->ClipSize;
				weapon->Add_Rounds( clip_rounds * GrantWeaponRounds );
			}
		}
	}


	// Grant the key
	if ( GrantKey != 0 ) {
		SoldierGameObj * soldier = obj->As_SoldierGameObj();
		if ( soldier && soldier->Is_Human_Controlled() ) {
			if ( !soldier->Has_Key( GrantKey ) ) {
				soldier->Give_Key( GrantKey );
				granted = true;
			}
		}
		if ( obj == COMBAT_STAR ) {
			Vector3 pos;
			obj->Get_Position( &pos );
			DIAG_LOG(( "KEPU", "%1.2f; %1.2f; %1.2f; %d", pos.X, pos.Y, pos.Z, GrantKey ));
		}

		if ( granted && hud_display && obj == COMBAT_STAR ) {
			HUDClass::Add_Key_Grant( GrantKey );
		}
	}

	/*
	// Handle Capture the Flag
	if ( IsCaptureTheFlag && p_powerup != NULL && obj->As_SoldierGameObj() != NULL ) {
		CombatManager::Soldier_Contacts_Flag( obj->As_SoldierGameObj(), p_powerup );
		granted = true;
	}
	*/

	if ( AlwaysAllowGrant ) {
		granted = true;
	}

	if ( granted && p_powerup != NULL ) {
		p_powerup->Set_State( PowerUpGameObj::STATE_GRANTING );

		//
		//	Reveal this object to the player
		//
		if ( COMBAT_STAR == obj ) {
			EncyclopediaMgrClass::Reveal_Object( p_powerup );
		}
	}

	// Stats
	if ( granted && obj->Get_Player_Data() ) {
		obj->Get_Player_Data()->Stats_Add_Powerup();
	}

	if ( !granted && ( COMBAT_STAR == obj ) && no_grant_message != 0 ) {
		HUDInfo::Set_HUD_Help_Text( TRANSLATE( no_grant_message ), Vector3( 0,1,0 ) );

	}

	return granted;
}


/*
** PowerUpGameObj
*/
SimplePersistFactoryClass<PowerUpGameObj, CHUNKID_GAME_OBJECT_POWERUP>	_PowerUpGameObjPersistFactory;

PowerUpGameObj::PowerUpGameObj (void)	:
	IdleSoundObj( NULL ),
	State( STATE_BECOMING_IDLE ),
	WeaponBag( NULL )
{
	Set_App_Packet_Type(APPPACKETTYPE_POWERUP);
}

PowerUpGameObj::~PowerUpGameObj (void)
{
	//
	//	Cleanup the idle sound
	//
	if ( IdleSoundObj != NULL ) {
		IdleSoundObj->Remove_From_Scene();
		IdleSoundObj->Release_Ref();
		IdleSoundObj = NULL;
	}

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

	return ;
}

const PersistFactoryClass & PowerUpGameObj::Get_Factory (void) const
{
	return _PowerUpGameObjPersistFactory;
}

/*
**
*/
void PowerUpGameObj::Init( void )
{
	Init( Get_Definition() );
}

/*
**
*/
void	PowerUpGameObj::Init( const PowerUpGameObjDef & definition )
{
	SimpleGameObj::Init( definition );

	// Only collide with terrain!
	Peek_Physical_Object()->Set_Collision_Group( TERRAIN_ONLY_COLLISION_GROUP );
}

const PowerUpGameObjDef & PowerUpGameObj::Get_Definition( void ) const
{
	return (const PowerUpGameObjDef &)BaseGameObj::Get_Definition();
}


/*
** PowerUpGameObj Save and Load
*/
enum	{
	CHUNKID_PARENT							=	927991635,
	CHUNKID_VARIABLES,
	CHUNKID_WEAPONBAG,

	MICROCHUNKID_STATE					=	1,
	MICROCHUNKID_STATE_END_TIMER,
};

bool	PowerUpGameObj::Save( ChunkSaveClass & csave )
{
	csave.Begin_Chunk( CHUNKID_PARENT );
		SimpleGameObj::Save( csave );
	csave.End_Chunk();

	csave.Begin_Chunk( CHUNKID_VARIABLES );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE,				State );
		WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STATE_END_TIMER,	StateEndTimer );
	csave.End_Chunk();

	if ( WeaponBag != NULL ) {
		csave.Begin_Chunk( CHUNKID_WEAPONBAG );
		WeaponBag->Save( csave );
		csave.End_Chunk();
	}

// We don't save IdleSoundObj

	return true;
}

bool	PowerUpGameObj::Load( ChunkLoadClass &cload )
{
	while (cload.Open_Chunk()) {
		switch(cload.Cur_Chunk_ID()) {

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

			case CHUNKID_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE,					State );
						READ_MICRO_CHUNK( cload, MICROCHUNKID_STATE_END_TIMER,	StateEndTimer );

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

					}
					cload.Close_Micro_Chunk();
				}

				break;

			case CHUNKID_WEAPONBAG:
				WWASSERT( WeaponBag == NULL );
				WeaponBag = new WeaponBagClass( NULL );
			 	WeaponBag->Load( cload );
				break;

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

		}
		cload.Close_Chunk();
	}

	SaveLoadSystemClass::Register_Post_Load_Callback(this);		// MOVED
	return true;
}

void	PowerUpGameObj::On_Post_Load( void )
{
	SimpleGameObj::On_Post_Load();
	Peek_Physical_Object()->Set_Collision_Group( UNCOLLIDEABLE_GROUP );	// MOVED
	if ( Peek_Physical_Object()->As_MoveablePhysClass() != NULL ) {
		Peek_Physical_Object()->As_MoveablePhysClass()->Set_Gravity_Multiplier( 0 );
	}

	// This allows the idle sound and animation to start after loading
	if ( State == STATE_IDLING ) {
		State = STATE_BECOMING_IDLE;
	}

}

void	PowerUpGameObj::Set_State( int state )
{
	if (state != State) {
		State				= state;
		StateEndTimer	= 0;

		if ( State == STATE_GRANTING ) {

			//
			//	Stop the idling sound (if necessary)
			//
			if ( IdleSoundObj != NULL ) {
				IdleSoundObj->Remove_From_Scene();
				IdleSoundObj->Stop();
			}

			//
			// Play the grant sound (if exists)
			//
			if ( Get_Definition ().GrantSoundID != 0 ) {
				WWAudioClass::Get_Instance()->Create_Instant_Sound( Get_Definition ().GrantSoundID, Get_Transform () );
			}

			//
			// Play the grant animation (if exists)
			//
			if ( Get_Definition ().GrantAnimationName.Get_Length () > 0 ) {
				Set_Animation( Get_Definition ().GrantAnimationName, false );
				HAnimClass *animation =  WW3DAssetManager::Get_Instance ()->Get_HAnim( Get_Anim_Control ()->Get_Animation_Name () );
				if ( animation != NULL ) {
					StateEndTimer = animation->Get_Total_Time();
					REF_PTR_RELEASE( animation );
				}
			}
		}
	}

	return ;
}

void	PowerUpGameObj::Update_State( void )
{
	switch ( State )
	{
		case STATE_IDLING:
			break;

		case STATE_BECOMING_IDLE:

			//
			//	Start playing the idle sound
			//
			if ( Get_Definition ().IdleSoundID != 0 ) {
				if ( IdleSoundObj == NULL ) {
					IdleSoundObj = WWAudioClass::Get_Instance ()->Create_Continuous_Sound( Get_Definition ().IdleSoundID );
				}
				if ( IdleSoundObj != NULL ) {
					IdleSoundObj->Set_Transform( Get_Transform () );
					IdleSoundObj->Add_To_Scene( true );
				}
			}

			//
			//	Start playing the idle animation
			//
			if ( Get_Definition ().IdleAnimationName.Get_Length () > 0 ) {
				Set_Animation( Get_Definition ().IdleAnimationName, true );
			}

			State = STATE_IDLING;
			break;

		case STATE_GRANTING:

			//
			//	If the granting animation has finished, then change to another state (or remove
			// the powerup from the world).
			//
			StateEndTimer -= TimeManager::Get_Frame_Seconds();
			if ( StateEndTimer <= 0 ) {
				//if ( Get_Definition().IsCaptureTheFlag || Get_Definition().Persistent ) {
				if ( Get_Definition().Persistent ) {
					Set_State ( STATE_BECOMING_IDLE );
				} else {
					Set_Delete_Pending();
				}
			}
			break;

		case STATE_EXPIRING:

			//
			//	If the granting animation has finished, then change to another state (or remove
			// the powerup from the world).
			//
			StateEndTimer -= TimeManager::Get_Frame_Seconds();
			if ( StateEndTimer <= 0 ) {
				Set_Delete_Pending();
			}
			break;
	}

	return ;
}

void	PowerUpGameObj::Grant( SmartGameObj * obj )
{
	WWASSERT ( State != STATE_GRANTING );

	WWASSERT( obj );

	// Grant Def
	Get_Definition().Grant( obj, this );

	// If we have a weapon bag, move it
	if ( WeaponBag != NULL ) {
		WWASSERT( obj->Get_Weapon_Bag() );
		if ( obj->Get_Weapon_Bag()->Move_Contents( WeaponBag ) ) {
			Set_State( PowerUpGameObj::STATE_GRANTING );
		}
	}

	if ( State == STATE_GRANTING ) {
		const GameObjObserverList & observer_list = Get_Observers();
		for( int index = 0; index < observer_list.Count(); index++ ) {
			observer_list[ index ]->Custom( this, CUSTOM_EVENT_POWERUP_GRANTED, 0, obj );
		}
	}
}


void	PowerUpGameObj::Think( void )
{
	SimpleGameObj::Think();

	WWPROFILE( "PowerUp Think" );

	//
	//	Make sure the powerup is playing its correct animation and sound
	//
	Update_State();

	//
	//	If this powerup isn't currently granting itself to a player, then check
	// to see if it should!
	//
	// The server grants the prize, but allow the client to destroy
	// the powerup before being instructed to do so,
	// so that this doesn't lag
	//
	if ( CombatManager::I_Am_Server() && State != STATE_GRANTING ) {

		// Check my bounding box for collisions with Soldiers
		AABoxClass box = Peek_Model()->Get_Bounding_Box();

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

			SoldierGameObj * soldier = obj->As_SoldierGameObj();

			if ( obj->As_VehicleGameObj() ) {
				soldier = obj->As_VehicleGameObj()->Get_Driver();
			}

			if ( soldier && soldier->Wants_Powerups() ) {

				PhysAABoxIntersectionTestClass test( box, DEFAULT_COLLISION_GROUP, COLLISION_TYPE_PHYSICAL );
				bool result = obj->Peek_Physical_Object()->Intersection_Test(test);
				if ( result ) {
					Grant( soldier );		// Don't grant any more
					break;
				}
			}
  		}
	}

	return ;
}

PowerUpGameObj *	PowerUpGameObj::Create_Backpack( ArmedGameObj * provider )
{
	WWASSERT( provider );

//	Debug_Say(( "Creating a Backpack\n" ));

	PowerUpGameObj * backpack = (PowerUpGameObj *)ObjectLibraryManager::Create_Object( "Backpack" );

	if ( backpack != NULL ) {
		Matrix3D tm(1);
		tm.Set_Translation( provider->Get_Bullseye_Position() );
		backpack->Set_Transform(tm);
		backpack->WeaponBag = new WeaponBagClass( NULL );
		backpack->WeaponBag->Move_Contents( provider->Get_Weapon_Bag() );
		backpack->Start_Observers();
	}

	return backpack;
}


//void	PowerUpGameObj::Get_Extended_Information( StringClass & description ) 
void	PowerUpGameObj::Get_Description( StringClass & description ) 
{
	//
	// Construct a diagnostic string
	//

	StringClass line;

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

   line.Format("Name:  %s\n", Get_Definition().Get_Name());
	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;
	
	if (Get_Defense_Object() != NULL) {
		line.Format("HLTH:  %-5.2f\n", Get_Defense_Object()->Get_Health());
		description += line;
	}

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

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

void	PowerUpGameObj::Expire( void )
{
#define		EXPIRE_TIME		2

	if ( State != STATE_EXPIRING ) {
		/*
		** If the definition calls for it, add a material effect to the object
		*/
		PhysClass * physobj = Peek_Physical_Object();
		if (physobj != NULL) {
			TransitionEffectClass * effect = CombatMaterialEffectManager::Get_Death_Effect();
			effect->Set_Transition_Time( EXPIRE_TIME );
			physobj->Add_Effect_To_Me(effect);
			REF_PTR_RELEASE(effect);
		}

		State = STATE_EXPIRING;
		StateEndTimer = EXPIRE_TIME;
	}
}
