diff options
Diffstat (limited to 'game/client/tf/tf_hud_menu_engy_build.cpp')
| -rw-r--r-- | game/client/tf/tf_hud_menu_engy_build.cpp | 931 |
1 files changed, 931 insertions, 0 deletions
diff --git a/game/client/tf/tf_hud_menu_engy_build.cpp b/game/client/tf/tf_hud_menu_engy_build.cpp new file mode 100644 index 0000000..c4d6544 --- /dev/null +++ b/game/client/tf/tf_hud_menu_engy_build.cpp @@ -0,0 +1,931 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "cbase.h" +#include "hud.h" +#include "hudelement.h" +#include "c_tf_player.h" +#include "iclientmode.h" +#include "ienginevgui.h" +#include <vgui/ILocalize.h> +#include <vgui/ISurface.h> +#include <vgui/IVGui.h> +#include <vgui/IInput.h> +#include "c_baseobject.h" +#include "tf_gamerules.h" +#include "tf_item_inventory.h" +#include "tf_hud_menu_engy_build.h" +#include "inputsystem/iinputsystem.h" + +// NVNT haptics for buildings +#include "haptics/haptic_utils.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +// Set to 1 to simulate xbox-style menu interaction +ConVar tf_build_menu_controller_mode( "tf_build_menu_controller_mode", "0", FCVAR_ARCHIVE, "Use console controller build menus. 1 = ON, 0 = OFF." ); + +const EngyConstructBuilding_t g_kEngyBuildings[ NUM_ENGY_BUILDINGS ] = +{ + // Sentry gun + EngyConstructBuilding_t( true, + OBJ_SENTRYGUN, + 0, + "sentry_active.res", + "sentry_already_built.res", + "sentry_cant_afford.res", + "sentry_unavailable.res", + "sentry_active.res", + "sentry_inactive.res", + "sentry_inactive.res" ), + + // Dispenser + EngyConstructBuilding_t( true, + OBJ_DISPENSER, + 0, + "dispenser_active.res", + "dispenser_already_built.res", + "dispenser_cant_afford.res", + "dispenser_unavailable.res", + "dispenser_active.res", + "dispenser_inactive.res", + "dispenser_inactive.res" ), + + // Teleporter entrance + EngyConstructBuilding_t( true, + OBJ_TELEPORTER, + MODE_TELEPORTER_ENTRANCE, + "tele_entrance_active.res", + "tele_entrance_already_built.res", + "tele_entrance_cant_afford.res", + "tele_entrance_unavailable.res", + "tele_entrance_active.res", + "tele_entrance_inactive.res", + "tele_entrance_inactive.res" ), + + // Teleporter exit + EngyConstructBuilding_t( true, + OBJ_TELEPORTER, + MODE_TELEPORTER_EXIT, + "tele_exit_active.res", + "tele_exit_already_built.res", + "tele_exit_cant_afford.res", + "tele_exit_unavailable.res", + "tele_exit_active.res", + "tele_exit_inactive.res", + "tele_exit_inactive.res" ) +}; + + +static int flagSlots[NUM_ENGY_BUILDINGS] = +{ + 0x01, + 0x02, + 0x04, + 0x08 +}; + + +#ifdef STAGING_ONLY +const EngyBuildingReplacement_t s_alternateEngineerBuildings[] = +{ + // Catapult + EngyBuildingReplacement_t( + OBJ_CATAPULT, + 0, + "catapult_active.res", + "catapult_already_built.res", + "catapult_cant_afford.res", + "catapult_unavailable.res", + "catapult_active.res", + "catapult_inactive.res", + "catapult_inactive.res", + flagSlots[2], + flagSlots[3] + ), + // Speed 1 + EngyBuildingReplacement_t( + OBJ_TELEPORTER, + MODE_TELEPORTER_SPEED, + "speedpad_active.res", + "speedpad_already_built.res", + "speedpad_cant_afford.res", + "speedpad_unavailable.res", + "speedpad_active.res", + "speedpad_inactive.res", + "speedpad_inactive.res", + flagSlots[2], + 0 + ), + // Speed 2 + EngyBuildingReplacement_t( + OBJ_TELEPORTER, + MODE_TELEPORTER_SPEED2, + "speedpad_active.res", + "speedpad_already_built.res", + "speedpad_cant_afford.res", + "speedpad_unavailable.res", + "speedpad_active.res", + "speedpad_inactive.res", + "speedpad_inactive.res", + flagSlots[3], + 0 + ), + + // Add more objects here +}; +#endif + +//====================================== +DECLARE_HUDELEMENT_DEPTH( CHudMenuEngyBuild, 40 ); // in front of engy building status + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudMenuEngyBuild::CHudMenuEngyBuild( const char *pElementName ) + : CHudBaseBuildMenu( pElementName, "HudMenuEngyBuild" ) +{ + Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_MISCSTATUS ); + + for ( int i=0; i<NUM_ENGY_BUILDINGS; i++ ) + { + char buf[32]; + + Q_snprintf( buf, sizeof(buf), "active_item_%d", i+1 ); + m_pAvailableObjects[i] = new EditablePanel( this, buf ); + + Q_snprintf( buf, sizeof(buf), "already_built_item_%d", i+1 ); + m_pAlreadyBuiltObjects[i] = new EditablePanel( this, buf ); + + Q_snprintf( buf, sizeof(buf), "cant_afford_item_%d", i+1 ); + m_pCantAffordObjects[i] = new EditablePanel( this, buf ); + + Q_snprintf( buf, sizeof(buf), "unavailable_item_%d", i+1 ); + m_pUnavailableObjects[i] = new EditablePanel( this, buf ); + } + + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_pActiveSelection = NULL; + + m_iSelectedItem = -1; + + m_pBuildLabelBright = NULL; + m_pBuildLabelDim = NULL; + + m_pDestroyLabelBright = NULL; + m_pDestroyLabelDim = NULL; + + m_bInConsoleMode = false; + m_eCurrentBuildMenuLayout = BUILDMENU_DEFAULT; + + RegisterForRenderGroup( "mid" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMenuEngyBuild::ApplySchemeSettings( IScheme *pScheme ) +{ + bool bSteamController = ::input->IsSteamControllerActive(); + bool b360Style = ( bSteamController || IsConsole() || tf_build_menu_controller_mode.GetBool() ); + + // load control settings... + + if ( b360Style ) + { + auto res_dir = bSteamController ? "resource/UI/build_menu_sc" : "resource/UI/build_menu_360"; + + LoadControlSettings( VarArgs("%s/HudMenuEngyBuild.res", res_dir ) ); + + // Load the already built images, destroyable + m_pAlreadyBuiltObjects[0]->LoadControlSettings( VarArgs( "%s/sentry_already_built.res", res_dir ) ); + m_pAlreadyBuiltObjects[1]->LoadControlSettings( VarArgs( "%s/dispenser_already_built.res", res_dir ) ); + m_pAlreadyBuiltObjects[2]->LoadControlSettings( VarArgs( "%s/tele_entrance_already_built.res", res_dir ) ); + m_pAlreadyBuiltObjects[3]->LoadControlSettings( VarArgs( "%s/tele_exit_already_built.res", res_dir ) ); + + m_pAvailableObjects[0]->LoadControlSettings( VarArgs( "%s/sentry_active.res", res_dir ) ); + m_pAvailableObjects[1]->LoadControlSettings( VarArgs( "%s/dispenser_active.res", res_dir ) ); + m_pAvailableObjects[2]->LoadControlSettings( VarArgs( "%s/tele_entrance_active.res", res_dir ) ); + m_pAvailableObjects[3]->LoadControlSettings( VarArgs( "%s/tele_exit_active.res", res_dir ) ); + + m_pCantAffordObjects[0]->LoadControlSettings( VarArgs( "%s/sentry_cant_afford.res", res_dir ) ); + m_pCantAffordObjects[1]->LoadControlSettings( VarArgs( "%s/dispenser_cant_afford.res", res_dir ) ); + m_pCantAffordObjects[2]->LoadControlSettings( VarArgs( "%s/tele_entrance_cant_afford.res", res_dir ) ); + m_pCantAffordObjects[3]->LoadControlSettings( VarArgs( "%s/tele_exit_cant_afford.res", res_dir ) ); + + m_pUnavailableObjects[0]->LoadControlSettings( "resource/UI/build_menu/sentry_unavailable.res" ); + m_pUnavailableObjects[1]->LoadControlSettings( "resource/UI/build_menu/dispenser_unavailable.res" ); + m_pUnavailableObjects[2]->LoadControlSettings( "resource/UI/build_menu/tele_entrance_unavailable.res" ); + m_pUnavailableObjects[3]->LoadControlSettings( "resource/UI/build_menu/tele_exit_unavailable.res" ); + + m_pActiveSelection = dynamic_cast< CIconPanel * >( FindChildByName( "active_selection_bg" ) ); + + m_pBuildLabelBright = dynamic_cast< CExLabel * >( FindChildByName( "BuildHintLabel_Bright" ) ); + m_pBuildLabelDim = dynamic_cast< CExLabel * >( FindChildByName( "BuildHintLabel_Dim" ) ); + + m_pDestroyLabelBright = dynamic_cast< CExLabel * >( FindChildByName( "DestroyHintLabel_Bright" ) ); + m_pDestroyLabelDim = dynamic_cast< CExLabel * >( FindChildByName( "DestroyHintLabel_Dim" ) ); + + // Reposition the active selection to the default position + m_iSelectedItem = -1; // force reposition + SetSelectedItem( 1 ); + } + else + { + const char *pszCustomDir = NULL; + switch( m_eCurrentBuildMenuLayout ) + { + case BUILDMENU_PIPBOY: + pszCustomDir = "resource/UI/build_menu/pipboy"; + break; + + default: + case BUILDMENU_DEFAULT: + pszCustomDir = "resource/UI/build_menu"; + break; + } + + LoadControlSettings( VarArgs("%s/HudMenuEngyBuild.res",pszCustomDir) ); + + // Load the already built images, not destroyable + for ( int i=0; i<NUM_ENGY_BUILDINGS; ++i ) + { + CExLabel *pNumberLabel = NULL; + m_pAvailableObjects[i]->LoadControlSettings( VarArgs( "%s/%s", pszCustomDir, m_Buildings[i].m_pszConstructAvailableObjectRes ) ); + m_pAlreadyBuiltObjects[i]->LoadControlSettings( VarArgs( "%s/%s", pszCustomDir, m_Buildings[i].m_pszConstructAlreadyBuiltObjectRes ) ); + m_pCantAffordObjects[i]->LoadControlSettings( VarArgs( "%s/%s", pszCustomDir, m_Buildings[i].m_pszConstructCantAffordObjectRes ) ); + m_pUnavailableObjects[i]->LoadControlSettings( VarArgs( "%s/%s", pszCustomDir, m_Buildings[i].m_pszConstructUnavailableObjectRes ) ); + + // Set the Numerical Number + pNumberLabel = dynamic_cast< CExLabel * >( m_pAvailableObjects[i]->FindChildByName( "NumberLabel" ) ); + if ( pNumberLabel ) + { + pNumberLabel->SetText( VarArgs( "%d", i+1 ) ); + } + // Set the Numerical Number + pNumberLabel = dynamic_cast<CExLabel *>( m_pAlreadyBuiltObjects[i]->FindChildByName( "NumberLabel" ) ); + if ( pNumberLabel ) + { + pNumberLabel->SetText( VarArgs( "%d", i+1 ) ); + } + // Set the Numerical Number + pNumberLabel = dynamic_cast<CExLabel *>( m_pCantAffordObjects[i]->FindChildByName( "NumberLabel" ) ); + if ( pNumberLabel ) + { + pNumberLabel->SetText( VarArgs( "%d", i+1 ) ); + } + // Set the Numerical Number + pNumberLabel = dynamic_cast<CExLabel *>( m_pUnavailableObjects[i]->FindChildByName( "NumberLabel" ) ); + if ( pNumberLabel ) + { + pNumberLabel->SetText( VarArgs( "%d", i+1 ) ); + } + } + + m_pActiveSelection = NULL; + + m_pBuildLabelBright = NULL; + m_pBuildLabelDim = NULL; + + m_pDestroyLabelBright = NULL; + m_pDestroyLabelDim = NULL; + } + + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + // Set the cost label + for ( int i=0; i<NUM_ENGY_BUILDINGS; i++ ) + { + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( i+1, iBuilding, iMode, m_Buildings ); + int iCost = ( pLocalPlayer ) ? pLocalPlayer->m_Shared.CalculateObjectCost( pLocalPlayer, iBuilding ) : GetObjectInfo( iBuilding )->m_Cost; + + m_pAvailableObjects[i]->SetDialogVariable( "metal", iCost ); + m_pAlreadyBuiltObjects[i]->SetDialogVariable( "metal", iCost ); + m_pCantAffordObjects[i]->SetDialogVariable( "metal", iCost ); + m_pUnavailableObjects[i]->SetDialogVariable( "metal", iCost ); + } + + BaseClass::ApplySchemeSettings( pScheme ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMenuEngyBuild::GetBuildingIDAndModeFromSlot( int iSlot, int &iBuilding, int &iMode, const EngyConstructBuilding_t (&buildings)[ NUM_ENGY_BUILDINGS ] ) +{ + iBuilding = OBJ_LAST; + iMode = 0; + int index = iSlot - 1; + if ( index >= 0 && index < NUM_ENGY_BUILDINGS ) + { + iBuilding = buildings[index].m_iObjectType; + iMode = buildings[index].m_iMode; + } + else + { + Assert( !"What slot are we asking for and why?" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Keyboard input hook. Return 0 if handled +//----------------------------------------------------------------------------- +int CHudMenuEngyBuild::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding ) +{ + if ( !ShouldDraw() ) + { + return 1; + } + + if ( !down ) + { + return 1; + } + + bool bController = ( IsConsole() || ( keynum >= JOYSTICK_FIRST ) ); + + if ( bController ) + { + int iNewSelection = m_iSelectedItem; + + switch( keynum ) + { + case KEY_XBUTTON_UP: + case STEAMCONTROLLER_DPAD_UP: + // jump to last + iNewSelection = NUM_ENGY_BUILDINGS; + break; + + case KEY_XBUTTON_DOWN: + case STEAMCONTROLLER_DPAD_DOWN: + // jump to first + iNewSelection = 1; + break; + + case KEY_XBUTTON_RIGHT: + case STEAMCONTROLLER_DPAD_RIGHT: + // move selection to the right + iNewSelection++; + if ( iNewSelection > NUM_ENGY_BUILDINGS ) + iNewSelection = 1; + break; + + case KEY_XBUTTON_LEFT: + case STEAMCONTROLLER_DPAD_LEFT: + // move selection to the left + iNewSelection--; + if ( iNewSelection < 1 ) + iNewSelection = NUM_ENGY_BUILDINGS; + break; + + case KEY_XBUTTON_A: + case KEY_XBUTTON_RTRIGGER: + case STEAMCONTROLLER_A: + // build selected item + SendBuildMessage( m_iSelectedItem ); + return 0; + + case KEY_XBUTTON_Y: + case KEY_XBUTTON_LTRIGGER: + case STEAMCONTROLLER_Y: + { + // destroy selected item + bool bSuccess = SendDestroyMessage( m_iSelectedItem ); + + if ( bSuccess ) + { + engine->ExecuteClientCmd( "lastinv" ); + } + } + return 0; + + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + // cancel, close the menu + engine->ExecuteClientCmd( "lastinv" ); + return 0; + + default: + return 1; // key not handled + } + + SetSelectedItem( iNewSelection ); + + return 0; + } + else + { + int iSlot = 0; + + // convert slot1, slot2 etc to 1,2,3,4 + if( pszCurrentBinding && ( !Q_strncmp( pszCurrentBinding, "slot", NUM_ENGY_BUILDINGS ) && Q_strlen(pszCurrentBinding) > NUM_ENGY_BUILDINGS ) ) + { + const char *pszNum = pszCurrentBinding+NUM_ENGY_BUILDINGS; + iSlot = atoi(pszNum); + + // slot10 cancels + if ( iSlot == 10 ) + { + engine->ExecuteClientCmd( "lastinv" ); + return 0; + } + + // allow slot1 - slot4 + if ( iSlot < 1 || iSlot > NUM_ENGY_BUILDINGS ) + return 1; + } + else + { + switch( keynum ) + { + case KEY_1: + iSlot = 1; + break; + case KEY_2: + iSlot = 2; + break; + case KEY_3: + iSlot = 3; + break; + case KEY_4: + iSlot = 4; + break; + + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + // Eat these keys + return 0; + + case KEY_0: + case KEY_XBUTTON_B: + case STEAMCONTROLLER_B: + // cancel, close the menu + engine->ExecuteClientCmd( "lastinv" ); + return 0; + + default: + return 1; // key not handled + } + } + + if ( iSlot > 0 ) + { + SendBuildMessage( iSlot ); + return 0; + } + } + + return 1; // key not handled +} + +void CHudMenuEngyBuild::SendBuildMessage( int iSlot ) +{ + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + if ( !pLocalPlayer ) + return; + + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( iSlot, iBuilding, iMode, m_Buildings ); + + if ( CanBuild( iSlot ) == false ) + { + return; + } + + C_BaseObject *pObj = pLocalPlayer->GetObjectOfType( iBuilding, iMode ); + int iCost = pLocalPlayer->m_Shared.CalculateObjectCost( pLocalPlayer, iBuilding ); + + int iBuildDisposableSents = CB_CANNOT_BUILD; + if ( TFGameRules()->GameModeUsesUpgrades() && iBuilding == OBJ_SENTRYGUN ) + { + iBuildDisposableSents = pLocalPlayer->CanBuild( iBuilding, iMode ); + } + + // If we don't already have a sentry (NULL), or we're allowed to build multiple, and we can afford it + if ( ( pObj == NULL || iBuildDisposableSents == CB_CAN_BUILD ) && pLocalPlayer->GetAmmoCount( TF_AMMO_METAL ) >= iCost ) + { + char szCmd[128]; + Q_snprintf( szCmd, sizeof(szCmd), "build %d %d", iBuilding, iMode ); + engine->ClientCmd( szCmd ); + + // NVNT send the build command + if ( haptics ) + haptics->ProcessHapticEvent(2, "Game", szCmd); + + } + else + { + pLocalPlayer->EmitSound( "Player.DenyWeaponSelection" ); + } +} + +bool CHudMenuEngyBuild::SendDestroyMessage( int iSlot ) +{ + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + if ( !pLocalPlayer ) + return false; + + bool bSuccess = false; + + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( iSlot, iBuilding, iMode, m_Buildings ); + + C_BaseObject *pObj = pLocalPlayer->GetObjectOfType( iBuilding, iMode ); + + if ( pObj != NULL ) + { + char szCmd[128]; + Q_snprintf( szCmd, sizeof(szCmd), "destroy %d %d", iBuilding, iMode ); + engine->ClientCmd( szCmd ); + // NVNT send the destroy command + if ( haptics ) + haptics->ProcessHapticEvent(2, "Game", szCmd); + bSuccess = true; + } + else + { + pLocalPlayer->EmitSound( "Player.DenyWeaponSelection" ); + } + + return bSuccess; +} + +// NVNT gate for placing effect. + +void CHudMenuEngyBuild::OnTick( void ) +{ + if ( !IsVisible() ) + return; + + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + if ( !pLocalPlayer ) + return; + + int iAccount = pLocalPlayer->GetAmmoCount( TF_AMMO_METAL ); + + for ( int i=0;i<NUM_ENGY_BUILDINGS; i++ ) + { + int iRemappedObjectID, iMode; + GetBuildingIDAndModeFromSlot( i + 1, iRemappedObjectID, iMode, m_Buildings ); + + // update this slot + C_BaseObject *pObj = NULL; + + if ( pLocalPlayer ) + { + pObj = pLocalPlayer->GetObjectOfType( iRemappedObjectID, iMode ); + } + + m_pAvailableObjects[i]->SetVisible( false ); + m_pAlreadyBuiltObjects[i]->SetVisible( false ); + m_pCantAffordObjects[i]->SetVisible( false ); + m_pUnavailableObjects[i]->SetVisible( false ); + + if ( !m_Buildings[i].m_bEnabled ) + { + continue; + } + + int iCost = pLocalPlayer->m_Shared.CalculateObjectCost( pLocalPlayer, iRemappedObjectID ); + bool bAvailable = CanBuild( i + 1 ); + + // If the building is already built, and we don't have an ability to build more than one (sentry) + if ( pObj != NULL && !pObj->IsPlacing() && !( pLocalPlayer->CanBuild( iRemappedObjectID, iMode ) == CB_CAN_BUILD ) ) + { + m_pAlreadyBuiltObjects[i]->SetVisible( true ); + } + // unavailable + else if ( bAvailable == false ) + { + m_pUnavailableObjects[i]->SetVisible( true ); + } + // See if we can afford it + else if ( iAccount < iCost ) + { + m_pCantAffordObjects[i]->SetVisible( true ); + } + else + { + // we can buy it + m_pAvailableObjects[i]->SetVisible( true ); + } + } +} + + +void CHudMenuEngyBuild::SetVisible( bool state ) +{ + if ( state == true ) + { + InitBuildings(); + + // close the weapon selection menu + engine->ClientCmd( "cancelselect" ); + + bool bConsoleMode = ( IsConsole() || tf_build_menu_controller_mode.GetBool() ); + + if ( bConsoleMode != m_bInConsoleMode ) + { + InvalidateLayout( true, true ); + m_bInConsoleMode = bConsoleMode; + } + else + { + // See if our layout needs to change, due to equipped items + buildmenulayouts_t eDesired = CalcCustomBuildMenuLayout(); + if ( eDesired != m_eCurrentBuildMenuLayout ) + { + m_eCurrentBuildMenuLayout = eDesired; + InvalidateLayout( true, true ); + } + } + + // set the %lastinv% dialog var to our binding + const char *key = engine->Key_LookupBinding( "lastinv" ); + if ( !key ) + { + key = "< not bound >"; + } + + SetDialogVariable( "lastinv", key ); + + // Set selection to the first available building that we can build + + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + if ( !pLocalPlayer ) + return; + + int iDefaultSlot = 1; + + // Find the first slot that represents a building that we haven't built + int iSlot; + for ( iSlot = 1; iSlot <= NUM_ENGY_BUILDINGS; iSlot++ ) + { + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( iSlot, iBuilding, iMode, m_Buildings ); + C_BaseObject *pObj = pLocalPlayer->GetObjectOfType( iBuilding, iMode ); + + if ( pObj == NULL ) + { + iDefaultSlot = iSlot; + break; + } + } + + m_iSelectedItem = -1; //force redo + SetSelectedItem( iDefaultSlot ); + + HideLowerPriorityHudElementsInGroup( "mid" ); + + for ( int i=0; i<NUM_ENGY_BUILDINGS; i++ ) + { + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( i+1, iBuilding, iMode, m_Buildings ); + int iCost = pLocalPlayer->m_Shared.CalculateObjectCost( pLocalPlayer, iBuilding ); + m_pAvailableObjects[i]->SetDialogVariable( "metal", iCost ); + m_pAlreadyBuiltObjects[i]->SetDialogVariable( "metal", iCost ); + m_pCantAffordObjects[i]->SetDialogVariable( "metal", iCost ); + } + } + else + { + UnhideLowerPriorityHudElementsInGroup( "mid" ); + } + + BaseClass::SetVisible( state ); +} + +void CHudMenuEngyBuild::SetSelectedItem( int iSlot ) +{ + if ( m_iSelectedItem != iSlot ) + { + m_iSelectedItem = iSlot; + + // move the selection item to the new position + if ( m_pActiveSelection ) + { + // move the selection background + int x, y; + m_pAlreadyBuiltObjects[m_iSelectedItem-1]->GetPos( x, y ); + + x -= XRES(NUM_ENGY_BUILDINGS); + y -= XRES(NUM_ENGY_BUILDINGS); + + m_pActiveSelection->SetPos( x, y ); + + UpdateHintLabels(); + } + } +} + +void CHudMenuEngyBuild::UpdateHintLabels( void ) +{ + // hilight the action we can perform ( build or destroy or neither ) + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + + if ( pLocalPlayer ) + { + int iBuilding, iMode; + GetBuildingIDAndModeFromSlot( m_iSelectedItem, iBuilding, iMode, m_Buildings ); + C_BaseObject *pObj = pLocalPlayer->GetObjectOfType( iBuilding ); + + bool bDestroyLabelBright = false; + bool bBuildLabelBright = false; + + int iCost = pLocalPlayer->m_Shared.CalculateObjectCost( pLocalPlayer, iBuilding ); + + if ( pObj ) + { + // hilight destroy, we have a building + bDestroyLabelBright = true; + } + else if ( pLocalPlayer->GetAmmoCount( TF_AMMO_METAL ) >= iCost ) // I can afford it + { + // hilight build, we can build this + bBuildLabelBright = true; + } + else + { + // dim both, do nothing + } + + if ( m_pDestroyLabelBright && m_pDestroyLabelDim && m_pBuildLabelBright && m_pBuildLabelDim ) + { + m_pDestroyLabelBright->SetVisible( bDestroyLabelBright ); + m_pDestroyLabelDim->SetVisible( !bDestroyLabelBright ); + + m_pBuildLabelBright->SetVisible( bBuildLabelBright ); + m_pBuildLabelDim->SetVisible( !bBuildLabelBright ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +buildmenulayouts_t CHudMenuEngyBuild::CalcCustomBuildMenuLayout( void ) +{ + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + if ( !pLocalPlayer ) + return BUILDMENU_DEFAULT; + + int iMenu = BUILDMENU_DEFAULT; + CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iMenu, set_custom_buildmenu ); + return (buildmenulayouts_t)iMenu; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMenuEngyBuild::InitBuildings() +{ + for( int i=0; i<NUM_ENGY_BUILDINGS; ++i ) + { + m_Buildings[i] = g_kEngyBuildings[i]; + } + + ReplaceBuildings( m_Buildings ); + + InvalidateLayout( true, true ); +} + + +void CHudMenuEngyBuild::ReplaceBuildings( EngyConstructBuilding_t (&targetBuildings)[NUM_ENGY_BUILDINGS] ) +{ + C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); + if ( !pLocalPlayer ) + return; + + CUtlVector< const EngyBuildingReplacement_t* > vecReplacements; + +#ifdef STAGING_ONLY + + int iOverrideType = -1; + CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iOverrideType, override_engineer_object_type ); + if ( iOverrideType >= 0 && iOverrideType < ARRAYSIZE( s_alternateEngineerBuildings ) ) + { + vecReplacements.AddToTail( &s_alternateEngineerBuildings[iOverrideType] ); + } + + iOverrideType = -1; + CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iOverrideType, override_engineer_object_type_2 ); + if ( iOverrideType >= 0 && iOverrideType < ARRAYSIZE( s_alternateEngineerBuildings ) ) + { + vecReplacements.AddToTail( &s_alternateEngineerBuildings[iOverrideType] ); + } + // add more replacement attributes here + +#endif + + // verify the override data to make sure that they don't conflict with each other + int iReplacedSlots = 0; + int iDisabledSlots = 0; + bool bReplaced = false; + for ( int i=0; i<vecReplacements.Count(); ++i ) + { + const EngyBuildingReplacement_t* pReplace = vecReplacements[i]; + int iReplacingSlots = pReplace->m_iReplacementSlots; + int iDisablingSlots = pReplace->m_iDisableSlots; + + if ( iReplacedSlots & iReplacingSlots ) + { + AssertMsg( 0, "Trying to replace the same engineer building slot multiple time" ); + continue; + } + + if ( iReplacedSlots & iDisablingSlots ) + { + AssertMsg( 0, "Trying to disable a replaced engineer building slot" ); + continue; + } + + if ( iDisabledSlots & iReplacingSlots ) + { + AssertMsg( 0, "Trying to replace a disabled slot" ); + continue; + } + + // no conflict, replace the building + for ( int j=0; j<ARRAYSIZE( flagSlots ); ++j ) + { + COMPILE_TIME_ASSERT( ARRAYSIZE( targetBuildings ) == ARRAYSIZE( flagSlots ) ); + if ( flagSlots[j] & iReplacingSlots ) + { + targetBuildings[j] = pReplace->m_building; + } + else if ( flagSlots[j] & iDisablingSlots ) + { + targetBuildings[j].m_bEnabled = false; + } + } + + iReplacedSlots |= iReplacingSlots; + iDisabledSlots |= iDisablingSlots; + bReplaced = true; + } +} + + +bool CHudMenuEngyBuild::CanBuild( int iSlot ) +{ + bool bInTraining = TFGameRules() && TFGameRules()->IsInTraining(); + if ( bInTraining == false ) + { + int slot = iSlot - 1; + if ( slot >= 0 && slot < NUM_ENGY_BUILDINGS ) + { + return m_Buildings[slot].m_bEnabled; + } + + return false; + } + + bool bCanBuild = true; + switch ( iSlot ) + { + case 1: + { + ConVarRef training_can_build_sentry( "training_can_build_sentry"); + bCanBuild = training_can_build_sentry.GetInt() != 0; + } + break; + case 2: + { + ConVarRef training_can_build_dispenser( "training_can_build_dispenser"); + bCanBuild = training_can_build_dispenser.GetInt() != 0; + } + break; + case 3: + { + ConVarRef training_can_build_tele_entrance( "training_can_build_tele_entrance"); + bCanBuild = training_can_build_tele_entrance.GetInt() != 0; + } + break; + case 4: + { + ConVarRef training_can_build_tele_exit( "training_can_build_tele_exit"); + bCanBuild = training_can_build_tele_exit.GetInt() != 0; + } + break; + } + return bCanBuild; +}
\ No newline at end of file |