summaryrefslogtreecommitdiff
path: root/game/shared/tf2/weapon_builder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf2/weapon_builder.cpp')
-rw-r--r--game/shared/tf2/weapon_builder.cpp530
1 files changed, 530 insertions, 0 deletions
diff --git a/game/shared/tf2/weapon_builder.cpp b/game/shared/tf2/weapon_builder.cpp
new file mode 100644
index 0000000..7d90410
--- /dev/null
+++ b/game/shared/tf2/weapon_builder.cpp
@@ -0,0 +1,530 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The "weapon" used to build objects
+//
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "EntityList.h"
+#include "in_buttons.h"
+#include "weapon_builder.h"
+#include "tf_obj.h"
+#include "sendproxy.h"
+#include "weapon_objectselection.h"
+#include "info_act.h"
+#include "vguiscreen.h"
+
+extern ConVar tf2_object_hard_limits;
+extern ConVar tf_fastbuild;
+
+EXTERN_SEND_TABLE(DT_BaseCombatWeapon)
+
+IMPLEMENT_SERVERCLASS_ST(CWeaponBuilder, DT_WeaponBuilder)
+ SendPropInt( SENDINFO( m_iBuildState ), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iCurrentObject ), BUILDER_OBJECT_BITS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iCurrentObjectState ), 4, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO( m_hObjectBeingBuilt ) ),
+ SendPropTime( SENDINFO( m_flStartTime ) ),
+ SendPropTime( SENDINFO( m_flTotalTime ) ),
+ SendPropArray
+ (
+ SendPropInt( SENDINFO_ARRAY(m_bObjectValidity), 1, SPROP_UNSIGNED), m_bObjectValidity
+ ),
+ SendPropArray
+ (
+ SendPropInt( SENDINFO_ARRAY(m_bObjectBuildability), 1, SPROP_UNSIGNED), m_bObjectBuildability
+ ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_builder, CWeaponBuilder );
+PRECACHE_WEAPON_REGISTER(weapon_builder);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponBuilder::CWeaponBuilder()
+{
+ for ( int i=0; i < m_bObjectValidity.Count(); i++ )
+ m_bObjectValidity.Set( i, 0 );
+
+ m_iCurrentObject = BUILDER_INVALID_OBJECT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( "models/weapons/v_slam.mdl" );
+ PrecacheVGuiScreen( "screen_human_pda" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets info about the control panels
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+{
+ pPanelName = "screen_human_pda";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::ShouldShowControlPanels( void )
+{
+ if ( GetActivity() == ACT_VM_IDLE )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::UpdateOnRemove( void )
+{
+ // Tell the player he's lost his build weapon
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( pOwner && pOwner->GetWeaponBuilder() == this )
+ {
+ pOwner->SetWeaponBuilder( NULL );
+ }
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Builder weapon has just been given to a player
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::Equip( CBaseCombatCharacter *pOwner )
+{
+ BaseClass::Equip( pOwner );
+ ((CBaseTFPlayer*)pOwner)->SetWeaponBuilder( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a new object type to this build weapon. This will allow
+// the player carrying this builder to build the object.
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::AddBuildableObject( int iObjectType )
+{
+ m_bObjectValidity.Set( iObjectType, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::CanDeploy( void )
+{
+ if ( m_iCurrentObject != BUILDER_INVALID_OBJECT )
+ {
+ SetCurrentState( BS_PLACING );
+ StartPlacement();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.35f;
+ }
+ else
+ {
+ m_hObjectBeingBuilt = NULL;
+ SetCurrentState( BS_IDLE );
+ SetCurrentObject( m_iCurrentObject );
+ }
+ return BaseClass::CanDeploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::Deploy( )
+{
+#if !defined( CLIENT_DLL )
+ if ( m_hObjectBeingBuilt.Get() && m_hObjectBeingBuilt->IsAnUpgrade() )
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_SLAM_STICKWALL_ND_DRAW, (char*)GetAnimPrefix() );
+
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
+#else
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prevent switching when working on something
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::CanHolster( void )
+{
+ if ( IsBuilding() )
+ return false;
+
+ return BaseClass::CanHolster();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseCombatWeapon *CWeaponBuilder::GetLastWeapon( void )
+{
+ return BaseClass::GetLastWeapon();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop placement when holstering
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID )
+ {
+ SetCurrentState( BS_IDLE );
+ }
+ StopPlacement();
+
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Ignore input while the player's building anything
+ if ( pOwner->IsBuilding() )
+ return;
+
+ // Switch away if I'm not in placement mode
+ if ( m_iBuildState != BS_PLACING && m_iBuildState != BS_PLACING_INVALID )
+ {
+ pOwner->SwitchToNextBestWeapon( NULL );
+ return;
+ }
+
+ if (( pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ PrimaryAttack();
+ }
+
+ // Allow shield post frame
+ AllowShieldPostFrame( true );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start placing or building the currently selected object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // What state should we move to?
+ switch( m_iBuildState )
+ {
+ case BS_IDLE:
+ {
+ // Idle state starts selection
+ SetCurrentState( BS_SELECTING );
+ }
+ break;
+
+ case BS_SELECTING:
+ {
+ // Do nothing, client handles selection
+ return;
+ }
+ break;
+
+ case BS_PLACING:
+ {
+ if ( m_hObjectBeingBuilt )
+ {
+ // Give the object a chance to veto the "start building" command. Objects like barbed wire
+ // may want to change their properties instead of actually building yet.
+ if ( m_hObjectBeingBuilt->PreStartBuilding() )
+ {
+ int iFlags = m_hObjectBeingBuilt->GetObjectFlags();
+
+ // Can't build if the game hasn't started
+ if ( !tf_fastbuild.GetInt() && CurrentActIsAWaitingAct() )
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Can't build until the game's started.\n" );
+ return;
+ }
+
+ StartBuilding();
+
+ // Should we switch away?
+ if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT )
+ {
+ // Start placing another
+ SetCurrentState( BS_PLACING );
+ StartPlacement();
+ }
+ else
+ {
+ pOwner->SwitchToNextBestWeapon( NULL );
+ }
+ }
+ }
+ }
+ break;
+
+ case BS_PLACING_INVALID:
+ {
+ WeaponSound( SINGLE_NPC );
+
+ // If there is any associated error text when placing the object, display it
+ if( m_hObjectBeingBuilt != NULL )
+ {
+ if (m_hObjectBeingBuilt->MustBeBuiltInResourceZone())
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Only placeable in an empty resource zone.\n" );
+ }
+ else if (m_hObjectBeingBuilt->MustBeBuiltInConstructionYard())
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Only placeable in a construction yard.\n" );
+ }
+ }
+ }
+ break;
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the builder to the specified state
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::SetCurrentState( int iState )
+{
+ // Check the current build state... we may need to shut some stuff down...
+ switch(m_iBuildState)
+ {
+ case BS_PLACING:
+ case BS_PLACING_INVALID:
+ {
+ if ((iState != BS_PLACING) && (iState != BS_PLACING_INVALID) && (iState != BS_BUILDING))
+ {
+ StopPlacement();
+ WeaponSound( SPECIAL1 );
+ }
+ }
+ break;
+ }
+
+ m_iBuildState = iState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the builder to the specified object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::SetCurrentObject( int iObject )
+{
+ // Fixup for invalid objects
+ if (iObject < 0)
+ iObject = BUILDER_INVALID_OBJECT;
+
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ int i;
+
+ // If -1 was passed in, set to our first available object
+ if ( iObject == BUILDER_INVALID_OBJECT )
+ {
+ for ( i = 0; i < OBJ_LAST; i++ )
+ {
+ if ( m_bObjectValidity[i] )
+ {
+ iObject = i;
+ break;
+ }
+ }
+ }
+
+ // Recalculate the buildability of each object (for propagation to the client)
+ for ( i=0; i < m_bObjectBuildability.Count(); i++ )
+ m_bObjectBuildability.Set( i, 0 );
+
+ for ( i = 0; i < OBJ_LAST; i++ )
+ {
+ if ( m_bObjectValidity[i] && pOwner->CanBuild(i) == CB_CAN_BUILD )
+ {
+ m_bObjectBuildability.Set( i, true );
+ }
+ }
+
+ m_iCurrentObject = iObject;
+ m_iCurrentObjectState = pOwner->CanBuild( m_iCurrentObject );
+ m_flStartTime = 0;
+ m_flTotalTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle updates the position of the build placement model
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::WeaponIdle( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // If we're in placement mode, update the placement model
+ switch( m_iBuildState )
+ {
+ case BS_PLACING:
+ case BS_PLACING_INVALID:
+ {
+ if ( UpdatePlacement() )
+ {
+ SetCurrentState( BS_PLACING );
+ }
+ else
+ {
+ SetCurrentState( BS_PLACING_INVALID );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+// Check to see if it affects the medikit
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start placing the object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StartPlacement( void )
+{
+ StopPlacement();
+
+ // Create the slab
+ m_hObjectBeingBuilt = (CBaseObject*)CreateEntityByName( GetObjectInfo( m_iCurrentObject )->m_pClassName );
+ if ( m_hObjectBeingBuilt )
+ {
+ m_hObjectBeingBuilt->Spawn();
+ m_hObjectBeingBuilt->StartPlacement( ToBaseTFPlayer( GetOwner() ) );
+ UpdatePlacement();
+
+ // Stomp this here in the same frame we make the object, so prevent clientside warnings that it's under attack
+ m_hObjectBeingBuilt->m_iHealth = OBJECT_CONSTRUCTION_STARTINGHEALTH;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the viewmodel according to the type of object we're placing
+//-----------------------------------------------------------------------------
+const char *CWeaponBuilder::GetViewModel( int viewmodelindex /*=0*/ ) const
+{
+ if ( m_hObjectBeingBuilt.Get() && m_hObjectBeingBuilt->IsAnUpgrade() )
+ return "models/weapons/v_slam.mdl";
+
+ return BaseClass::GetViewModel( viewmodelindex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StopPlacement( void )
+{
+ if ( m_hObjectBeingBuilt )
+ {
+ m_hObjectBeingBuilt->StopPlacement();
+ m_hObjectBeingBuilt = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the placement model to the current position. Return false if it's an invalid position
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::UpdatePlacement( void )
+{
+ if ( !m_hObjectBeingBuilt )
+ return false;
+
+ return m_hObjectBeingBuilt->UpdatePlacement( ToBaseTFPlayer(GetOwner()) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player holding this weapon has started building something
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StartBuilding( void )
+{
+ if ( m_hObjectBeingBuilt.Get() && UpdatePlacement() )
+ {
+ SetCurrentState( BS_BUILDING );
+ m_hObjectBeingBuilt->StartBuilding( GetOwner() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player holding this weapon has aborted the build of an object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StoppedBuilding( int iObjectType )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ SetCurrentState( BS_IDLE );
+
+ WeaponSound( SPECIAL2 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::IsBuilding( void )
+{
+ return ( m_iBuildState == BS_BUILDING );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just finished building an object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::FinishedObject( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // We're no longer building anything...
+ m_hObjectBeingBuilt = NULL;
+
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ SetCurrentState( BS_IDLE );
+ }
+}