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.
1320 lines
37 KiB
1320 lines
37 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: The "weapon" used to build objects |
|
// |
|
// |
|
// $Workfile: $ |
|
// $Date: $ |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "tf_player.h" |
|
#include "entitylist.h" |
|
#include "in_buttons.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "engine/IEngineSound.h" |
|
#include "tf_obj.h" |
|
#include "sendproxy.h" |
|
#include "tf_weapon_builder.h" |
|
#include "vguiscreen.h" |
|
#include "tf_gamerules.h" |
|
#include "tf_obj_teleporter.h" |
|
#include "tf_obj_sapper.h" |
|
|
|
extern ISoundEmitterSystemBase *soundemitterbase; |
|
|
|
extern ConVar tf2_object_hard_limits; |
|
extern ConVar tf_fastbuild; |
|
|
|
|
|
EXTERN_SEND_TABLE(DT_BaseCombatWeapon) |
|
|
|
BEGIN_NETWORK_TABLE_NOBASE( CTFWeaponBuilder, DT_BuilderLocalData ) |
|
SendPropInt( SENDINFO( m_iObjectType ), BUILDER_OBJECT_BITS, SPROP_UNSIGNED ), |
|
SendPropEHandle( SENDINFO( m_hObjectBeingBuilt ) ), |
|
SendPropArray3( SENDINFO_ARRAY3( m_aBuildableObjectTypes ), SendPropBool( SENDINFO_ARRAY( m_aBuildableObjectTypes ) ) ), |
|
END_NETWORK_TABLE() |
|
|
|
IMPLEMENT_SERVERCLASS_ST(CTFWeaponBuilder, DT_TFWeaponBuilder) |
|
SendPropInt( SENDINFO( m_iBuildState ), 4, SPROP_UNSIGNED ), |
|
SendPropDataTable( "BuilderLocalData", 0, &REFERENCE_SEND_TABLE( DT_BuilderLocalData ), SendProxy_SendLocalWeaponDataTable ), |
|
SendPropInt( SENDINFO( m_iObjectMode ) , 4, SPROP_UNSIGNED ), |
|
SendPropFloat( SENDINFO( m_flWheatleyTalkingUntil) ), |
|
END_SEND_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_builder, CTFWeaponBuilder ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_builder ); |
|
|
|
// |
|
|
|
IMPLEMENT_SERVERCLASS_ST( CTFWeaponSapper, DT_TFWeaponSapper ) |
|
SendPropFloat( SENDINFO( m_flChargeBeginTime ) ), |
|
END_SEND_TABLE() |
|
|
|
LINK_ENTITY_TO_CLASS( tf_weapon_sapper, CTFWeaponSapper ); |
|
PRECACHE_WEAPON_REGISTER( tf_weapon_sapper ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFWeaponBuilder::CTFWeaponBuilder() |
|
{ |
|
m_iObjectType.Set( BUILDER_INVALID_OBJECT ); |
|
m_iObjectMode = 0; |
|
m_bAttack3Down = false; |
|
|
|
//Sapper VO Pack stuff |
|
WheatleyReset( true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFWeaponBuilder::~CTFWeaponBuilder() |
|
{ |
|
StopPlacement(); |
|
if (m_pkvWavList) |
|
{ |
|
m_pkvWavList->deleteThis(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::SetSubType( int iSubType ) |
|
{ |
|
m_iObjectType = iSubType; |
|
|
|
// m_iViewModelIndex is set by the base Precache(), which didn't know what |
|
// type of object we built, so it didn't get the right viewmodel index. |
|
// Now that our data is filled in, go and get the right index. |
|
const char *pszViewModel = GetViewModel(0); |
|
if ( pszViewModel && pszViewModel[0] ) |
|
{ |
|
m_iViewModelIndex = CBaseEntity::PrecacheModel( pszViewModel ); |
|
} |
|
|
|
if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
if ( IsWheatleySapper() ) |
|
{ |
|
if (m_pkvWavList) |
|
{ |
|
m_pkvWavList->deleteThis(); |
|
} |
|
m_pkvWavList = new KeyValues("sappervo"); |
|
} |
|
} |
|
|
|
BaseClass::SetSubType( iSubType ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
|
|
// Precache all the viewmodels we could possibly be building |
|
for ( int iObj=0; iObj < OBJ_LAST; iObj++ ) |
|
{ |
|
const CObjectInfo *pInfo = GetObjectInfo( iObj ); |
|
|
|
if ( pInfo ) |
|
{ |
|
if ( pInfo->m_pViewModel ) |
|
{ |
|
PrecacheModel( pInfo->m_pViewModel ); |
|
} |
|
|
|
if ( pInfo->m_pPlayerModel ) |
|
{ |
|
PrecacheModel( pInfo->m_pPlayerModel ); |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::CanDeploy( void ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); |
|
if (!pPlayer) |
|
return false; |
|
|
|
if ( pPlayer->m_Shared.IsCarryingObject() ) |
|
return BaseClass::CanDeploy(); |
|
|
|
if ( pPlayer->CanBuild( m_iObjectType, m_iObjectMode ) != CB_CAN_BUILD ) |
|
{ |
|
return false; |
|
} |
|
|
|
return BaseClass::CanDeploy(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::Deploy( void ) |
|
{ |
|
bool bDeploy = BaseClass::Deploy(); |
|
|
|
if ( bDeploy ) |
|
{ |
|
SetCurrentState( BS_PLACING ); |
|
StartPlacement(); |
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.35f; |
|
m_flNextSecondaryAttack = gpGlobals->curtime; // asap |
|
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); |
|
if (!pPlayer) |
|
return false; |
|
|
|
pPlayer->SetNextAttack( gpGlobals->curtime ); |
|
|
|
m_iWorldModelIndex = modelinfo->GetModelIndex( GetWorldModel() ); |
|
|
|
m_flNextDenySound = 0; |
|
|
|
// Set off the hint here, because we don't know until now if our building |
|
// is rotate-able or not. |
|
if ( m_hObjectBeingBuilt && !m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() ) |
|
{ |
|
// set the alt-fire hint so it gets removed when we holster |
|
m_iAltFireHint = HINT_ALTFIRE_ROTATE_BUILDING; |
|
pPlayer->StartHintTimer( m_iAltFireHint ); |
|
} |
|
|
|
pPlayer->PlayWearableAnimsForPlaybackEvent( WAP_START_BUILDING ); |
|
} |
|
|
|
return bDeploy; |
|
} |
|
|
|
Activity CTFWeaponBuilder::GetDrawActivity( void ) |
|
{ |
|
// sapper used to call different draw animations , one when invis and one when not. |
|
// now you can go invis *while* deploying, so let's always use the one-handed deploy. |
|
if ( GetType() == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
return ACT_VM_DRAW_DEPLOYED; |
|
} |
|
|
|
return BaseClass::GetDrawActivity(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop placement when holstering |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return false; |
|
|
|
if ( pOwner->m_Shared.IsCarryingObject() ) |
|
return false; |
|
|
|
if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
if( IsWheatleySapper() ) |
|
{ |
|
pOwner->ClearSappingTracking(); |
|
if ( pOwner->m_Shared.GetState() == TF_STATE_DYING) |
|
{ |
|
if ( RandomInt( 0, 4) == 0 ) |
|
{ |
|
WheatleyEmitSound( "PSap.DeathLong", true ); |
|
} |
|
else |
|
{ |
|
WheatleyEmitSound( "PSap.Death", true ); |
|
} |
|
} |
|
else |
|
{ |
|
float flSoundDuration; |
|
if ( gpGlobals->curtime - m_flWheatleyLastDeploy < 1.5 && gpGlobals->curtime - m_flWheatleyLastDeploy > -1.0 ) |
|
{ |
|
flSoundDuration = WheatleyEmitSound( "PSap.HolsterFast"); |
|
} |
|
else |
|
{ |
|
flSoundDuration = WheatleyEmitSound( "PSap.Holster"); |
|
} |
|
m_flWheatleyLastHolster = gpGlobals->curtime + flSoundDuration; |
|
} |
|
} |
|
} |
|
|
|
m_flNextVoicePakIdleStartTime = -1.0f; |
|
|
|
if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID ) |
|
{ |
|
SetCurrentState( BS_IDLE ); |
|
} |
|
StopPlacement(); |
|
|
|
pOwner->PlayWearableAnimsForPlaybackEvent( WAP_STOP_BUILDING ); |
|
|
|
return BaseClass::Holster(pSwitchingTo); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::ItemPostFrame( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
// If we're building, and our team has lost, stop placing the object |
|
if ( m_hObjectBeingBuilt.Get() && |
|
TFGameRules()->State_Get() == GR_STATE_TEAM_WIN && |
|
pOwner->GetTeamNumber() != TFGameRules()->GetWinningTeam() ) |
|
{ |
|
StopPlacement(); |
|
return; |
|
} |
|
|
|
// Check that I still have enough resources to build this item |
|
if ( pOwner->CanBuild( m_iObjectType, m_iObjectMode ) != CB_CAN_BUILD ) |
|
{ |
|
SwitchOwnersWeaponToLast(); |
|
} |
|
|
|
if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) ) |
|
{ |
|
PrimaryAttack(); |
|
} |
|
|
|
if ( pOwner->m_nButtons & IN_ATTACK2 ) |
|
{ |
|
if ( m_flNextSecondaryAttack <= gpGlobals->curtime ) |
|
{ |
|
SecondaryAttack(); |
|
} |
|
} |
|
else |
|
{ |
|
m_bInAttack2 = false; |
|
} |
|
|
|
// Attrib |
|
int iMarkForDeathOnPickup = 0; |
|
if ( pOwner->m_Shared.IsCarryingObject () ) |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iMarkForDeathOnPickup, mark_for_death_on_building_pickup ); |
|
if ( iMarkForDeathOnPickup ) |
|
{ |
|
pOwner->m_Shared.AddCond( TF_COND_MARKEDFORDEATH_SILENT, 3.f ); |
|
} |
|
} |
|
|
|
WeaponIdle(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Start placing or building the currently selected object |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::PrimaryAttack( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
if ( !CanAttack() ) |
|
return; |
|
|
|
// Necessary so that we get the latest building position for the test, otherwise |
|
// we are one frame behind. |
|
UpdatePlacementState(); |
|
|
|
// 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 ) |
|
{ |
|
int iFlags = m_hObjectBeingBuilt->GetObjectFlags(); |
|
|
|
// Tricky, because this can re-calc the object position and change whether its a valid |
|
// pos or not. Best not to do this only in debug, but we can be pretty sure that this |
|
// will give the same result as was calculated in UpdatePlacementState() above. |
|
Assert( IsValidPlacement() ); |
|
|
|
// If we're placing an attachment, like a sapper, play a placement animation on the owner |
|
if ( m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() ) |
|
{ |
|
pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE ); |
|
} |
|
|
|
CBaseEntity *pBuiltOnObject = m_hObjectBeingBuilt->GetBuiltOnObject(); |
|
|
|
if ( pBuiltOnObject && m_iObjectType == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
m_vLastKnownSapPos = pBuiltOnObject->GetAbsOrigin(); |
|
m_hLastSappedBuilding = pBuiltOnObject; |
|
} |
|
StartBuilding(); |
|
|
|
if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
// tell players a sapper was just placed (so bots can react) |
|
CUtlVector< CTFPlayer * > playerVector; |
|
CollectPlayers( &playerVector, TEAM_ANY, COLLECT_ONLY_LIVING_PLAYERS ); |
|
|
|
for( int i=0; i<playerVector.Count(); ++i ) |
|
playerVector[i]->OnSapperPlaced( pBuiltOnObject ); |
|
|
|
// if we just placed a sapper on a teleporter...try to sap the match, too? |
|
if ( pBuiltOnObject ) |
|
{ |
|
CObjectTeleporter *pTeleporter = dynamic_cast<CObjectTeleporter*>( pBuiltOnObject ); |
|
if ( pTeleporter && pTeleporter->GetMatchingTeleporter() && !pTeleporter->GetMatchingTeleporter()->HasSapper() ) |
|
{ |
|
// Start placing another |
|
SetCurrentState( BS_PLACING ); |
|
StartPlacement(); |
|
|
|
if ( m_hObjectBeingBuilt.Get() ) |
|
{ |
|
m_hObjectBeingBuilt->UpdateAttachmentPlacement( pTeleporter->GetMatchingTeleporter() ); |
|
StartBuilding(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Should we switch away? |
|
if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT ) |
|
{ |
|
// Start placing another |
|
SetCurrentState( BS_PLACING ); |
|
StartPlacement(); |
|
} |
|
else |
|
{ |
|
SwitchOwnersWeaponToLast(); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case BS_PLACING_INVALID: |
|
{ |
|
if ( m_flNextDenySound < gpGlobals->curtime ) |
|
{ |
|
CSingleUserRecipientFilter filter( pOwner ); |
|
EmitSound( filter, entindex(), "Player.DenyWeaponSelection" ); |
|
|
|
m_flNextDenySound = gpGlobals->curtime + 0.5; |
|
} |
|
} |
|
break; |
|
} |
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f; |
|
} |
|
|
|
void CTFWeaponBuilder::SecondaryAttack( void ) |
|
{ |
|
if ( m_bInAttack2 ) |
|
return; |
|
|
|
// require a re-press |
|
m_bInAttack2 = true; |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
UpdatePlacementState(); |
|
|
|
if ( !pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) && pOwner->DoClassSpecialSkill() ) |
|
{ |
|
// Spies do the special skill first. |
|
} |
|
else if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID ) |
|
{ |
|
if ( m_hObjectBeingBuilt ) |
|
{ |
|
pOwner->StopHintTimer( HINT_ALTFIRE_ROTATE_BUILDING ); |
|
m_hObjectBeingBuilt->RotateBuildAngles(); |
|
} |
|
} |
|
else if ( pOwner->DoClassSpecialSkill() ) |
|
{ |
|
// Engineers do the special skill last. |
|
} |
|
|
|
m_flNextSecondaryAttack = gpGlobals->curtime + 0.2f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the builder to the specified state |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::SetCurrentState( int iState ) |
|
{ |
|
m_iBuildState = iState; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the owner's weapon and last weapon appropriately when we need to |
|
// switch away from the builder weapon. |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::SwitchOwnersWeaponToLast() |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
// for engineer, switch to wrench and set last weapon appropriately |
|
if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) ) |
|
{ |
|
// Switch to wrench if possible. if not, then best weapon |
|
CBaseCombatWeapon *pWpn = pOwner->Weapon_GetSlot( 2 ); |
|
|
|
// Don't store last weapon when we autoswitch off builder |
|
CBaseCombatWeapon *pLastWpn = pOwner->GetLastWeapon(); |
|
|
|
if ( pWpn ) |
|
{ |
|
pOwner->Weapon_Switch( pWpn ); |
|
} |
|
else |
|
{ |
|
pOwner->SwitchToNextBestWeapon( NULL ); |
|
} |
|
|
|
if ( pWpn == pLastWpn ) |
|
{ |
|
// We had the wrench out before we started building. Go ahead and set out last |
|
// weapon to our primary weapon. |
|
pWpn = pOwner->Weapon_GetSlot( 0 ); |
|
pOwner->Weapon_SetLast( pWpn ); |
|
} |
|
else |
|
{ |
|
pOwner->Weapon_SetLast( pLastWpn ); |
|
} |
|
} |
|
else |
|
{ |
|
// for all other classes, just switch to last weapon used |
|
pOwner->Weapon_Switch( pOwner->GetLastWeapon() ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: updates the building postion and checks the new postion |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::UpdatePlacementState( void ) |
|
{ |
|
// This updates the building position |
|
bool bValidPos = IsValidPlacement(); |
|
|
|
// If we're in placement mode, update the placement model |
|
switch( m_iBuildState ) |
|
{ |
|
case BS_PLACING: |
|
case BS_PLACING_INVALID: |
|
{ |
|
if ( bValidPos ) |
|
{ |
|
SetCurrentState( BS_PLACING ); |
|
} |
|
else |
|
{ |
|
SetCurrentState( BS_PLACING_INVALID ); |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Idle updates the position of the build placement model |
|
//----------------------------------------------------------------------------- |
|
/*#define SAPPER_VOPAK_DEFAULT_WAIT 0.0f*/ |
|
void CTFWeaponBuilder::WeaponIdle( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
WheatleySapperIdle( pOwner ); |
|
|
|
if ( HasWeaponIdleTimeElapsed() ) |
|
{ |
|
SendWeaponAnim( ACT_VM_IDLE ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Special Item Idle |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::IsWheatleySapper( void ) |
|
{ |
|
float flVoicePak = 0.0; |
|
CALL_ATTRIB_HOOK_FLOAT( flVoicePak, sapper_voice_pak ); |
|
return (flVoicePak == 1.0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Special Item Reset |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::WheatleyReset( bool bResetIntro ) |
|
{ |
|
if ( IsWheatleySapper() ) |
|
{ |
|
WheatleyEmitSound( "PSap.null" ); |
|
} |
|
if ( bResetIntro ) |
|
{ |
|
m_bWheatleyIntroPlayed = false; |
|
} |
|
m_flNextVoicePakIdleStartTime = -1.0f; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_flWheatleyTalkingUntil = 0.00; |
|
m_flWheatleyLastDamage = 0.00; |
|
m_iWheatleyVOSequenceOffset = 0; |
|
m_flWheatleyLastDeploy = 0.00; |
|
m_flWheatleyLastHolster = 0.00; |
|
} |
|
|
|
bool CTFWeaponBuilder::IsWheatleyTalking( void ) |
|
{ |
|
return gpGlobals->curtime <= m_flWheatleyTalkingUntil; |
|
} |
|
|
|
float CTFWeaponBuilder::WheatleyEmitSound( const char *snd, bool bEmitToAll /*= false*/, bool bNoRepeats /*= false */ ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
CSoundParameters params; |
|
if ( !soundemitterbase->GetParametersForSound( snd, params, GENDER_NONE ) ) |
|
{ |
|
return 0.00; |
|
} |
|
//Should we check to see if it's already been played? |
|
if ( bNoRepeats && m_pkvWavList ) |
|
{ |
|
if ( m_pkvWavList->GetInt( params.soundname , 0 ) ) |
|
{ |
|
return 0; |
|
} |
|
else |
|
{ |
|
m_pkvWavList->SetInt( params.soundname , 1); |
|
} |
|
} |
|
//Look for special cases that require us to pick the next lines from a sequential list |
|
if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle38.mp3") == NULL ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_HARMLESS ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
} |
|
else if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle41.mp3") == NULL ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_HACK ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
} |
|
else if ( Q_strcmp( params.soundname, "vo/items/wheatley_sapper/wheatley_sapper_idle35.mp3") == NULL ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_SPECIALIDLE_KNIFE ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
} |
|
|
|
//Play the sound |
|
// When playing a sound to all players, do it at the last known sapper location |
|
// This is not played on the object itself (building or sapper) cause it may not exist in the case of death and follow up audio |
|
// Also having it played on this entity itself prevents multiple VO playing in the case of mass sapping |
|
if ( bEmitToAll ) |
|
{ |
|
//int entIndex = 0; |
|
CBroadcastNonOwnerRecipientFilter filter( pOwner ); |
|
EmitSound( filter, entindex(), snd, &m_vLastKnownSapPos ); |
|
} |
|
|
|
// GetSoundDuration is not supported on Linux or for MP3s. So lets just put in a good number |
|
//float flSoundDuration = enginesound->GetSoundDuration( params.soundname ); |
|
float flSoundDuration = 3.0f; |
|
CSingleUserRecipientFilter filter( pOwner ); |
|
EmitSound( filter, entindex(), params ); |
|
m_flWheatleyTalkingUntil = gpGlobals->curtime + flSoundDuration; |
|
return flSoundDuration; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set Wheatley Sapper State |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::SetWheatleyState( int iNewState ) |
|
{ |
|
m_iSapState = iNewState; |
|
} |
|
|
|
int CTFWeaponBuilder::GetWheatleyIdleWait() |
|
{ |
|
return RandomInt( WHEATLEY_IDLE_WAIT_SECS_MIN, WHEATLEY_IDLE_WAIT_SECS_MAX ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set Wheatley Sapper State |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::WheatleyDamage( void ) |
|
{ |
|
if ( (gpGlobals->curtime - m_flWheatleyLastDamage) > 10.0) |
|
{ |
|
if ( RandomInt(0,2) == 0 ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if (pOwner) |
|
{ |
|
pOwner->ClearSappingEvent(); |
|
} |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_flWheatleyLastDamage = gpGlobals->curtime; |
|
WheatleyEmitSound( "PSap.Damage" ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Special Item Idle |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::WheatleySapperIdle( CTFPlayer *pOwner ) |
|
{ |
|
if ( pOwner && m_iObjectType == OBJ_ATTACHMENT_SAPPER && IsWheatleySapper()) |
|
{ |
|
//Is Wheatley coming out of the player's pocket? |
|
if( m_flNextVoicePakIdleStartTime < 0.0f ) |
|
{ |
|
pOwner->ClearSappingTracking(); |
|
float flSoundDuration; |
|
if ( gpGlobals->curtime - m_flWheatleyLastHolster < 2.0 && gpGlobals->curtime - m_flWheatleyLastHolster >= -1.00 ) |
|
{ |
|
flSoundDuration = WheatleyEmitSound( "Psap.DeployAgain" ); |
|
} |
|
else |
|
{ |
|
flSoundDuration = WheatleyEmitSound( (m_bWheatleyIntroPlayed) ? "Psap.Deploy" : "Psap.DeployIntro" ); |
|
} |
|
m_flWheatleyLastDeploy = gpGlobals->curtime + flSoundDuration; |
|
if ( (!m_bWheatleyIntroPlayed) && (RandomInt(0,2) == 0) ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_INTRO ); |
|
m_bWheatleyIntroPlayed = true; |
|
m_iWheatleyVOSequenceOffset = 0; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + flSoundDuration + 3.0; |
|
} |
|
else |
|
{ |
|
m_bWheatleyIntroPlayed = true; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + flSoundDuration + GetWheatleyIdleWait(); |
|
} |
|
} |
|
//Is there a sapper event? (sapper placed / sapper finished) |
|
else if( pOwner->GetSappingEvent() != TF_SAPEVENT_NONE) |
|
{ |
|
char *pVoicePakString = NULL; |
|
switch ( pOwner->GetSappingEvent() ) |
|
{ |
|
case TF_SAPEVENT_PLACED: |
|
if (RandomInt(0,1) == 0) |
|
{ |
|
if ( RandomInt(0,3) == 0 ) |
|
{ |
|
pVoicePakString = "PSap.AttachedPW"; |
|
SetWheatleyState( TF_PSAPSTATE_WAITINGHACK ); |
|
} |
|
else |
|
{ |
|
pVoicePakString = "PSap.Attached"; |
|
SetWheatleyState( TF_PSAPSTATE_WAITINGHACKPW ); |
|
} |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.2; |
|
} |
|
else |
|
{ |
|
pVoicePakString = "PSap.Hacking"; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
break; |
|
case TF_SAPEVENT_DONE: |
|
if ( IsWheatleyTalking() ) |
|
{ |
|
if ( m_hLastSappedBuilding && m_hLastSappedBuilding.Get() ) |
|
{ |
|
//Building Alive, Sapper died |
|
pVoicePakString = "PSap.Death"; |
|
} |
|
else |
|
{ |
|
pVoicePakString = "PSap.HackedLoud"; |
|
} |
|
|
|
if ( RandomInt( 0, 3 ) == 0 ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_WAITINGFOLLOWUP); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.3; |
|
} |
|
else |
|
{ |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
} |
|
else |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_WAITINGHACKED ); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.5; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
pOwner->ClearSappingEvent(); |
|
if ( pVoicePakString ) |
|
{ |
|
float flSoundDuration = WheatleyEmitSound( pVoicePakString, true ); |
|
m_flNextVoicePakIdleStartTime += flSoundDuration; |
|
} |
|
} |
|
//Are we in the intro sequence? |
|
else if ( m_iSapState == TF_PSAPSTATE_INTRO && gpGlobals->curtime > m_flNextVoicePakIdleStartTime ) |
|
{ |
|
if ( !IsWheatleyTalking() ) |
|
{ |
|
char szVoicePakString[128]; |
|
szVoicePakString[0] = '\0'; |
|
if ( m_iWheatleyVOSequenceOffset >= 0 && m_iWheatleyVOSequenceOffset <=3 ) |
|
{ |
|
V_sprintf_safe( szVoicePakString, "PSap.IdleIntro0%i", ++m_iWheatleyVOSequenceOffset); |
|
float flSoundDuration = WheatleyEmitSound( szVoicePakString ); |
|
if ( m_iWheatleyVOSequenceOffset == 4 ) |
|
{ |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait() + flSoundDuration; |
|
} |
|
else |
|
{ |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.0 + flSoundDuration; |
|
} |
|
} |
|
else |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
} |
|
} |
|
} |
|
//Does a generic timed event need to be serviced? |
|
else if( gpGlobals->curtime > m_flNextVoicePakIdleStartTime ) |
|
{ |
|
bool bNoRepeats = false; |
|
bool bEmitAll = false; |
|
char *pVoicePakString = NULL; |
|
//Sapped! vo |
|
if ( m_iSapState == TF_PSAPSTATE_WAITINGHACKED ) |
|
{ |
|
bEmitAll = true; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
if ( IsWheatleyTalking() ) |
|
{ |
|
pVoicePakString = "PSap.HackedLoud"; |
|
} |
|
else |
|
{ |
|
pVoicePakString = "PSap.Hacked"; |
|
} |
|
if ( RandomInt( 0, 3 ) == 0 ) |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_WAITINGFOLLOWUP); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 1.3; |
|
} |
|
else |
|
{ |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
} |
|
//Waiting to start the password guessing vo |
|
else if ( m_iSapState == TF_PSAPSTATE_WAITINGHACKPW ) |
|
{ |
|
bEmitAll = true; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
pVoicePakString = "PSap.HackingPW"; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
//Waiting to start regular hacking vo |
|
else if ( m_iSapState == TF_PSAPSTATE_WAITINGHACK ) |
|
{ |
|
bEmitAll = true; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
if ( RandomInt( 0, 2 ) == 0 ) |
|
{ |
|
pVoicePakString = "PSap.HackingShort"; |
|
} |
|
else |
|
{ |
|
pVoicePakString = "PSap.Hacking"; |
|
} |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
//Waiting to start successful hack followup vo |
|
else if ( m_iSapState == TF_PSAPSTATE_WAITINGFOLLOWUP ) |
|
{ |
|
bEmitAll = true; |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
pVoicePakString = "PSap.HackedFollowup"; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
//If Wheatley's talking, skip & check again later |
|
else if ( IsWheatleyTalking() ) |
|
{ |
|
pVoicePakString = NULL; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 5.0; |
|
} |
|
//Are we in the SPECIAL IDLE SEQUENCE "HACK"? |
|
else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_HACK ) |
|
{ |
|
switch ( m_iWheatleyVOSequenceOffset ) |
|
{ |
|
case 0: |
|
pVoicePakString = "PSap.IdleHack02"; |
|
m_iWheatleyVOSequenceOffset++; |
|
break; |
|
default: |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
break; |
|
} |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
//Are we in the SPECIAL IDLE SEQUENCE "KNIFE"? |
|
else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_KNIFE ) |
|
{ |
|
switch ( m_iWheatleyVOSequenceOffset ) |
|
{ |
|
case 0: |
|
pVoicePakString = "PSap.IdleKnife02"; |
|
m_iWheatleyVOSequenceOffset++; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + 0.3; |
|
break; |
|
case 1: |
|
pVoicePakString = "PSap.IdleKnife03"; |
|
m_iWheatleyVOSequenceOffset++; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
break; |
|
default: |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
break; |
|
} |
|
} |
|
//Are we in the SPECIAL IDLE SEQUENCE "HARMLESS"? |
|
else if ( m_iSapState == TF_PSAPSTATE_SPECIALIDLE_HARMLESS ) |
|
{ |
|
switch ( m_iWheatleyVOSequenceOffset ) |
|
{ |
|
case 0: |
|
pVoicePakString = "PSap.IdleHarmless02"; |
|
m_iWheatleyVOSequenceOffset++; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
break; |
|
default: |
|
SetWheatleyState( TF_PSAPSTATE_IDLE ); |
|
m_iWheatleyVOSequenceOffset = 0; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
break; |
|
} |
|
} |
|
//Is the player stealthed? |
|
else if ( pOwner->m_Shared.IsStealthed() ) |
|
{ |
|
if ( RandomInt(0,1) == 0 ) |
|
{ |
|
pVoicePakString = "PSap.Sneak"; |
|
} |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
else if ( m_iSapState == TF_PSAPSTATE_IDLE ) |
|
{ |
|
pVoicePakString = "PSap.Idle"; |
|
bNoRepeats = true; |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
} |
|
else |
|
{ |
|
pVoicePakString = NULL; |
|
} |
|
if (!pVoicePakString) |
|
{ |
|
m_flNextVoicePakIdleStartTime = gpGlobals->curtime + GetWheatleyIdleWait(); |
|
return; |
|
} |
|
float flSoundDuration = WheatleyEmitSound( pVoicePakString, bEmitAll, bNoRepeats ); |
|
m_flNextVoicePakIdleStartTime += flSoundDuration; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Start placing the object |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::StartPlacement( void ) |
|
{ |
|
StopPlacement(); |
|
|
|
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwner() ); |
|
if ( !pTFPlayer ) |
|
return; |
|
|
|
if ( pTFPlayer->m_Shared.IsCarryingObject() ) |
|
{ |
|
m_hObjectBeingBuilt = pTFPlayer->m_Shared.GetCarriedObject(); |
|
m_hObjectBeingBuilt->StopFollowingEntity(); |
|
} |
|
else |
|
{ |
|
m_hObjectBeingBuilt = (CBaseObject*)CreateEntityByName( GetObjectInfo( m_iObjectType )->m_pClassName ); |
|
} |
|
|
|
if ( m_hObjectBeingBuilt ) |
|
{ |
|
// Set the builder before Spawn() so attributes can hook correctly |
|
m_hObjectBeingBuilt->SetBuilder( pTFPlayer ); |
|
|
|
bool bIsCarried = m_hObjectBeingBuilt->IsCarried(); |
|
|
|
// split this off from the block at the bottom because we need to know what type of building |
|
// this is before we spawn so things like the teleporters have the correct placement models |
|
// but we need to set the starting construction health after we've called spawn |
|
if ( !bIsCarried ) |
|
{ |
|
m_hObjectBeingBuilt->SetObjectMode( m_iObjectMode ); |
|
} |
|
|
|
m_hObjectBeingBuilt->Spawn(); |
|
m_hObjectBeingBuilt->StartPlacement( pTFPlayer ); |
|
|
|
if ( !bIsCarried ) |
|
{ |
|
m_hObjectBeingBuilt->m_iHealth = OBJECT_CONSTRUCTION_STARTINGHEALTH; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::StopPlacement( void ) |
|
{ |
|
if ( m_hObjectBeingBuilt ) |
|
{ |
|
if ( m_hObjectBeingBuilt->IsCarried() ) |
|
{ |
|
m_hObjectBeingBuilt->MakeCarriedObject( ToTFPlayer( GetOwner() ) ); |
|
} |
|
else |
|
{ |
|
m_hObjectBeingBuilt->StopPlacement(); |
|
} |
|
m_hObjectBeingBuilt = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::WeaponReset( void ) |
|
{ |
|
//Check to see if the active weapon is the wheatley sapper, and, if so, reset him |
|
if ( m_iObjectType == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
if ( IsWheatleySapper() ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
pPlayer->ClearSappingTracking(); |
|
} |
|
WheatleyReset(); |
|
} |
|
} |
|
|
|
BaseClass::WeaponReset(); |
|
|
|
StopPlacement(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Move the placement model to the current position. Return false if it's an invalid position |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::IsValidPlacement( void ) |
|
{ |
|
if ( !m_hObjectBeingBuilt ) |
|
return false; |
|
|
|
CBaseObject *pObj = m_hObjectBeingBuilt.Get(); |
|
|
|
pObj->UpdatePlacement(); |
|
|
|
return m_hObjectBeingBuilt->IsValidPlacement(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Player holding this weapon has started building something |
|
// Assumes we are in a valid build position |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::StartBuilding( void ) |
|
{ |
|
CBaseObject *pObj = m_hObjectBeingBuilt.Get(); |
|
|
|
Assert( pObj ); |
|
|
|
pObj->StartBuilding( GetOwner() ); |
|
|
|
m_hObjectBeingBuilt = NULL; |
|
|
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( pOwner ) |
|
{ |
|
pOwner->RemoveInvisibility(); |
|
pOwner->m_Shared.SetCarriedObject( NULL ); |
|
|
|
if ( TFGameRules() && TFGameRules()->GameModeUsesUpgrades() ) |
|
{ |
|
if ( pObj->ObjectType() == OBJ_ATTACHMENT_SAPPER ) |
|
{ |
|
// Let human players place player-targeted sappers in modes that allow upgrades |
|
if ( !pOwner->IsBot() && pObj->GetBuiltOnObject() && pObj->GetBuiltOnObject()->IsPlayer() ) |
|
{ |
|
int iRoboSapper = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRoboSapper, robo_sapper ); |
|
|
|
int nMode = iRoboSapper ? MODE_SAPPER_ANTI_ROBOT_RADIUS : MODE_SAPPER_ANTI_ROBOT; |
|
pObj->SetObjectMode( nMode ); |
|
|
|
pOwner->RemoveAmmo( 1, TF_AMMO_GRENADES2 ); |
|
StartEffectBarRegen(); |
|
} |
|
} |
|
#ifdef STAGING_ONLY |
|
// Traps use TF_AMMO_GRENADES1 |
|
else if ( pObj->GetType() == OBJ_SPY_TRAP ) |
|
{ |
|
pOwner->RemoveAmmo( 1, TF_AMMO_GRENADES1 ); |
|
} |
|
#endif // STAGING_ONLY |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if this weapon has some ammo |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::HasAmmo( void ) |
|
{ |
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return false; |
|
|
|
int iCost = pOwner->m_Shared.CalculateObjectCost( pOwner, m_iObjectType ); |
|
return ( pOwner->GetBuildResources() >= iCost ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWeaponBuilder::GetSlot( void ) const |
|
{ |
|
return GetObjectInfo( m_iObjectType )->m_SelectionSlot; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWeaponBuilder::GetPosition( void ) const |
|
{ |
|
return GetObjectInfo( m_iObjectType )->m_SelectionPosition; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : char const |
|
//----------------------------------------------------------------------------- |
|
const char *CTFWeaponBuilder::GetPrintName( void ) const |
|
{ |
|
return GetObjectInfo( m_iObjectType )->m_pStatusName; |
|
} |
|
|
|
// ----------------------------------------------------------------------------- |
|
// Purpose: |
|
// ----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::CanBuildObjectType( int iObjectType ) |
|
{ |
|
if ( iObjectType < 0 || iObjectType >= OBJ_LAST ) |
|
return false; |
|
|
|
return m_aBuildableObjectTypes.Get( iObjectType ); |
|
} |
|
|
|
// ----------------------------------------------------------------------------- |
|
// Purpose: |
|
// ----------------------------------------------------------------------------- |
|
void CTFWeaponBuilder::SetObjectTypeAsBuildable( int iObjectType ) |
|
{ |
|
if ( iObjectType < 0 || iObjectType >= OBJ_LAST ) |
|
return; |
|
|
|
m_aBuildableObjectTypes.Set( iObjectType, true ); |
|
SetSubType( iObjectType ); |
|
} |
|
|
|
// ----------------------------------------------------------------------------- |
|
// Purpose: |
|
// ----------------------------------------------------------------------------- |
|
Activity CTFWeaponBuilder::TranslateViewmodelHandActivity( Activity actBase ) |
|
{ |
|
if ( GetObjectInfo( m_iObjectType )->m_bUseItemInfo ) |
|
{ |
|
return BaseClass::TranslateViewmodelHandActivity( actBase ); |
|
} |
|
else |
|
{ |
|
return actBase; |
|
} |
|
} |
|
|
|
// ----------------------------------------------------------------------------- |
|
// Purpose: |
|
// ----------------------------------------------------------------------------- |
|
const char *CTFWeaponBuilder::GetViewModel( int iViewModel ) const |
|
{ |
|
if ( m_iObjectType != BUILDER_INVALID_OBJECT ) |
|
{ |
|
if ( GetObjectInfo( m_iObjectType )->m_bUseItemInfo ) |
|
return BaseClass::GetViewModel(); |
|
|
|
return GetObjectInfo( m_iObjectType )->m_pViewModel; |
|
} |
|
|
|
return BaseClass::GetViewModel(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CTFWeaponBuilder::GetWorldModel( void ) const |
|
{ |
|
if ( m_iObjectType != BUILDER_INVALID_OBJECT ) |
|
{ |
|
return GetObjectInfo( m_iObjectType )->m_pPlayerModel; |
|
} |
|
|
|
return BaseClass::GetWorldModel(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFWeaponBuilder::AllowsAutoSwitchTo( void ) const |
|
{ |
|
// ask the object we're building |
|
return GetObjectInfo( m_iObjectType )->m_bAutoSwitchTo; |
|
} |
|
|
|
// **************************************************************************** |
|
// SAPPER |
|
// **************************************************************************** |
|
CTFWeaponSapper::CTFWeaponSapper() |
|
{ |
|
m_flChargeBeginTime = 0; |
|
m_bAttackDown = false; |
|
} |
|
//----------------------------------------------------------------------------- |
|
void CTFWeaponSapper::ItemPostFrame( void ) |
|
{ |
|
#ifdef STAGING_ONLY |
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
float flSapperDeployTime = 0; |
|
CALL_ATTRIB_HOOK_FLOAT( flSapperDeployTime, sapper_deploy_time ); |
|
if ( flSapperDeployTime ) |
|
{ |
|
//IsValidPlacement |
|
if ( !( pPlayer->m_nButtons & IN_ATTACK ) || !IsValidPlacement() ) |
|
{ |
|
m_bAttackDown = false; |
|
m_flChargeBeginTime = 0; |
|
} |
|
else if ( m_bAttackDown == false && ( pPlayer->m_nButtons & IN_ATTACK ) ) |
|
{ |
|
m_bAttackDown = true; |
|
m_flChargeBeginTime = gpGlobals->curtime; |
|
} |
|
|
|
if ( ( m_bAttackDown == true && m_flChargeBeginTime + flSapperDeployTime < gpGlobals->curtime ) || !( pPlayer->m_nButtons & IN_ATTACK ) ) |
|
{ |
|
BaseClass::ItemPostFrame(); |
|
} |
|
return; |
|
} |
|
} |
|
#endif // STAGING_ONLY |
|
BaseClass::ItemPostFrame(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char *CTFWeaponSapper::GetViewModel( int iViewModel ) const |
|
{ |
|
// Skip over Builder's version |
|
return CTFWeaponBase::GetViewModel(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
const char *CTFWeaponSapper::GetWorldModel( void ) const |
|
{ |
|
// Skip over Builder's version |
|
return CTFWeaponBase::GetWorldModel(); |
|
} |
|
//----------------------------------------------------------------------------- |
|
Activity CTFWeaponSapper::TranslateViewmodelHandActivity( Activity actBase ) |
|
{ |
|
return BaseClass::TranslateViewmodelHandActivity( actBase ); |
|
|
|
// Skip over Builder's version |
|
//return CTFWeaponBase::TranslateViewmodelHandActivity( actBase ); |
|
} |
|
|
|
|