//========= 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 #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(); }