//========= 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 #include #include #include "c_baseobject.h" #include "tf_gamerules.h" #include "tf_item_inventory.h" #include "tf_hud_menu_spy_build.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" #ifdef STAGING_ONLY using namespace vgui; // Set to 1 to simulate xbox-style menu interaction extern ConVar tf_build_menu_controller_mode; const SpyConstructBuilding_t g_kSpyBuildings[ NUM_SPY_BUILDINGS ] = { // Sap Trap SpyConstructBuilding_t( true, OBJ_SPY_TRAP, MODE_SPY_TRAP_RADIUS_STEALTH, "spy_trap_active.res", "spy_trap_already_built.res", "spy_trap_cant_afford.res", "spy_trap_unavailable.res", "spy_trap_active.res", "spy_trap_inactive.res", "spy_trap_inactive.res" ), // Reprogrammer SpyConstructBuilding_t( true, OBJ_SPY_TRAP, MODE_SPY_TRAP_REPROGRAM, "spy_trap_reprog_active.res", "spy_trap_reprog_already_built.res", "spy_trap_reprog_cant_afford.res", "spy_trap_reprog_unavailable.res", "spy_trap_reprog_active.res", "spy_trap_reprog_inactive.res", "spy_trap_reprog_inactive.res" ), // Pulse Magnet SpyConstructBuilding_t( true, OBJ_SPY_TRAP, MODE_SPY_TRAP_MAGNET, "spy_trap_magnet_active.res", "spy_trap_magnet_already_built.res", "spy_trap_magnet_cant_afford.res", "spy_trap_magnet_unavailable.res", "spy_trap_magnet_active.res", "spy_trap_magnet_inactive.res", "spy_trap_magnet_inactive.res" ), // // Repulsor // SpyConstructBuilding_t( true, // OBJ_SPY_TRAP, // MODE_SPY_TRAP_REPULSOR, // "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 trapSlots[NUM_SPY_BUILDINGS] = { 0x01, 0x02 // 0x04, }; const SpyConstructBuildingReplacement_t s_alternateSpyBuildings[] = { // Temp - modify this SpyConstructBuildingReplacement_t( OBJ_CATAPULT, 0, "spy_trap_active.res", "spy_trap_already_built.res", "spy_trap_cant_afford.res", "spy_trap_unavailable.res", "spy_trap_active.res", "spy_trap_inactive.res", "spy_trap_inactive.res", trapSlots[0], trapSlots[1] ), // Add more objects here }; //====================================== DECLARE_HUDELEMENT_DEPTH( CHudMenuSpyBuild, 40 ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CHudMenuSpyBuild::CHudMenuSpyBuild( const char *pElementName ) : CHudBaseBuildMenu( pElementName, "HudMenuSpyBuild" ) { Panel *pParent = g_pClientMode->GetViewport(); SetParent( pParent ); SetHiddenBits( HIDEHUD_MISCSTATUS ); for ( int i = 0; i < NUM_SPY_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_iCurrentBuildMenuLayout = SPY_BUILDMENU_DEFAULT; RegisterForRenderGroup( "mid" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMenuSpyBuild::ApplySchemeSettings( IScheme *pScheme ) { // load control settings... bool b360Style = ( IsConsole() || tf_build_menu_controller_mode.GetBool() ); if ( b360Style ) { // This entire block is unused and ancient. Will need to support a controller // once we deem spy traps as shippable, but for now they're a prototype. // LoadControlSettings( "resource/UI/build_menu_360/HudMenuSpyBuild.res" ); // // // Load the already built images, destroyable // m_pAlreadyBuiltObjects[0]->LoadControlSettings( "resource/UI/build_menu_360/sentry_already_built.res" ); // m_pAlreadyBuiltObjects[1]->LoadControlSettings( "resource/UI/build_menu_360/dispenser_already_built.res" ); // m_pAlreadyBuiltObjects[2]->LoadControlSettings( "resource/UI/build_menu_360/tele_entrance_already_built.res" ); // m_pAlreadyBuiltObjects[3]->LoadControlSettings( "resource/UI/build_menu_360/tele_exit_already_built.res" ); // // m_pAvailableObjects[0]->LoadControlSettings( "resource/UI/build_menu_360/sentry_active.res" ); // m_pAvailableObjects[1]->LoadControlSettings( "resource/UI/build_menu_360/dispenser_active.res" ); // m_pAvailableObjects[2]->LoadControlSettings( "resource/UI/build_menu_360/tele_entrance_active.res" ); // m_pAvailableObjects[3]->LoadControlSettings( "resource/UI/build_menu_360/tele_exit_active.res" ); // // m_pCantAffordObjects[0]->LoadControlSettings( "resource/UI/build_menu_360/sentry_cant_afford.res" ); // m_pCantAffordObjects[1]->LoadControlSettings( "resource/UI/build_menu_360/dispenser_cant_afford.res" ); // m_pCantAffordObjects[2]->LoadControlSettings( "resource/UI/build_menu_360/tele_entrance_cant_afford.res" ); // m_pCantAffordObjects[3]->LoadControlSettings( "resource/UI/build_menu_360/tele_exit_cant_afford.res" ); // // 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 activeselection to the default position m_iSelectedItem = -1; // force reposition SetSelectedItem( 1 ); } else { const char *pszCustomDir = NULL; switch( m_iCurrentBuildMenuLayout ) { default: case SPY_BUILDMENU_DEFAULT: pszCustomDir = "resource/UI/build_menu"; break; } LoadControlSettings( VarArgs("%s/HudMenuSpyBuild.res",pszCustomDir) ); // Load the already built images, not destroyable for ( int i = 0; i < NUM_SPY_BUILDINGS; ++i ) { 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 ) ); } m_pActiveSelection = NULL; m_pBuildLabelBright = NULL; m_pBuildLabelDim = NULL; m_pDestroyLabelBright = NULL; m_pDestroyLabelDim = NULL; } // Set the cost label for ( int i = 0; i < NUM_SPY_BUILDINGS; i++ ) { int iBuilding, iMode; GetBuildingIDAndModeFromSlot( i+1, iBuilding, iMode, m_Buildings ); int iCost = 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: //----------------------------------------------------------------------------- bool CHudMenuSpyBuild::ShouldDraw( void ) { if ( !TFGameRules() || !TFGameRules()->GameModeUsesUpgrades() ) return false; CTFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pPlayer ) return false; if ( !pPlayer->m_Shared.CanBuildSpyTraps() ) return false; return BaseClass::ShouldDraw(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMenuSpyBuild::GetBuildingIDAndModeFromSlot( int iSlot, int &iBuilding, int &iMode, const SpyConstructBuilding_t (&buildings)[ NUM_SPY_BUILDINGS ] ) { iBuilding = OBJ_LAST; iMode = 0; int index = iSlot - 1; if ( index >= 0 && index < NUM_SPY_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 CHudMenuSpyBuild::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_SPY_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_SPY_BUILDINGS ) iNewSelection = 1; break; case KEY_XBUTTON_LEFT: case STEAMCONTROLLER_DPAD_LEFT: // move selection to the left iNewSelection--; if ( iNewSelection < 1 ) iNewSelection = NUM_SPY_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_SPY_BUILDINGS ) && Q_strlen(pszCurrentBinding) > NUM_SPY_BUILDINGS ) ) { const char *pszNum = pszCurrentBinding + Q_strlen( "slot" ); iSlot = atoi(pszNum); // slot10 cancels if ( iSlot == 10 ) { engine->ExecuteClientCmd( "lastinv" ); return 0; } // allow slot1 - slot4 if ( iSlot < 1 || iSlot > NUM_SPY_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 CHudMenuSpyBuild::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 ); if ( !pObj && pLocalPlayer->GetAmmoCount( TF_AMMO_GRENADES1 ) >= 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 CHudMenuSpyBuild::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; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CHudMenuSpyBuild::OnTick( void ) { if ( !IsVisible() ) return; C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return; int iAmmo = pLocalPlayer->GetAmmoCount( TF_AMMO_GRENADES1 ); for ( int i = 0;i < NUM_SPY_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 ( iAmmo < iCost ) { m_pCantAffordObjects[i]->SetVisible( true ); } else { // we can buy it m_pAvailableObjects[i]->SetVisible( true ); } } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CHudMenuSpyBuild::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 int iDesired = CalcCustomBuildMenuLayout(); if ( iDesired != m_iCurrentBuildMenuLayout ) { m_iCurrentBuildMenuLayout = (spybuildmenulayouts_t)iDesired; 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_SPY_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_SPY_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 CHudMenuSpyBuild::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_SPY_BUILDINGS); y -= XRES(NUM_SPY_BUILDINGS); m_pActiveSelection->SetPos( x, y ); UpdateHintLabels(); } } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CHudMenuSpyBuild::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_GRENADES1 ) >= 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: //----------------------------------------------------------------------------- int CHudMenuSpyBuild::CalcCustomBuildMenuLayout( void ) const { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return SPY_BUILDMENU_DEFAULT; int iMenu = SPY_BUILDMENU_DEFAULT; CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iMenu, set_custom_buildmenu ); return iMenu; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHudMenuSpyBuild::InitBuildings() { for( int i = 0; i < NUM_SPY_BUILDINGS; ++i ) { m_Buildings[i] = g_kSpyBuildings[i]; } ReplaceBuildings( m_Buildings ); InvalidateLayout( true, true ); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CHudMenuSpyBuild::ReplaceBuildings( SpyConstructBuilding_t (&targetBuildings)[NUM_SPY_BUILDINGS] ) { C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pLocalPlayer ) return; int iOverrideType = -1; CALL_ATTRIB_HOOK_INT_ON_OTHER( pLocalPlayer, iOverrideType, override_engineer_object_type ); CUtlVector< const SpyConstructBuildingReplacement_t* > vecReplacements; if ( iOverrideType >= 0 && iOverrideType < ARRAYSIZE( s_alternateSpyBuildings ) ) { vecReplacements.AddToTail( &s_alternateSpyBuildings[iOverrideType] ); } // add more replacement attributes here // 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 SpyConstructBuildingReplacement_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( trapSlots ); ++j ) { COMPILE_TIME_ASSERT( ARRAYSIZE( targetBuildings ) == ARRAYSIZE( trapSlots ) ); if ( trapSlots[j] & iReplacingSlots ) { targetBuildings[j] = pReplace->m_building; } else if ( trapSlots[j] & iDisablingSlots ) { targetBuildings[j].m_bEnabled = false; } } iReplacedSlots |= iReplacingSlots; iDisabledSlots |= iDisablingSlots; bReplaced = true; } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CHudMenuSpyBuild::CanBuild( int iSlot ) { bool bInTraining = TFGameRules() && TFGameRules()->IsInTraining(); if ( bInTraining == false ) { int slot = iSlot - 1; if ( slot >= 0 && slot < NUM_SPY_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; } #endif // STAGING_ONLY