You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
931 lines
25 KiB
931 lines
25 KiB
//========= 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; |
|
} |