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.
789 lines
23 KiB
789 lines
23 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "tf_item_wearable.h" |
|
#include "vcollide_parse.h" |
|
#include "tf_gamerules.h" |
|
#include "animation.h" |
|
#include "basecombatweapon_shared.h" |
|
#ifdef CLIENT_DLL |
|
#include "c_tf_player.h" |
|
#include "model_types.h" |
|
#include "props_shared.h" |
|
#include "tf_mapinfo.h" |
|
#else |
|
#include "tf_player.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
LINK_ENTITY_TO_CLASS( tf_wearable, CTFWearable ); |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearable, DT_TFWearable ) |
|
|
|
// Network Table -- |
|
BEGIN_NETWORK_TABLE( CTFWearable, DT_TFWearable ) |
|
#if defined( GAME_DLL ) |
|
SendPropBool( SENDINFO( m_bDisguiseWearable ) ), |
|
SendPropEHandle( SENDINFO( m_hWeaponAssociatedWith ) ), |
|
#else |
|
RecvPropBool( RECVINFO( m_bDisguiseWearable ) ), |
|
RecvPropEHandle( RECVINFO( m_hWeaponAssociatedWith ) ), |
|
#endif // GAME_DLL |
|
END_NETWORK_TABLE() |
|
// -- Network Table |
|
|
|
// Data Desc -- |
|
BEGIN_DATADESC( CTFWearable ) |
|
END_DATADESC() |
|
// -- Data Desc |
|
|
|
PRECACHE_REGISTER( tf_wearable ); |
|
|
|
|
|
LINK_ENTITY_TO_CLASS( tf_wearable_vm, CTFWearableVM ); |
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableVM, DT_TFWearableVM ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFWearableVM, DT_TFWearableVM ) |
|
END_NETWORK_TABLE() |
|
|
|
PRECACHE_REGISTER( tf_wearable_vm ); |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// SHARED CODE |
|
//----------------------------------------------------------------------------- |
|
|
|
CTFWearable::CTFWearable() : CEconWearable() |
|
{ |
|
m_bDisguiseWearable = false; |
|
m_hWeaponAssociatedWith = NULL; |
|
#if defined( CLIENT_DLL ) |
|
m_eParticleSystemVisibility = kParticleSystemVisibility_Undetermined; |
|
m_nWorldModelIndex = 0; |
|
#endif |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// SERVER ONLY CODE |
|
//----------------------------------------------------------------------------- |
|
|
|
#if defined( GAME_DLL ) |
|
void CTFWearable::Break( void ) |
|
{ |
|
CPVSFilter filter( GetAbsOrigin() ); |
|
UserMessageBegin( filter, "BreakModel" ); |
|
WRITE_SHORT( GetModelIndex() ); |
|
WRITE_VEC3COORD( GetAbsOrigin() ); |
|
WRITE_ANGLES( GetAbsAngles() ); |
|
WRITE_SHORT( GetSkin() ); |
|
MessageEnd(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWearable::CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer ) |
|
{ |
|
if ( m_bDisguiseWearable ) |
|
{ |
|
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); |
|
if ( pTFPlayer ) |
|
return pTFPlayer->m_Shared.GetDisguiseClass(); |
|
} |
|
return BaseClass::CalculateVisibleClassFor( pPlayer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWearable::UpdateTransmitState() |
|
{ |
|
return SetTransmitState( FL_EDICT_FULLCHECK ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWearable::ShouldTransmit( const CCheckTransmitInfo *pInfo ) |
|
{ |
|
if ( pInfo->m_pClientEnt && GetOwnerEntity() && CBaseEntity::Instance( pInfo->m_pClientEnt ) == GetOwnerEntity() ) |
|
{ |
|
return FL_EDICT_ALWAYS; |
|
} |
|
|
|
// We have some entities that have no model (ie., "hatless hats") but we still want |
|
// to transmit them down to clients so that the clients can do things like update body |
|
// groups, etc. |
|
return FL_EDICT_PVSCHECK; |
|
} |
|
|
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
ConVar tf_test_hat_bodygroup( "tf_test_hat_bodygroup", "0", 0, "For testing bodygroups on hats." ); |
|
#endif |
|
|
|
static int CalcBodyGroup( CBaseCombatCharacter* pOwner, CEconItemView *pItem, const char *pBodyGroup, codecontrolledbodygroupdata_t &ccbgd ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
if ( !Q_strnicmp( ccbgd.pFuncName, "test", ARRAYSIZE( "test" ) ) ) |
|
{ |
|
return tf_test_hat_bodygroup.GetInt(); |
|
} |
|
else if ( !Q_strnicmp( ccbgd.pFuncName, "map_contributor", ARRAYSIZE( "map_contributor" ) ) ) |
|
{ |
|
int iDonationAmount = MapInfo_GetDonationAmount( pItem->GetAccountID(), engine->GetLevelName() ); |
|
return MIN( iDonationAmount / 25, 4 ); |
|
} |
|
#endif |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// CLIENT ONLY CODE |
|
//----------------------------------------------------------------------------- |
|
|
|
#if defined( CLIENT_DLL ) |
|
extern ConVar tf_playergib_forceup; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Receive the BreakModel user message |
|
//----------------------------------------------------------------------------- |
|
void HandleBreakModel( bf_read &msg, bool bCheap ) |
|
{ |
|
int nModelIndex = (int)msg.ReadShort(); |
|
CUtlVector<breakmodel_t> aGibs; |
|
BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE ); |
|
if ( !aGibs.Count() ) |
|
return; |
|
|
|
// Get the origin & angles |
|
Vector vecOrigin; |
|
QAngle vecAngles; |
|
int nSkin = 0; |
|
msg.ReadBitVec3Coord( vecOrigin ); |
|
if ( !bCheap ) |
|
{ |
|
msg.ReadBitAngles( vecAngles ); |
|
nSkin = (int)msg.ReadShort(); |
|
} |
|
else |
|
{ |
|
vecAngles = vec3_angle; |
|
} |
|
|
|
// Launch it straight up with some random spread |
|
Vector vecBreakVelocity = Vector(0,0,200); |
|
AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 ); |
|
breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse ); |
|
breakParams.impactEnergyScale = 1.0f; |
|
breakParams.nDefaultSkin = nSkin; |
|
|
|
CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Receive the BreakModel user message |
|
//----------------------------------------------------------------------------- |
|
void __MsgFunc_BreakModel( bf_read &msg ) |
|
{ |
|
HandleBreakModel( msg, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Receive the CheapBreakModel user message |
|
//----------------------------------------------------------------------------- |
|
void __MsgFunc_CheapBreakModel( bf_read &msg ) |
|
{ |
|
HandleBreakModel( msg, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Receive the BreakModel_Pumpkin user message |
|
//----------------------------------------------------------------------------- |
|
void __MsgFunc_BreakModel_Pumpkin( bf_read &msg ) |
|
{ |
|
int nModelIndex = (int)msg.ReadShort(); |
|
CUtlVector<breakmodel_t> aGibs; |
|
BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE ); |
|
if ( !aGibs.Count() ) |
|
return; |
|
|
|
// Get the origin & angles |
|
Vector vecOrigin; |
|
QAngle vecAngles; |
|
msg.ReadBitVec3Coord( vecOrigin ); |
|
msg.ReadBitAngles( vecAngles ); |
|
|
|
// Launch it straight up with some random spread |
|
Vector vecBreakVelocity = Vector(0,0,0); |
|
AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 ); |
|
breakablepropparams_t breakParams( vecOrigin /*+ Vector(0,0,20)*/, vecAngles, vecBreakVelocity, angularImpulse ); |
|
breakParams.impactEnergyScale = 1.0f; |
|
|
|
for ( int i=0; i<aGibs.Count(); ++i ) |
|
{ |
|
aGibs[i].burstScale = 1000.f; |
|
} |
|
|
|
CUtlVector<EHANDLE> hSpawnedGibs; |
|
CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true, &hSpawnedGibs ); |
|
|
|
// Make the base stay low to the ground. |
|
for ( int i=0; i<hSpawnedGibs.Count(); ++i ) |
|
{ |
|
CBaseEntity *pGib = hSpawnedGibs[i]; |
|
if ( pGib ) |
|
{ |
|
IPhysicsObject *pPhysObj = pGib->VPhysicsGetObject(); |
|
if ( pPhysObj ) |
|
{ |
|
Vector vecVel; |
|
AngularImpulse angImp; |
|
pPhysObj->GetVelocity( &vecVel, &angImp ); |
|
vecVel *= 3.0; |
|
if ( i == 3 ) |
|
{ |
|
vecVel.z = 300; |
|
} |
|
else |
|
{ |
|
vecVel.z = 400; |
|
} |
|
pPhysObj->SetVelocity( &vecVel, &angImp ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFWearable::InternalDrawModel( int flags ) |
|
{ |
|
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); |
|
|
|
if ( pOwner && pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ) |
|
{ |
|
bool bShouldDraw = false; |
|
const CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem ) |
|
{ |
|
econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( "ghost_wearable" ); |
|
if ( pItem->GetItemDefinition()->HasEconTag( tagHandle ) ) |
|
bShouldDraw = true; |
|
} |
|
|
|
if ( !bShouldDraw ) |
|
return 0; |
|
} |
|
|
|
bool bUseInvulnMaterial = ( pOwner && pOwner->m_Shared.IsInvulnerable() && |
|
( !pOwner->m_Shared.InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) || gpGlobals->curtime < pOwner->GetLastDamageTime() + 2.0f ) ); |
|
|
|
if ( bUseInvulnMaterial && (flags & STUDIO_RENDER) ) |
|
{ |
|
modelrender->ForcedMaterialOverride( *pOwner->GetInvulnMaterialRef() ); |
|
} |
|
|
|
int ret = BaseClass::InternalDrawModel( flags ); |
|
|
|
if ( bUseInvulnMaterial && (flags & STUDIO_RENDER) ) |
|
{ |
|
modelrender->ForcedMaterialOverride( NULL ); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFWearable::ShouldDraw() |
|
{ |
|
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); |
|
|
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ) |
|
{ |
|
const CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem ) |
|
{ |
|
econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( "ghost_wearable" ); |
|
if ( pItem->GetItemDefinition()->HasEconTag( tagHandle ) ) |
|
return BaseClass::ShouldDraw(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// don't draw cosmetic while sniper is zoom |
|
if ( pOwner == C_TFPlayer::GetLocalTFPlayer() && pOwner->m_Shared.InCond( TF_COND_ZOOMED ) ) |
|
return false; |
|
} |
|
|
|
// Don't draw 3rd person wearables if our owner is disguised. |
|
if ( pOwner && pOwner->m_Shared.InCond( TF_COND_DISGUISED ) && !IsViewModelWearable() ) |
|
{ |
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( m_bDisguiseWearable && pLocalPlayer ) |
|
{ |
|
int iLocalPlayerTeam = pLocalPlayer->GetTeamNumber(); |
|
if ( pLocalPlayer->m_bIsCoaching && pLocalPlayer->m_hStudent ) |
|
{ |
|
iLocalPlayerTeam = pLocalPlayer->m_hStudent->GetTeamNumber(); |
|
} |
|
|
|
// This wearable is a part of our disguise -- we might want to draw it. |
|
if ( GetEnemyTeam( pOwner->GetTeamNumber() ) != iLocalPlayerTeam ) |
|
{ |
|
// The local player is on this spy's team. We don't see the disguise. |
|
return false; |
|
} |
|
else |
|
{ |
|
if ( pOwner->m_Shared.GetDisguiseClass() == TF_CLASS_SPY && |
|
pOwner->m_Shared.GetDisguiseTeam() == iLocalPlayerTeam ) |
|
{ |
|
// This enemy spy is disguised as a spy on our team, don't draw wearables. |
|
return false; |
|
} |
|
else |
|
{ |
|
// The local player is an enemy. Show the disguise wearable. |
|
return BaseClass::ShouldDraw(); |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
else |
|
{ |
|
// See if the visibility is controlled by a weapon. |
|
CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( GetWeaponAssociatedWith() ); |
|
if ( pWeapon ) |
|
{ |
|
// If the weapon isn't active, don't draw |
|
if ( pOwner && pOwner->GetActiveWeapon() != pWeapon ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( !IsViewModelWearable() ) |
|
{ |
|
// If it's the 3rd person wearable, don't draw it when the weapon is hidden |
|
if ( !pWeapon->ShouldDraw() ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
// If the weapon is being repurposed for a taunt dont draw. |
|
// The Brutal Legend taunt changes your weapon's model to be the guitar, |
|
// but we dont want things like bot-killer skulls or festive lights |
|
// to continue to draw |
|
if( pWeapon->IsBeingRepurposedForTaunt() ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
return BaseClass::ShouldDraw(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFWearable::ShouldDrawParticleSystems( void ) |
|
{ |
|
if ( !BaseClass::ShouldDrawParticleSystems() ) |
|
return false; |
|
|
|
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
bool bStealthed = pPlayer->m_Shared.IsStealthed(); |
|
|
|
// If we're disguised, this ought to only be getting called on disguise wearables, |
|
// otherwise we could get two particles showing at once (disguise wearable + real wearable). |
|
Assert( !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) || IsDisguiseWearable() ); |
|
|
|
if ( bStealthed ) |
|
{ |
|
return false; |
|
} |
|
|
|
if ( m_eParticleSystemVisibility == kParticleSystemVisibility_Undetermined ) |
|
{ |
|
static CSchemaItemDefHandle pItemDef_MapLoverHat( "World Traveler" ); |
|
|
|
m_eParticleSystemVisibility = kParticleSystemVisibility_Shown; |
|
|
|
const CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->GetStaticData() == pItemDef_MapLoverHat ) |
|
{ |
|
if ( MapInfo_DidPlayerDonate( pItem->GetAccountID(), engine->GetLevelName() ) == false ) |
|
{ |
|
m_eParticleSystemVisibility = kParticleSystemVisibility_Hidden; |
|
} |
|
} |
|
} |
|
|
|
return m_eParticleSystemVisibility == kParticleSystemVisibility_Shown; |
|
} |
|
|
|
int CTFWearable::GetWorldModelIndex( void ) |
|
{ |
|
if ( m_nWorldModelIndex == 0 ) |
|
return m_nModelIndex; |
|
|
|
static CSchemaItemDefHandle pItemDef_OculusRiftHeadset( "The TF2VRH" ); |
|
const CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->GetStaticData() == pItemDef_OculusRiftHeadset ) |
|
{ |
|
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pTFPlayer ) |
|
{ |
|
if ( pTFPlayer->IsUsingVRHeadset() && pTFPlayer->GetPlayerClass() ) |
|
{ |
|
const char *pszReplacementModel = pItem->GetStaticData()->GetPlayerDisplayModelAlt( pTFPlayer->GetPlayerClass()->GetClassIndex() ); |
|
if ( pszReplacementModel && pszReplacementModel[0] ) |
|
{ |
|
return modelinfo->GetModelIndex( pszReplacementModel ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//********************************************************************************* |
|
// Parachute states |
|
static CSchemaItemDefHandle pItemDef_BaseJumper( "The B.A.S.E. Jumper" ); |
|
const int iParachuteOpen = modelinfo->GetModelIndex( "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack_open.mdl" ); |
|
const int iParachuteClosed = modelinfo->GetModelIndex( "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack.mdl" ); |
|
if ( m_nModelIndex == iParachuteOpen || m_nModelIndex == iParachuteClosed ) |
|
{ |
|
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pTFPlayer ) |
|
{ |
|
if ( pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) ) |
|
{ |
|
return iParachuteOpen; |
|
} |
|
else |
|
{ |
|
return iParachuteClosed; |
|
} |
|
} |
|
} |
|
|
|
if ( GameRules() ) |
|
{ |
|
const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( m_nWorldModelIndex ) ); |
|
const char *pTranslatedName = GameRules()->TranslateEffectForVisionFilter( "weapons", pBaseName ); |
|
|
|
if ( pTranslatedName != pBaseName ) |
|
{ |
|
return modelinfo->GetModelIndex( pTranslatedName ); |
|
} |
|
} |
|
|
|
return m_nWorldModelIndex; |
|
} |
|
|
|
void CTFWearable::ValidateModelIndex( void ) |
|
{ |
|
m_nModelIndex = GetWorldModelIndex(); |
|
|
|
BaseClass::ValidateModelIndex(); |
|
} |
|
|
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Hides or shows masked bodygroups associated with this item. |
|
//----------------------------------------------------------------------------- |
|
bool CTFWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState ) |
|
{ |
|
CTFPlayer *pTFOwner = ToTFPlayer( pOwner ); |
|
if ( !pTFOwner ) |
|
return false; |
|
|
|
bool bBaseUpdate = BaseClass::UpdateBodygroups( pOwner, iState ); |
|
if ( bBaseUpdate && m_bDisguiseWearable ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call. |
|
|
|
CTFPlayer *pDisguiseTarget = ToTFPlayer( 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 ); |
|
|
|
if ( iBodyGroup == -1 ) |
|
continue; |
|
|
|
::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState ); |
|
} |
|
|
|
pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody ); |
|
} |
|
|
|
CEconItemView *pItem = GetAttributeContainer() ? GetAttributeContainer()->GetItem() : NULL; |
|
if ( pItem ) |
|
{ |
|
int iTeam = pTFOwner->GetTeamNumber(); |
|
int iNumBodyGroups = pItem->GetStaticData()->GetNumCodeControlledBodyGroups( iTeam ); |
|
for ( int i=0; i<iNumBodyGroups; ++i ) |
|
{ |
|
codecontrolledbodygroupdata_t ccbgd = { NULL, NULL }; |
|
const char *pszBodyGroup = pItem->GetStaticData()->GetCodeControlledBodyGroup( iTeam, i, ccbgd ); |
|
int iBodyGroup = FindBodygroupByName( pszBodyGroup ); |
|
if ( iBodyGroup != -1 ) |
|
{ |
|
SetBodygroup( iBodyGroup, CalcBodyGroup( pOwner, pItem, pszBodyGroup, ccbgd ) ); |
|
} |
|
} |
|
} |
|
|
|
// Additional hidden bodygroups. |
|
for ( int i=0; i<m_HiddenBodyGroups.Count(); ++i ) |
|
{ |
|
int iBodyGroup = pOwner->FindBodygroupByName( m_HiddenBodyGroups[i] ); |
|
if ( iBodyGroup == -1 ) |
|
continue; |
|
pOwner->SetBodygroup( iBodyGroup, iState ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
int CTFWearable::GetSkin() |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( !pPlayer ) |
|
return 0; |
|
|
|
int iTeamNumber = pPlayer->GetTeamNumber(); |
|
|
|
#if defined( CLIENT_DLL ) |
|
// Run client-only "is the viewer on the same team as the wielder" logic. Assumed to |
|
// always be false on the server. |
|
CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer(); |
|
if ( !pLocalPlayer ) |
|
return 0; |
|
|
|
int iLocalTeam = pLocalPlayer->GetTeamNumber(); |
|
|
|
// We only show disguise weapon to the enemy team when owner is disguised |
|
bool bUseDisguiseWeapon = ( iTeamNumber != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM ); |
|
|
|
if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ) |
|
{ |
|
if ( pLocalPlayer != pPlayer ) |
|
{ |
|
iTeamNumber = pPlayer->m_Shared.GetDisguiseTeam(); |
|
} |
|
} |
|
#endif // defined( CLIENT_DLL ) |
|
|
|
// See if the item wants to override the skin |
|
int nSkin = -1; |
|
|
|
CBaseCombatWeapon *pWeapon = assert_cast< CBaseCombatWeapon* >( GetWeaponAssociatedWith() ); |
|
if ( pWeapon ) |
|
{ |
|
CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem(); |
|
if ( pItem->IsValid() ) |
|
{ |
|
nSkin = pItem->GetSkin( iTeamNumber ); // if we didn't have custom code, fall back to the item definition |
|
} |
|
} |
|
|
|
if ( nSkin != -1 ) |
|
{ |
|
return nSkin; |
|
} |
|
|
|
return BaseClass::GetSkin(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::InternalSetPlayerDisplayModel( void ) |
|
{ |
|
// Set our model to the player model |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->IsValid() && pItem->GetStaticData() ) |
|
{ |
|
if ( pItem->GetStaticData()->IsContentStreamable() ) |
|
{ |
|
const char *pszPlayerDisplayModelAlt = pItem->GetStaticData()->GetPlayerDisplayModelAlt(); |
|
if ( pszPlayerDisplayModelAlt && pszPlayerDisplayModelAlt[0] ) |
|
{ |
|
modelinfo->RegisterDynamicModel( pszPlayerDisplayModelAlt, IsClient() ); |
|
} |
|
} |
|
} |
|
|
|
BaseClass::InternalSetPlayerDisplayModel(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::AddHiddenBodyGroup( const char* bodygroup ) |
|
{ |
|
m_HiddenBodyGroups.AddToHead( bodygroup ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::ReapplyProvision( void ) |
|
{ |
|
// Disguise wearables never provide |
|
if ( IsDisguiseWearable() ) |
|
{ |
|
#ifdef GAME_DLL |
|
UpdateModelToClass(); |
|
#endif |
|
return; |
|
} |
|
|
|
BaseClass::ReapplyProvision(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attaches the item to the player. |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::Equip( CBasePlayer* pOwner ) |
|
{ |
|
BaseClass::Equip( pOwner ); |
|
|
|
CTFPlayer *pTFPlayer = ToTFPlayer( pOwner ); |
|
if ( !pTFPlayer ) |
|
return; |
|
|
|
int iTeamNumber = pTFPlayer->GetTeamNumber(); |
|
if ( m_bDisguiseWearable ) |
|
{ |
|
iTeamNumber = pTFPlayer->m_Shared.GetDisguiseTeam(); |
|
} |
|
ChangeTeam( iTeamNumber ); |
|
m_nSkin = ( iTeamNumber == (LAST_SHARED_TEAM+1) ) ? 0 : 1; |
|
|
|
#ifdef CLIENT_DLL |
|
pTFPlayer->SetBodygroupsDirty(); |
|
#endif |
|
|
|
#ifdef GAME_DLL |
|
// Reapply upgrades for wearables upon equip |
|
CEconItemView *pItem = ( (CTFWearable *)this )->GetAttributeContainer()->GetItem(); |
|
if ( pTFPlayer && pItem->IsValid() ) |
|
{ |
|
pTFPlayer->ReapplyItemUpgrades( pItem ); |
|
} |
|
#endif // GAME_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attaches the item to the player. |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::UnEquip( CBasePlayer* pOwner ) |
|
{ |
|
BaseClass::UnEquip( pOwner ); |
|
|
|
#ifdef CLIENT_DLL |
|
CTFPlayer *pTFPlayer = ToTFPlayer( pOwner ); |
|
if ( pTFPlayer ) |
|
{ |
|
pTFPlayer->SetBodygroupsDirty(); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Check for any TF specific restrictions on item use. |
|
//----------------------------------------------------------------------------- |
|
bool CTFWearable::CanEquip( CBaseEntity *pOther ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && TFGameRules() ) |
|
{ |
|
CEconItemDefinition* pData = pItem->GetStaticData(); |
|
if ( pData && pData->GetHolidayRestriction() ) |
|
{ |
|
int iHolidayRestriction = UTIL_GetHolidayForString( pData->GetHolidayRestriction() ); |
|
if ( iHolidayRestriction != kHoliday_None && !TFGameRules()->IsHolidayActive( iHolidayRestriction ) ) |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::OnDataChanged( DataUpdateType_t updateType ) |
|
{ |
|
BaseClass::OnDataChanged( updateType ); |
|
|
|
if ( updateType == DATA_UPDATE_CREATED ) |
|
{ |
|
ListenForGameEvent( "localplayer_changeteam" ); |
|
|
|
m_nWorldModelIndex = m_nModelIndex; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFWearable::FireGameEvent( IGameEvent *event ) |
|
{ |
|
const char *pszEventName = event->GetName(); |
|
if ( Q_strcmp( pszEventName, "localplayer_changeteam" ) == 0 ) |
|
{ |
|
UpdateVisibility(); |
|
} |
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Choose shadow type for VM-wearables. |
|
//----------------------------------------------------------------------------- |
|
#if defined( CLIENT_DLL ) |
|
ShadowType_t CTFWearableVM::ShadowCastType( void ) |
|
{ |
|
if ( ToTFPlayer(GetMoveParent())->ShouldDrawThisPlayer() ) |
|
{ |
|
// Using the viewmodel. |
|
return SHADOWS_NONE; |
|
} |
|
|
|
return SHADOWS_RENDER_TO_TEXTURE; |
|
} |
|
#endif |
|
|
|
|
|
|