//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Client's CWeaponBuilder class
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "in_buttons.h"
#include "clientmode_tf.h"
#include "engine/IEngineSound.h"
#include "c_tf_weapon_builder.h"
#include "c_weapon__stubs.h"
#include "iinput.h"
#include <vgui/IVGui.h>
#include "c_tf_player.h"
#include "c_vguiscreen.h"
#include "ienginevgui.h"

STUB_WEAPON_CLASS_IMPLEMENT( tf_weapon_builder, C_TFWeaponBuilder );
PRECACHE_WEAPON_REGISTER( tf_weapon_builder );

// SUPER HACK TO FIX DEMOS.  For a couple days, we accidently renamed 
// CTFWeaponBuilder to C_TFWeaponBuilder on the server.  This was fine for
// playing the game but broke all previously recorded demos.  Fixing this and
// re-renaming the class back to the original name fixed all demos recorded
// with the brokenly-renamed class.  To handle these demos that think the class
// is called C_TFWeaponBuilder on the server, we're creating a new class that derives from
// the real C_TFWeaponBuilder and does nothing special except that it calls
// IMPLEMENT_CLIENTCLASS and maps itself to serverclass "C_TFWeaponBuilder"
// (which, if you've followed along, doesn't exist anymore).
//
// As a history lesson, this broke from the change in tf_player_shared.h in cl 1722245
class C_TFWeaponBuilderReplayHack : public C_TFWeaponBuilder
{
	DECLARE_CLASS( C_TFWeaponBuilderReplayHack, C_TFWeaponBuilder );
public:
	DECLARE_CLIENTCLASS();
	DECLARE_PREDICTABLE();
};
IMPLEMENT_CLIENTCLASS( C_TFWeaponBuilderReplayHack, DT_TFWeaponBuilder, C_TFWeaponBuilder )
BEGIN_PREDICTION_DATA( C_TFWeaponBuilderReplayHack )
END_PREDICTION_DATA()


IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBuilder, DT_TFWeaponBuilder )

// Recalc object sprite when we receive a new object type to build
void RecvProxy_ObjectType( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	// Pass to normal Int recvproxy
	RecvProxy_Int32ToInt32( pData, pStruct, pOut );

	// Reset the object sprite
	C_TFWeaponBuilder *pBuilder = ( C_TFWeaponBuilder * )pStruct;
	pBuilder->SetupObjectSelectionSprite();
}

BEGIN_NETWORK_TABLE_NOBASE( C_TFWeaponBuilder, DT_BuilderLocalData )
	RecvPropInt( RECVINFO(m_iObjectType), 0, RecvProxy_ObjectType ),
	RecvPropEHandle( RECVINFO(m_hObjectBeingBuilt) ),
	RecvPropArray3( RECVINFO_ARRAY( m_aBuildableObjectTypes ), RecvPropBool( RECVINFO( m_aBuildableObjectTypes[0] ) ) ),
END_NETWORK_TABLE()

BEGIN_NETWORK_TABLE( C_TFWeaponBuilder, DT_TFWeaponBuilder )
	RecvPropInt( RECVINFO(m_iBuildState) ),
	RecvPropDataTable( "BuilderLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_BuilderLocalData ) ),
	RecvPropInt( RECVINFO(m_iObjectMode) ),
	RecvPropFloat( RECVINFO( m_flWheatleyTalkingUntil) ),
END_RECV_TABLE()




//-----------------------------------------------------------------------------
IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponSapper, DT_TFWeaponSapper )
BEGIN_NETWORK_TABLE( C_TFWeaponSapper, DT_TFWeaponSapper )
	RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ),
END_NETWORK_TABLE()


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_TFWeaponBuilder::C_TFWeaponBuilder()
{
	m_iBuildState = 0;
	m_iObjectType = BUILDER_INVALID_OBJECT;
	m_pSelectionTextureActive = NULL;
	m_pSelectionTextureInactive = NULL;
	m_iValidBuildPoseParam = -1;
	m_flWheatleyTalkingUntil = 0;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_TFWeaponBuilder::~C_TFWeaponBuilder()
{
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : char const
//-----------------------------------------------------------------------------
const char *C_TFWeaponBuilder::GetCurrentSelectionObjectName( void )
{
	if ( m_iObjectType == -1 || (m_iBuildState == BS_SELECTING) )
		return "";

	return GetObjectInfo( m_iObjectType )->m_pBuilderWeaponName;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::Deploy( void )
{
	bool bDeploy = BaseClass::Deploy();

	if ( bDeploy )
	{
		m_flNextPrimaryAttack = gpGlobals->curtime + 0.35f;
		m_flNextSecondaryAttack = gpGlobals->curtime;		// asap

		CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
		if (!pPlayer)
			return false;

		pPlayer->SetNextAttack( gpGlobals->curtime );

		m_iWorldModelIndex = modelinfo->GetModelIndex( GetWorldModel() );
	}

	return bDeploy;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_TFWeaponBuilder::SecondaryAttack( void )
{
	if ( m_bInAttack2 )
		return;

	// require a re-press
	m_bInAttack2 = true;

	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return;

	pOwner->DoClassSpecialSkill();

	m_flNextSecondaryAttack = gpGlobals->curtime + 0.2f;
}

//-----------------------------------------------------------------------------
// Purpose: cache the build pos pose param
//-----------------------------------------------------------------------------
CStudioHdr *C_TFWeaponBuilder::OnNewModel( void )
{
	CStudioHdr *hdr = BaseClass::OnNewModel();

	m_iValidBuildPoseParam = LookupPoseParameter( "valid_build_pos" );

	return hdr;
}

//-----------------------------------------------------------------------------
// Purpose:
// ----------------------------------------------------------------------------
void C_TFWeaponBuilder::PostDataUpdate( DataUpdateType_t type )
{
	if ( type == DATA_UPDATE_CREATED )
	{
		// m_iViewModelIndex is set by the base Precache(), which didn't know what
		// type of object we built, so it didn't get the right viewmodel index.
		// Now that our data is filled in, go and get the right index.
		const char *pszViewModel = GetViewModel(0);
		if ( pszViewModel && pszViewModel[0] )
		{
			m_iViewModelIndex = CBaseEntity::PrecacheModel( pszViewModel );
		}
	}

	BaseClass::PostDataUpdate( type );
}

//-----------------------------------------------------------------------------
// Purpose: only called for local player
//-----------------------------------------------------------------------------
void C_TFWeaponBuilder::Redraw()
{
	if ( m_iValidBuildPoseParam >= 0 )
	{
		CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
		if ( !pOwner )
			return;

		// Assuming here that our model is the same as our viewmodel's model!
		CBaseViewModel *pViewModel = pOwner->GetViewModel(0);

		if ( pViewModel )
		{
			float flPoseParamValue = pViewModel->GetPoseParameter( m_iValidBuildPoseParam );

			C_BaseObject *pObj = m_hObjectBeingBuilt.Get();

			if ( pObj && pObj->WasLastPlacementPosValid() )
			{
				// pose param approach 1.0
				flPoseParamValue = Approach( 1.0, flPoseParamValue, 3.0 * gpGlobals->frametime );
			}
			else
			{
				// pose param approach 0.0
				flPoseParamValue = Approach( 0.0, flPoseParamValue, 1.5 * gpGlobals->frametime );
			}

			pViewModel->SetPoseParameter( m_iValidBuildPoseParam, flPoseParamValue );
		}
	}

	BaseClass::Redraw();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::IsPlacingObject( void )
{
	if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID )
		return true;

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int C_TFWeaponBuilder::GetSlot( void ) const
{
	return GetObjectInfo( m_iObjectType )->m_SelectionSlot;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int C_TFWeaponBuilder::GetPosition( void ) const
{
	return GetObjectInfo( m_iObjectType )->m_SelectionPosition;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_TFWeaponBuilder::SetupObjectSelectionSprite( void )
{
#ifdef CLIENT_DLL
	// Use the sprite details from the text file, with a custom sprite
	char *iconTexture = GetObjectInfo( m_iObjectType )->m_pIconActive;
	if ( iconTexture && iconTexture[ 0 ] )
	{
		m_pSelectionTextureActive = gHUD.GetIcon( iconTexture );
	}
	else
	{
		m_pSelectionTextureActive = NULL;
	}

	iconTexture = GetObjectInfo( m_iObjectType )->m_pIconInactive;
	if ( iconTexture && iconTexture[ 0 ] )
	{
		m_pSelectionTextureInactive = gHUD.GetIcon( iconTexture );
	}
	else
	{
		m_pSelectionTextureInactive = NULL;
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudTexture const *C_TFWeaponBuilder::GetSpriteActive( void ) const
{
	return m_pSelectionTextureActive;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CHudTexture const *C_TFWeaponBuilder::GetSpriteInactive( void ) const
{
	return m_pSelectionTextureInactive;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : char const
//-----------------------------------------------------------------------------
const char *C_TFWeaponBuilder::GetPrintName( void ) const
{
	return GetObjectInfo( m_iObjectType )->m_AltModes[m_iObjectMode].pszStatusName;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	C_TFWeaponBuilder::GetSubType( void )
{
	return m_iObjectType;
}

//-----------------------------------------------------------------------------
// Purpose: Return true if this weapon can be selected via the weapon selection
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::CanBeSelected( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return false;

	if ( pOwner->CanBuild( m_iObjectType, m_iObjectMode ) != CB_CAN_BUILD )
		return false;

	return HasAmmo();
}

//-----------------------------------------------------------------------------
// Purpose: Return true if this weapon should be visible in the weapon selection
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::VisibleInWeaponSelection( void )
{
	if ( BaseClass::VisibleInWeaponSelection() == false )
		return false;
	if ( m_iObjectType != BUILDER_INVALID_OBJECT )
		return GetObjectInfo( m_iObjectType )->m_bVisibleInWeaponSelection;
	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Return true if this weapon has some ammo
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::HasAmmo( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return false;

	int iCost = pOwner->m_Shared.CalculateObjectCost( pOwner, m_iObjectType );
	return ( pOwner->GetBuildResources() >= iCost );
}

// -----------------------------------------------------------------------------
// Purpose:
// -----------------------------------------------------------------------------
bool C_TFWeaponBuilder::CanBuildObjectType( int iObjectType )
{
	if ( iObjectType < 0 || iObjectType >= OBJ_LAST )
		return false;

	return m_aBuildableObjectTypes[iObjectType];
}

// -----------------------------------------------------------------------------
// Purpose:
// -----------------------------------------------------------------------------
void C_TFWeaponBuilder::UpdateAttachmentModels( void )
{
	if ( m_iObjectType != BUILDER_INVALID_OBJECT && GetObjectInfo( m_iObjectType )->m_bUseItemInfo )
	{
		BaseClass::UpdateAttachmentModels();
	}
}

// -----------------------------------------------------------------------------
// Purpose:
// -----------------------------------------------------------------------------
const char *C_TFWeaponBuilder::GetViewModel( int iViewModel ) const
{
	if ( GetPlayerOwner() == NULL )
	{
		return BaseClass::GetViewModel();
	}

	if ( m_iObjectType != BUILDER_INVALID_OBJECT )
	{
		if ( GetObjectInfo( m_iObjectType )->m_bUseItemInfo )
			return BaseClass::GetViewModel();

		return GetObjectInfo( m_iObjectType )->m_pViewModel;
	}

	return BaseClass::GetViewModel();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *C_TFWeaponBuilder::GetWorldModel( void ) const
{
	if ( GetPlayerOwner() == NULL )
	{
		return BaseClass::GetWorldModel();
	}

	if ( m_iObjectType != BUILDER_INVALID_OBJECT )
	{
		return GetObjectInfo( m_iObjectType )->m_pPlayerModel;
	}

	return BaseClass::GetWorldModel();
}

Activity C_TFWeaponBuilder::GetDrawActivity( void )
{
	// sapper used to call different draw animations , one when invis and one when not.
	// now you can go invis *while* deploying, so let's always use the one-handed deploy.
	if ( GetType() == OBJ_ATTACHMENT_SAPPER )
	{
		return ACT_VM_DRAW_DEPLOYED;
	}

	return BaseClass::GetDrawActivity();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool C_TFWeaponBuilder::EffectMeterShouldFlash( void )
{
	if ( !GetOwner() )
		return false;

	int iRoboSapper = 0;
	CALL_ATTRIB_HOOK_INT_ON_OTHER( GetOwner(), iRoboSapper, robo_sapper );

	return ( iRoboSapper && GetEffectBarProgress() >= 1.f );
}

const char *C_TFWeaponSapper::GetViewModel( int iViewModel ) const
{
	// Skip over Builder's version
	return C_TFWeaponBase::GetViewModel();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *C_TFWeaponSapper::GetWorldModel( void ) const
{
	// Skip over Builder's version
	return C_TFWeaponBase::GetWorldModel();
}