Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/game/shared/tf/tf_gamerules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5913,7 +5913,7 @@ void CTFGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecS
//-----------------------------------------------------------------------------
bool CTFGameRules::ApplyOnDamageModifyRules( CTakeDamageInfo &info, CBaseEntity *pVictimBaseEntity, bool bAllowDamage )
{
info.SetDamageForForceCalc( info.GetDamage() );
if (!info.GetDamageForForceCalc()) info.SetDamageForForceCalc( info.GetDamage() );
bool bDebug = tf_debug_damage.GetBool();

CTFPlayer *pVictim = ToTFPlayer( pVictimBaseEntity );
Expand Down
70 changes: 56 additions & 14 deletions src/game/shared/tf/tf_item_wearable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "tf_gamerules.h"
#include "animation.h"
#include "basecombatweapon_shared.h"
#include "tf_weapon_mechanical_arm.h"
#ifdef CLIENT_DLL
#include "c_tf_player.h"
#include "model_types.h"
Expand Down Expand Up @@ -516,6 +517,32 @@ void CTFWearable::ValidateModelIndex( void )

#endif

//-----------------------------------------------------------------------------
// Purpose: Helper to apply bodygroups from an item to a disguise target.
//-----------------------------------------------------------------------------
void CTFWearable::UpdateDisguiseBodygroups( CTFPlayer *pTFOwner, CTFPlayer *pDisguiseTarget, CEconItemView *pItem, int iTeam, int iState )
{
if ( !pTFOwner || !pDisguiseTarget || !pItem )
return;

int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
int iNumBodyGroups = pItem->GetStaticData()->GetNumModifiedBodyGroups( iTeam );

for ( int i = 0; i < iNumBodyGroups; ++i )
{
int iBody = 0;
const char *pszBodyGroup = pItem->GetStaticData()->GetModifiedBodyGroup( iTeam, i, iBody );
int iBodyGroup = pDisguiseTarget->FindBodygroupByName( pszBodyGroup );

if ( iBodyGroup == -1 )
continue;

::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState );
}

pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody );
}

//-----------------------------------------------------------------------------
// Purpose: Hides or shows masked bodygroups associated with this item.
//-----------------------------------------------------------------------------
Expand All @@ -529,28 +556,43 @@ bool CTFWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
if ( bBaseUpdate && m_bDisguiseWearable )
{
CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call.

CTFPlayer *pDisguiseTarget = pTFOwner->m_Shared.GetDisguiseTarget();
if ( !pDisguiseTarget )
return false;

// Update our disguise bodygroup.
int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
int iTeam = pTFOwner->m_Shared.GetDisguiseTeam();
int iNumBodyGroups = pItem->GetStaticData()->GetNumModifiedBodyGroups( iTeam );
for ( int i=0; i<iNumBodyGroups; ++i )
{
int iBody = 0;
const char *pszBodyGroup = pItem->GetStaticData()->GetModifiedBodyGroup( iTeam, i, iBody );
int iBodyGroup = pDisguiseTarget->FindBodygroupByName( pszBodyGroup );
UpdateDisguiseBodygroups( pTFOwner, pDisguiseTarget, pItem, iTeam, iState );
}

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

::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState );
if ( pItem && pDisguiseTarget )
{
// We must use team 0 for disguise weapons.
UpdateDisguiseBodygroups( pTFOwner, pDisguiseTarget, pItem, 0, iState );

CTFMechanicalArm* pMechArm = dynamic_cast<CTFMechanicalArm*>(pDisguiseWeapon);
if (pMechArm) {
// Hack, Short circuit is special case and should be off if it is the disguise weapon.
// Directly set the bodygroup since UpdateDisguiseBodygroups doesn't work for this weapon
int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
int iBodyGroup = pDisguiseTarget->FindBodygroupByName("rightarm");
if (iBodyGroup != -1)
{
::SetBodygroup(pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, 2);
pTFOwner->m_Shared.SetDisguiseBody(iDisguiseBody);
}
}
}

pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody );
}

CEconItemView *pItem = GetAttributeContainer() ? GetAttributeContainer()->GetItem() : NULL;
Expand Down
6 changes: 5 additions & 1 deletion src/game/shared/tf/tf_item_wearable.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#if defined( CLIENT_DLL )
#define CTFWearable C_TFWearable
#define CTFWearableVM C_TFWearableVM
class C_TFPlayer;
#define CTFPlayer C_TFPlayer
#else
class CTFPlayer;
#endif


Expand Down Expand Up @@ -74,8 +78,8 @@ class CTFWearable : public CEconWearable, public IHasGenericMeter
protected:
virtual void InternalSetPlayerDisplayModel( void );


private:
void UpdateDisguiseBodygroups( CTFPlayer *pTFOwner, CTFPlayer *pDisguiseTarget, CEconItemView *pItem, int iTeam, int iState );
CNetworkVar( bool, m_bDisguiseWearable );
CNetworkHandle( CBaseEntity, m_hWeaponAssociatedWith );

Expand Down
47 changes: 45 additions & 2 deletions src/game/shared/tf/tf_player_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,7 @@ void CTFPlayerShared::OnDataChanged( void )
{
m_hDisguiseWeapon->UpdateVisibility();
m_hDisguiseWeapon->UpdateParticleSystems();
m_hDisguiseWeapon->UpdateAttachmentModels();
}

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

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

// Mark the old disguise target's bodygroups as dirty so they'll be recalculated.
if ( pOldDisguiseTarget )
{
pOldDisguiseTarget->SetBodygroupsDirty();
}

#else

m_nDisguiseTeam = TF_SPY_UNDEFINED;
m_nDisguiseClass.Set( TF_CLASS_UNDEFINED );
m_nDisguiseSkinOverride = 0;
Expand Down Expand Up @@ -8223,6 +8233,15 @@ void CTFPlayerShared::Disguise( int nTeam, int nClass, CTFPlayer* pDesiredTarget
}
}

#ifdef CLIENT_DLL
// Save the old disguise target before changing disguise, so we can clean up bodygroups.
CTFPlayer *pOldDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
if ( pOldDisguiseTarget )
{
pOldDisguiseTarget->SetBodygroupsDirty();
}
#endif

m_hDesiredDisguiseTarget.Set( pDesiredTarget );
m_nDesiredDisguiseClass = nClass;
m_nDesiredDisguiseTeam = nTeam;
Expand Down Expand Up @@ -8402,6 +8421,7 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
{
CTFWeaponBase *pLastDisguiseWeapon = m_hDisguiseWeapon;
CTFWeaponBase *pFirstValidWeapon = NULL;

// Cycle through the target's weapons and see if we have a match.
// Note that it's possible the disguise target doesn't have a weapon in the slot we want,
// for example if they have replaced it with an unlockable that isn't a weapon (wearable).
Expand Down Expand Up @@ -8505,6 +8525,7 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
m_hDisguiseWeapon->m_bDisguiseWeapon = true;
m_hDisguiseWeapon->SetContextThink( &CTFWeaponBase::DisguiseWeaponThink, gpGlobals->curtime + 0.5, "DisguiseWeaponThink" );

m_hDisguiseWeapon->UpdateExtraWearables();

// Ammo/clip state is displayed to attached medics
m_iDisguiseAmmo = 0;
Expand Down Expand Up @@ -8539,14 +8560,31 @@ void CTFPlayerShared::DetermineDisguiseWeapon( bool bForcePrimary )
void CTFPlayerShared::DetermineDisguiseWearables()
{
CTFPlayer *pDisguiseTarget = ToTFPlayer( m_hDisguiseTarget.Get() );
if ( !pDisguiseTarget )
return;

// Remove any existing disguise wearables.
RemoveDisguiseWearables();

if ( !pDisguiseTarget )
{
// No target exists, reset disguise body to default state.
SetDisguiseBody( 0 );
return;
}

if ( GetDisguiseClass() != pDisguiseTarget->GetPlayerClass()->GetClassIndex() )
{
// Class mismatch, reset disguise body to default.
SetDisguiseBody( 0 );
#ifdef CLIENT_DLL
// Mark bodygroups dirty even when not copying wearables (class mismatch).
pDisguiseTarget->SetBodygroupsDirty();
#endif
return;
}

// Reset disguise body to default before applying new wearables.
// This ensures old bodygroup modifications don't carry over.
SetDisguiseBody( 0 );

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

m_nDisguiseSkinOverride = iPlayerSkinOverride;

#ifdef CLIENT_DLL
// Mark bodygroups dirty after creating disguise wearables.
pDisguiseTarget->SetBodygroupsDirty();
#endif
}

void CTFPlayerShared::RemoveDisguiseWearables()
Expand Down
84 changes: 83 additions & 1 deletion src/game/shared/tf/tf_weaponbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ void CTFWeaponBase::UpdateExtraWearables()
// Precaching may be needed here, because we allow virtually everything to be loaded on demand now.
pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableViewModel() );
}

pExtraWearableItem->SetDisguiseWearable(m_bDisguiseWeapon);
pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
pExtraWearableItem->SetAlwaysAllow( true );
DispatchSpawn( pExtraWearableItem );
Expand All @@ -1008,6 +1008,7 @@ void CTFWeaponBase::UpdateExtraWearables()
pExtraWearableItem->PrecacheModel( pEconItemView->GetExtraWearableModel() );
}

pExtraWearableItem->SetDisguiseWearable(m_bDisguiseWeapon);
pExtraWearableItem->AddSpawnFlags( SF_NORESPAWN );
pExtraWearableItem->SetAlwaysAllow( true );
DispatchSpawn( pExtraWearableItem );
Expand Down Expand Up @@ -3300,6 +3301,87 @@ bool CTFWeaponBase::OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
return BaseClass::OnInternalDrawModel( pInfo );
}

//-----------------------------------------------------------------------------
// Purpose: Override for disguise weapons to use team 0 for attachment lookups
//-----------------------------------------------------------------------------
void CTFWeaponBase::UpdateAttachmentModels( void )
{
#ifdef CLIENT_DLL
// For disguise weapons, we need to use the disguise target's team when fetching attachment models
// because GetTeamNumber() returns the spy's team, not the disguised target's team.
if ( m_bDisguiseWeapon )
{
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
if ( !pOwner )
{
BaseClass::UpdateAttachmentModels();
return;
}

C_TFPlayer *pDisguiseTarget = pOwner->m_Shared.GetDisguiseTarget();
int iTeamNumber = pDisguiseTarget ? pDisguiseTarget->GetTeamNumber() : 0;

CEconItemView *pItem = GetAttributeContainer()->GetItem();
GameItemDefinition_t *pItemDef = pItem && pItem->IsValid() ? pItem->GetStaticData() : NULL;

// Update the state of additional model attachments
m_vecAttachedModels.Purge();
if ( pItemDef && AttachmentModelsShouldBeVisible() )
{
{
int iAttachedModels = pItemDef->GetNumAttachedModels( iTeamNumber );
for ( int i = 0; i < iAttachedModels; i++ )
{
attachedmodel_t *pModel = pItemDef->GetAttachedModelData( iTeamNumber, i );

int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
if ( iModelIndex >= 0 )
{
AttachedModelData_t attachedModelData;
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
m_vecAttachedModels.AddToTail( attachedModelData );
}
}
}

// Check for Festive attachedmodels for festivized weapons
{
int iAttachedModels = pItemDef->GetNumAttachedModelsFestivized( iTeamNumber );
if ( iAttachedModels )
{
int iFestivized = 0;
CALL_ATTRIB_HOOK_INT( iFestivized, is_festivized );
if ( iFestivized )
{
for ( int i = 0; i < iAttachedModels; i++ )
{
attachedmodel_t *pModel = pItemDef->GetAttachedModelDataFestivized( iTeamNumber, i );

int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
if ( iModelIndex >= 0 )
{
AttachedModelData_t attachedModelData;
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
m_vecAttachedModels.AddToTail( attachedModelData );
}
}
}
}
}
}
// Note: We skip the viewmodel attachment section (ShouldAttachToHands) because
// disguise weapons are world models only and don't need viewmodel attachments.
}
else
{
// Normal weapons use the base class implementation
BaseClass::UpdateAttachmentModels();
}
#endif
}

void CTFWeaponBase::ProcessMuzzleFlashEvent( void )
{
C_BaseAnimating *pAttachEnt = GetAppropriateWorldOrViewModel();
Expand Down
1 change: 1 addition & 0 deletions src/game/shared/tf/tf_weaponbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ class CTFWeaponBase : public CBaseCombatWeapon, public IHasOwner, public IHasGen
virtual float CalcViewmodelBob( void );
BobState_t *GetBobState();
virtual bool AttachmentModelsShouldBeVisible( void ) OVERRIDE { return (m_iState == WEAPON_IS_ACTIVE) && !IsBeingRepurposedForTaunt(); }
virtual void UpdateAttachmentModels( void ) OVERRIDE;

virtual bool ShouldEjectBrass() { return true; }

Expand Down
Loading