Skip to content

Commit 0d557e6

Browse files
seanmcgeehanEricS-Valve
authored andcommitted
Fix most broken spy disguise weapons.
1 parent facc907 commit 0d557e6

5 files changed

Lines changed: 176 additions & 18 deletions

File tree

src/game/shared/tf/tf_item_wearable.cpp

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,32 @@ void CTFWearable::ValidateModelIndex( void )
516516

517517
#endif
518518

519+
//-----------------------------------------------------------------------------
520+
// Purpose: Helper to apply bodygroups from an item to a disguise target.
521+
//-----------------------------------------------------------------------------
522+
void CTFWearable::UpdateDisguiseBodygroups( CTFPlayer *pTFOwner, CTFPlayer *pDisguiseTarget, CEconItemView *pItem, int iTeam, int iState )
523+
{
524+
if ( !pTFOwner || !pDisguiseTarget || !pItem )
525+
return;
526+
527+
int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
528+
int iNumBodyGroups = pItem->GetStaticData()->GetNumModifiedBodyGroups( iTeam );
529+
530+
for ( int i = 0; i < iNumBodyGroups; ++i )
531+
{
532+
int iBody = 0;
533+
const char *pszBodyGroup = pItem->GetStaticData()->GetModifiedBodyGroup( iTeam, i, iBody );
534+
int iBodyGroup = pDisguiseTarget->FindBodygroupByName( pszBodyGroup );
535+
536+
if ( iBodyGroup == -1 )
537+
continue;
538+
539+
::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState );
540+
}
541+
542+
pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody );
543+
}
544+
519545
//-----------------------------------------------------------------------------
520546
// Purpose: Hides or shows masked bodygroups associated with this item.
521547
//-----------------------------------------------------------------------------
@@ -529,28 +555,30 @@ bool CTFWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
529555
if ( bBaseUpdate && m_bDisguiseWearable )
530556
{
531557
CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call.
532-
533558
CTFPlayer *pDisguiseTarget = pTFOwner->m_Shared.GetDisguiseTarget();
534559
if ( !pDisguiseTarget )
535560
return false;
536561

537-
// Update our disguise bodygroup.
538-
int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
539562
int iTeam = pTFOwner->m_Shared.GetDisguiseTeam();
540-
int iNumBodyGroups = pItem->GetStaticData()->GetNumModifiedBodyGroups( iTeam );
541-
for ( int i=0; i<iNumBodyGroups; ++i )
542-
{
543-
int iBody = 0;
544-
const char *pszBodyGroup = pItem->GetStaticData()->GetModifiedBodyGroup( iTeam, i, iBody );
545-
int iBodyGroup = pDisguiseTarget->FindBodygroupByName( pszBodyGroup );
563+
UpdateDisguiseBodygroups( pTFOwner, pDisguiseTarget, pItem, iTeam, iState );
564+
}
546565

547-
if ( iBodyGroup == -1 )
548-
continue;
566+
// CEconEntity::UpdateBodygroups is broken for disguise weapons and wearables.
567+
// Additionally it is missing most of the infrastructure needed to set up this function there.
568+
// As such, we set up the disguise weapon along with the other wearables since this is the only place
569+
// they are actually handled correctly.
570+
CTFWeaponBase *pDisguiseWeapon = pTFOwner->m_Shared.GetDisguiseWeapon();
571+
if ( pDisguiseWeapon )
572+
{
573+
CAttributeContainer *pCont = pDisguiseWeapon->GetAttributeContainer();
574+
CEconItemView *pItem = pCont ? pCont->GetItem() : NULL;
575+
CTFPlayer *pDisguiseTarget = pTFOwner->m_Shared.GetDisguiseTarget();
549576

550-
::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState );
577+
if ( pItem && pDisguiseTarget )
578+
{
579+
// We must use team 0 for disguise weapons.
580+
UpdateDisguiseBodygroups( pTFOwner, pDisguiseTarget, pItem, 0, iState );
551581
}
552-
553-
pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody );
554582
}
555583

556584
CEconItemView *pItem = GetAttributeContainer() ? GetAttributeContainer()->GetItem() : NULL;

src/game/shared/tf/tf_item_wearable.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#if defined( CLIENT_DLL )
1919
#define CTFWearable C_TFWearable
2020
#define CTFWearableVM C_TFWearableVM
21+
class C_TFPlayer;
22+
#define CTFPlayer C_TFPlayer
23+
#else
24+
class CTFPlayer;
2125
#endif
2226

2327

@@ -74,8 +78,8 @@ class CTFWearable : public CEconWearable, public IHasGenericMeter
7478
protected:
7579
virtual void InternalSetPlayerDisplayModel( void );
7680

77-
7881
private:
82+
void UpdateDisguiseBodygroups( CTFPlayer *pTFOwner, CTFPlayer *pDisguiseTarget, CEconItemView *pItem, int iTeam, int iState );
7983
CNetworkVar( bool, m_bDisguiseWearable );
8084
CNetworkHandle( CBaseEntity, m_hWeaponAssociatedWith );
8185

src/game/shared/tf/tf_player_shared.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,7 @@ void CTFPlayerShared::OnDataChanged( void )
14791479
{
14801480
m_hDisguiseWeapon->UpdateVisibility();
14811481
m_hDisguiseWeapon->UpdateParticleSystems();
1482+
m_hDisguiseWeapon->UpdateAttachmentModels();
14821483
}
14831484

14841485
// XXX(JohnS): This is not the right place to do these things, SetWeaponVisible on the *client* is just stomping
@@ -7229,6 +7230,8 @@ void CTFPlayerShared::OnRemoveDisguising( void )
72297230
void CTFPlayerShared::OnRemoveDisguised( void )
72307231
{
72317232
#ifdef CLIENT_DLL
7233+
// Save the disguise target before clearing it, so we can mark bodygroups dirty.
7234+
CTFPlayer *pOldDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
72327235

72337236
if ( m_pOuter->GetPredictable() && ( !prediction->IsFirstTimePredicted() || m_bSyncingConditions ) )
72347237
return;
@@ -7253,7 +7256,14 @@ void CTFPlayerShared::OnRemoveDisguised( void )
72537256
UpdateCritBoostEffect( kCritBoost_ForceRefresh );
72547257
m_pOuter->UpdateSpyStateChange();
72557258

7259+
// Mark the old disguise target's bodygroups as dirty so they'll be recalculated.
7260+
if ( pOldDisguiseTarget )
7261+
{
7262+
pOldDisguiseTarget->SetBodygroupsDirty();
7263+
}
7264+
72567265
#else
7266+
72577267
m_nDisguiseTeam = TF_SPY_UNDEFINED;
72587268
m_nDisguiseClass.Set( TF_CLASS_UNDEFINED );
72597269
m_nDisguiseSkinOverride = 0;
@@ -8223,6 +8233,15 @@ void CTFPlayerShared::Disguise( int nTeam, int nClass, CTFPlayer* pDesiredTarget
82238233
}
82248234
}
82258235

8236+
#ifdef CLIENT_DLL
8237+
// Save the old disguise target before changing disguise, so we can clean up bodygroups.
8238+
CTFPlayer *pOldDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
8239+
if ( pOldDisguiseTarget )
8240+
{
8241+
pOldDisguiseTarget->SetBodygroupsDirty();
8242+
}
8243+
#endif
8244+
82268245
m_hDesiredDisguiseTarget.Set( pDesiredTarget );
82278246
m_nDesiredDisguiseClass = nClass;
82288247
m_nDesiredDisguiseTeam = nTeam;
@@ -8402,6 +8421,7 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
84028421
{
84038422
CTFWeaponBase *pLastDisguiseWeapon = m_hDisguiseWeapon;
84048423
CTFWeaponBase *pFirstValidWeapon = NULL;
8424+
84058425
// Cycle through the target's weapons and see if we have a match.
84068426
// Note that it's possible the disguise target doesn't have a weapon in the slot we want,
84078427
// for example if they have replaced it with an unlockable that isn't a weapon (wearable).
@@ -8505,6 +8525,7 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
85058525
m_hDisguiseWeapon->m_bDisguiseWeapon = true;
85068526
m_hDisguiseWeapon->SetContextThink( &CTFWeaponBase::DisguiseWeaponThink, gpGlobals->curtime + 0.5, "DisguiseWeaponThink" );
85078527

8528+
m_hDisguiseWeapon->UpdateExtraWearables();
85088529

85098530
// Ammo/clip state is displayed to attached medics
85108531
m_iDisguiseAmmo = 0;
@@ -8539,14 +8560,31 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
85398560
void CTFPlayerShared::DetermineDisguiseWearables()
85408561
{
85418562
CTFPlayer *pDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
8542-
if ( !pDisguiseTarget )
8543-
return;
85448563

85458564
// Remove any existing disguise wearables.
85468565
RemoveDisguiseWearables();
85478566

8567+
if ( !pDisguiseTarget )
8568+
{
8569+
// No target exists, reset disguise body to default state.
8570+
SetDisguiseBody( 0 );
8571+
return;
8572+
}
8573+
85488574
if ( GetDisguiseClass() != pDisguiseTarget->GetPlayerClass()->GetClassIndex() )
8575+
{
8576+
// Class mismatch, reset disguise body to default.
8577+
SetDisguiseBody( 0 );
8578+
#ifdef CLIENT_DLL
8579+
// Mark bodygroups dirty even when not copying wearables (class mismatch).
8580+
pDisguiseTarget->SetBodygroupsDirty();
8581+
#endif
85498582
return;
8583+
}
8584+
8585+
// Reset disguise body to default before applying new wearables.
8586+
// This ensures old bodygroup modifications don't carry over.
8587+
SetDisguiseBody( 0 );
85508588

85518589
// Equip us with copies of our disguise target's wearables.
85528590
int iPlayerSkinOverride = 0;
@@ -8592,6 +8630,11 @@ void CTFPlayerShared::DetermineDisguiseWearables()
85928630
}
85938631

85948632
m_nDisguiseSkinOverride = iPlayerSkinOverride;
8633+
8634+
#ifdef CLIENT_DLL
8635+
// Mark bodygroups dirty after creating disguise wearables.
8636+
pDisguiseTarget->SetBodygroupsDirty();
8637+
#endif
85958638
}
85968639

85978640
void CTFPlayerShared::RemoveDisguiseWearables()

src/game/shared/tf/tf_weaponbase.cpp

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ void CTFWeaponBase::UpdateExtraWearables()
982982
// Precaching may be needed here, because we allow virtually everything to be loaded on demand now.
983983
pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableViewModel() );
984984
}
985-
985+
pExtraWearableItem->SetDisguiseWearable(m_bDisguiseWeapon);
986986
pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
987987
pExtraWearableItem->SetAlwaysAllow( true );
988988
DispatchSpawn( pExtraWearableItem );
@@ -1008,6 +1008,7 @@ void CTFWeaponBase::UpdateExtraWearables()
10081008
pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableModel() );
10091009
}
10101010

1011+
pExtraWearableItem->SetDisguiseWearable(m_bDisguiseWeapon);
10111012
pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
10121013
pExtraWearableItem->SetAlwaysAllow( true );
10131014
DispatchSpawn( pExtraWearableItem );
@@ -3300,6 +3301,87 @@ bool CTFWeaponBase::OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
33003301
return BaseClass::OnInternalDrawModel( pInfo );
33013302
}
33023303

3304+
//-----------------------------------------------------------------------------
3305+
// Purpose: Override for disguise weapons to use team 0 for attachment lookups
3306+
//-----------------------------------------------------------------------------
3307+
void CTFWeaponBase::UpdateAttachmentModels( void )
3308+
{
3309+
#ifdef CLIENT_DLL
3310+
// For disguise weapons, we need to use the disguise target's team when fetching attachment models
3311+
// because GetTeamNumber() returns the spy's team, not the disguised target's team.
3312+
if ( m_bDisguiseWeapon )
3313+
{
3314+
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
3315+
if ( !pOwner )
3316+
{
3317+
BaseClass::UpdateAttachmentModels();
3318+
return;
3319+
}
3320+
3321+
C_TFPlayer *pDisguiseTarget = pOwner->m_Shared.GetDisguiseTarget();
3322+
int iTeamNumber = pDisguiseTarget ? pDisguiseTarget->GetTeamNumber() : 0;
3323+
3324+
CEconItemView *pItem = GetAttributeContainer()->GetItem();
3325+
GameItemDefinition_t *pItemDef = pItem && pItem->IsValid() ? pItem->GetStaticData() : NULL;
3326+
3327+
// Update the state of additional model attachments
3328+
m_vecAttachedModels.Purge();
3329+
if ( pItemDef && AttachmentModelsShouldBeVisible() )
3330+
{
3331+
{
3332+
int iAttachedModels = pItemDef->GetNumAttachedModels( iTeamNumber );
3333+
for ( int i = 0; i < iAttachedModels; i++ )
3334+
{
3335+
attachedmodel_t *pModel = pItemDef->GetAttachedModelData( iTeamNumber, i );
3336+
3337+
int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
3338+
if ( iModelIndex >= 0 )
3339+
{
3340+
AttachedModelData_t attachedModelData;
3341+
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
3342+
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
3343+
m_vecAttachedModels.AddToTail( attachedModelData );
3344+
}
3345+
}
3346+
}
3347+
3348+
// Check for Festive attachedmodels for festivized weapons
3349+
{
3350+
int iAttachedModels = pItemDef->GetNumAttachedModelsFestivized( iTeamNumber );
3351+
if ( iAttachedModels )
3352+
{
3353+
int iFestivized = 0;
3354+
CALL_ATTRIB_HOOK_INT( iFestivized, is_festivized );
3355+
if ( iFestivized )
3356+
{
3357+
for ( int i = 0; i < iAttachedModels; i++ )
3358+
{
3359+
attachedmodel_t *pModel = pItemDef->GetAttachedModelDataFestivized( iTeamNumber, i );
3360+
3361+
int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
3362+
if ( iModelIndex >= 0 )
3363+
{
3364+
AttachedModelData_t attachedModelData;
3365+
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
3366+
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
3367+
m_vecAttachedModels.AddToTail( attachedModelData );
3368+
}
3369+
}
3370+
}
3371+
}
3372+
}
3373+
}
3374+
// Note: We skip the viewmodel attachment section (ShouldAttachToHands) because
3375+
// disguise weapons are world models only and don't need viewmodel attachments.
3376+
}
3377+
else
3378+
{
3379+
// Normal weapons use the base class implementation
3380+
BaseClass::UpdateAttachmentModels();
3381+
}
3382+
#endif
3383+
}
3384+
33033385
void CTFWeaponBase::ProcessMuzzleFlashEvent( void )
33043386
{
33053387
C_BaseAnimating *pAttachEnt = GetAppropriateWorldOrViewModel();

src/game/shared/tf/tf_weaponbase.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ class CTFWeaponBase : public CBaseCombatWeapon, public IHasOwner, public IHasGen
586586
virtual float CalcViewmodelBob( void );
587587
BobState_t *GetBobState();
588588
virtual bool AttachmentModelsShouldBeVisible( void ) OVERRIDE { return (m_iState == WEAPON_IS_ACTIVE) && !IsBeingRepurposedForTaunt(); }
589+
virtual void UpdateAttachmentModels( void ) OVERRIDE;
589590

590591
virtual bool ShouldEjectBrass() { return true; }
591592

0 commit comments

Comments
 (0)