Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

531 lines
15 KiB

5 years ago
//========= 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 );
}
}