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.
2078 lines
64 KiB
2078 lines
64 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "econ_entity_creation.h" |
|
#include "vgui/ILocalize.h" |
|
#include "tier3/tier3.h" |
|
|
|
#if defined( CLIENT_DLL ) |
|
#define UTIL_VarArgs VarArgs |
|
|
|
#include "econ_item_inventory.h" |
|
#include "model_types.h" |
|
#include "eventlist.h" |
|
#include "networkstringtable_clientdll.h" |
|
#include "cdll_util.h" |
|
|
|
#if defined(TF_CLIENT_DLL) |
|
#include "c_tf_player.h" |
|
#include "tf_gamerules.h" |
|
#include "c_playerresource.h" |
|
#include "tf_shareddefs.h" |
|
#endif |
|
|
|
#else // defined( CLIENT_DLL ) |
|
|
|
#include "activitylist.h" |
|
|
|
#if defined(TF_DLL) |
|
#include "tf_player.h" |
|
#endif |
|
#endif // defined( CLIENT_DLL ) |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( EconEntity, DT_EconEntity ) |
|
IMPLEMENT_NETWORKCLASS_ALIASED( BaseAttributableItem, DT_BaseAttributableItem ) |
|
|
|
#if defined( CLIENT_DLL ) |
|
bool ParseItemKeyvalue( void *pObject, typedescription_t *pFields, int iNumFields, const char *szKeyName, const char *szValue ); |
|
#endif |
|
|
|
#if defined(_DEBUG) |
|
extern ConVar item_debug; |
|
extern ConVar item_debug_validation; |
|
#endif |
|
|
|
#if !defined( CLIENT_DLL ) |
|
#define DEFINE_ECON_ENTITY_NETWORK_TABLE() \ |
|
SendPropDataTable( SENDINFO_DT( m_AttributeManager ), &REFERENCE_SEND_TABLE(DT_AttributeContainer) ), |
|
#else |
|
#define DEFINE_ECON_ENTITY_NETWORK_TABLE() \ |
|
RecvPropDataTable( RECVINFO_DT( m_AttributeManager ), 0, &REFERENCE_RECV_TABLE(DT_AttributeContainer) ), |
|
#endif // CLIENT_DLL |
|
|
|
BEGIN_NETWORK_TABLE( CEconEntity , DT_EconEntity ) |
|
DEFINE_ECON_ENTITY_NETWORK_TABLE() |
|
|
|
#if defined(TF_DLL) |
|
SendPropBool( SENDINFO( m_bValidatedAttachedEntity ) ), |
|
#elif defined(TF_CLIENT_DLL) |
|
RecvPropBool( RECVINFO( m_bValidatedAttachedEntity ) ), |
|
#endif // TF_DLL || TF_CLIENT_DLL |
|
|
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_DATADESC( CEconEntity ) |
|
END_DATADESC() |
|
|
|
// |
|
// Duplicating CEconEntity's network table and data description for backwards compat with demos. |
|
// NOTE: NOTE_RENAMED_RECVTABLE() will not work with this class. |
|
// |
|
BEGIN_NETWORK_TABLE( CBaseAttributableItem, DT_BaseAttributableItem ) |
|
DEFINE_ECON_ENTITY_NETWORK_TABLE() |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_DATADESC( CBaseAttributableItem ) |
|
END_DATADESC() |
|
|
|
#ifdef TF_CLIENT_DLL |
|
extern ConVar cl_flipviewmodels; |
|
#ifdef STAGING_ONLY |
|
ConVar unusual_force_weapon_effect( "unusual_force_weapon_effect", "-1", FCVAR_CHEAT, "Set to force an Unusual effect on your weapon (Primary, Secondary, Melee)" ); |
|
ConVar unusual_force_cosmetic_effect( "unusual_force_cosmetic_effect", "-1", FCVAR_CHEAT, "Set to force an Unusual effect on your equipped cosmetics" ); |
|
#endif |
|
#endif |
|
|
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void DrawEconEntityAttachedModels( CBaseAnimating *pEnt, CEconEntity *pAttachedModelSource, const ClientModelRenderInfo_t *pInfo, int iMatchDisplayFlags ) |
|
{ |
|
#ifndef DOTA_DLL |
|
if ( !pEnt || !pAttachedModelSource || !pInfo ) |
|
return; |
|
|
|
// This flag says we should turn off the material overrides for attachments. |
|
IMaterial* pMaterialOverride = NULL; |
|
OverrideType_t nMaterialOverrideType = OVERRIDE_NORMAL; |
|
|
|
if ( ( pInfo->flags & STUDIO_NO_OVERRIDE_FOR_ATTACH ) != 0 ) |
|
{ |
|
modelrender->GetMaterialOverride( &pMaterialOverride, &nMaterialOverrideType ); |
|
modelrender->ForcedMaterialOverride( NULL, nMaterialOverrideType ); |
|
} |
|
|
|
// Draw our attached models as well |
|
for ( int i = 0; i < pAttachedModelSource->m_vecAttachedModels.Size(); i++ ) |
|
{ |
|
const AttachedModelData_t& attachedModel = pAttachedModelSource->m_vecAttachedModels[i]; |
|
|
|
if ( attachedModel.m_pModel && (attachedModel.m_iModelDisplayFlags & iMatchDisplayFlags) ) |
|
{ |
|
ClientModelRenderInfo_t infoAttached = *pInfo; |
|
|
|
infoAttached.pRenderable = pEnt; |
|
infoAttached.instance = MODEL_INSTANCE_INVALID; |
|
infoAttached.entity_index = pEnt->index; |
|
infoAttached.pModel = attachedModel.m_pModel; |
|
|
|
infoAttached.pModelToWorld = &infoAttached.modelToWorld; |
|
|
|
// Turns the origin + angles into a matrix |
|
AngleMatrix( infoAttached.angles, infoAttached.origin, infoAttached.modelToWorld ); |
|
|
|
DrawModelState_t state; |
|
matrix3x4_t *pBoneToWorld; |
|
bool bMarkAsDrawn = modelrender->DrawModelSetup( infoAttached, &state, NULL, &pBoneToWorld ); |
|
pEnt->DoInternalDrawModel( &infoAttached, ( bMarkAsDrawn && ( infoAttached.flags & STUDIO_RENDER ) ) ? &state : NULL, pBoneToWorld ); |
|
} |
|
} |
|
|
|
if ( pMaterialOverride != NULL ) |
|
modelrender->ForcedMaterialOverride( pMaterialOverride, nMaterialOverrideType ); |
|
#endif |
|
} |
|
#endif // CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CEconEntity::CEconEntity() |
|
{ |
|
m_pAttributes = this; |
|
|
|
// Inform base entity system that we can deal with dynamic models |
|
EnableDynamicModels(); |
|
#ifdef GAME_DLL |
|
m_iOldOwnerClass = 0; |
|
#endif |
|
|
|
#if defined(TF_DLL) || defined(TF_CLIENT_DLL) |
|
m_bValidatedAttachedEntity = false; |
|
#endif // TF_DLL || TF_CLIENT_DLL |
|
|
|
#ifdef CLIENT_DLL |
|
m_flFlexDelayTime = 0.0f; |
|
m_flFlexDelayedWeight = NULL; |
|
m_cFlexDelayedWeight = 0; |
|
m_iNumOwnerValidationRetries = 0; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CEconEntity::~CEconEntity() |
|
{ |
|
#ifdef CLIENT_DLL |
|
SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE ); |
|
delete [] m_flFlexDelayedWeight; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CStudioHdr * CEconEntity::OnNewModel() |
|
{ |
|
CStudioHdr* hdr = BaseClass::OnNewModel(); |
|
|
|
#ifdef GAME_DLL |
|
// Adjust class-specific bodygroup after model load if we have a model and a class |
|
if ( hdr && m_iOldOwnerClass > 0 ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->IsValid() && pItem->GetStaticData()->UsesPerClassBodygroups( GetTeamNumber() ) ) |
|
{ |
|
// Classes start at 1, bodygroups at 0 |
|
SetBodygroup( 1, m_iOldOwnerClass - 1 ); |
|
} |
|
} |
|
#endif |
|
|
|
#ifdef TF_CLIENT_DLL |
|
m_bValidatedOwner = false; // require item validation to re-run |
|
|
|
// If we're carried by a player, let him know he should recalc his bodygroups. |
|
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pPlayer ) |
|
{ |
|
pPlayer->SetBodygroupsDirty(); |
|
} |
|
|
|
// allocate room for delayed flex weights |
|
delete [] m_flFlexDelayedWeight; |
|
m_flFlexDelayedWeight = NULL; |
|
m_cFlexDelayedWeight = 0; |
|
if ( hdr && hdr->numflexcontrollers() ) |
|
{ |
|
m_cFlexDelayedWeight = hdr->numflexcontrollers(); |
|
m_flFlexDelayedWeight = new float[ m_cFlexDelayedWeight ]; |
|
memset( m_flFlexDelayedWeight, 0, sizeof( float ) * m_cFlexDelayedWeight ); |
|
|
|
C_BaseFlex::LinkToGlobalFlexControllers( hdr ); |
|
} |
|
#endif // TF_CLIENT_DLL |
|
|
|
return hdr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::InitializeAttributes( void ) |
|
{ |
|
m_AttributeManager.InitializeAttributes( this ); |
|
m_AttributeManager.SetProviderType( PROVIDER_WEAPON ); |
|
|
|
#ifdef CLIENT_DLL |
|
// Check particle systems |
|
CUtlVector<const attachedparticlesystem_t *> vecParticles; |
|
GetEconParticleSystems( &vecParticles ); |
|
m_bHasParticleSystems = vecParticles.Count() > 0; |
|
|
|
if ( !m_bClientside ) |
|
return; |
|
#else |
|
m_AttributeManager.GetItem()->InitNetworkedDynamicAttributesForDemos(); |
|
#endif |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::DebugDescribe( void ) |
|
{ |
|
CEconItemView *pScriptItem = GetAttributeContainer()->GetItem(); |
|
|
|
Msg("============================================\n"); |
|
char tempstr[1024]; |
|
// FIXME: ILocalize::ConvertUnicodeToANSI( pScriptItem->GetItemName(), tempstr, sizeof(tempstr) ); |
|
const char *pszQualityString = EconQuality_GetQualityString( (EEconItemQuality)pScriptItem->GetItemQuality() ); |
|
Msg("%s \"%s\" (level %d)\n", pszQualityString ? pszQualityString : "[unknown]", tempstr, pScriptItem->GetItemLevel() ); |
|
// FIXME: ILocalize::ConvertUnicodeToANSI( pScriptItem->GetAttributeDescription(), tempstr, sizeof(tempstr) ); |
|
Msg("%s", tempstr ); |
|
Msg("\n============================================\n"); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::UpdateOnRemove( void ) |
|
{ |
|
SetOwnerEntity( NULL ); |
|
ReapplyProvision(); |
|
|
|
BaseClass::UpdateOnRemove(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::ReapplyProvision( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
UpdateModelToClass(); |
|
#endif |
|
|
|
CBaseEntity *pNewOwner = GetOwnerEntity(); |
|
if ( pNewOwner == m_hOldProvidee.Get() ) |
|
return; |
|
|
|
// Remove ourselves from the old providee's list |
|
if ( m_hOldProvidee.Get() ) |
|
{ |
|
GetAttributeManager()->StopProvidingTo( m_hOldProvidee.Get() ); |
|
} |
|
|
|
// Add ourselves to our new owner's provider list |
|
if ( pNewOwner ) |
|
{ |
|
GetAttributeManager()->ProvideTo( pNewOwner ); |
|
} |
|
|
|
m_hOldProvidee = pNewOwner; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
Activity CEconEntity::TranslateViewmodelHandActivity( Activity actBase ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->IsValid() ) |
|
{ |
|
GameItemDefinition_t *pStaticData = pItem->GetStaticData(); |
|
if ( pStaticData && pStaticData->ShouldAttachToHands() ) |
|
{ |
|
return TranslateViewmodelHandActivityInternal(actBase); |
|
} |
|
} |
|
|
|
return actBase; |
|
} |
|
|
|
#if !defined( CLIENT_DLL ) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::OnOwnerClassChange( void ) |
|
{ |
|
#ifdef TF_DLL |
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pPlayer && pPlayer->GetPlayerClass()->GetClassIndex() != m_iOldOwnerClass ) |
|
{ |
|
UpdateModelToClass(); |
|
} |
|
#endif |
|
} |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CEconEntity::CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer ) |
|
{ |
|
#ifdef TF_DLL |
|
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); |
|
return (pTFPlayer ? pTFPlayer->GetPlayerClass()->GetClassIndex() : 0); |
|
#else |
|
return 0; |
|
#endif |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: double duty function - sets up model for current player class, and |
|
// also sets bodygroups if the correct model is fully loaded. |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::UpdateModelToClass( void ) |
|
{ |
|
#ifdef TF_DLL |
|
MDLCACHE_CRITICAL_SECTION(); |
|
|
|
CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
m_iOldOwnerClass = CalculateVisibleClassFor( pPlayer ); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( !pItem->IsValid() ) |
|
return; |
|
|
|
const char *pszModel = NULL; |
|
|
|
// If we attach to hands, we need to use the hand models |
|
if ( pItem->GetStaticData()->ShouldAttachToHands() ) |
|
{ |
|
pszModel = pPlayer->GetPlayerClass()->GetHandModelName( 0 ); |
|
} |
|
else |
|
{ |
|
int nTeam = pPlayer->GetTeamNumber(); |
|
CTFWearable *pWearable = dynamic_cast< CTFWearable*>( this ); |
|
if ( pWearable && pWearable->IsDisguiseWearable() ) |
|
{ |
|
nTeam = pPlayer->m_Shared.GetDisguiseTeam(); |
|
} |
|
|
|
pszModel = pItem->GetPlayerDisplayModel( m_iOldOwnerClass, nTeam ); |
|
} |
|
if ( pszModel && pszModel[0] ) |
|
{ |
|
if ( V_stricmp( STRING( GetModelName() ), pszModel ) != 0 ) |
|
{ |
|
if ( pItem->GetStaticData()->IsContentStreamable() ) |
|
{ |
|
modelinfo->RegisterDynamicModel( pszModel, IsClient() ); |
|
|
|
const char *pszModelAlt = pItem->GetStaticData()->GetPlayerDisplayModelAlt( m_iOldOwnerClass ); |
|
if ( pszModelAlt && pszModelAlt[0] ) |
|
{ |
|
modelinfo->RegisterDynamicModel( pszModelAlt, IsClient() ); |
|
} |
|
|
|
if ( pItem->GetVisionFilteredDisplayModel() && pItem->GetVisionFilteredDisplayModel()[ 0 ] != '\0' ) |
|
{ |
|
modelinfo->RegisterDynamicModel( pItem->GetVisionFilteredDisplayModel(), IsClient() ); |
|
} |
|
} |
|
|
|
SetModel( pszModel ); |
|
} |
|
} |
|
|
|
if ( GetModelPtr() && pItem->GetStaticData()->UsesPerClassBodygroups( GetTeamNumber() ) ) |
|
{ |
|
// Classes start at 1, bodygroups at 0, so we shift them all back 1. |
|
SetBodygroup( 1, (m_iOldOwnerClass-1) ); |
|
} |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::PlayAnimForPlaybackEvent( wearableanimplayback_t iPlayback ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( !pItem->IsValid() || !GetOwnerEntity() ) |
|
return; |
|
|
|
int iTeamNum = GetOwnerEntity()->GetTeamNumber(); |
|
int iActivities = pItem->GetStaticData()->GetNumPlaybackActivities( iTeamNum ); |
|
for ( int i = 0; i < iActivities; i++ ) |
|
{ |
|
activity_on_wearable_t *pData = pItem->GetStaticData()->GetPlaybackActivityData( iTeamNum, i ); |
|
if ( pData && pData->iPlayback == iPlayback && pData->pszActivity ) |
|
{ |
|
// If this is the first time we've tried to use it, find the activity |
|
if ( pData->iActivity == kActivityLookup_Unknown ) |
|
{ |
|
pData->iActivity = ActivityList_IndexForName( pData->pszActivity ); |
|
} |
|
|
|
int sequence = SelectWeightedSequence( (Activity)pData->iActivity ); |
|
if ( sequence != ACTIVITY_NOT_AVAILABLE ) |
|
{ |
|
ResetSequence( sequence ); |
|
SetCycle( 0 ); |
|
|
|
#if !defined( CLIENT_DLL ) |
|
if ( IsUsingClientSideAnimation() ) |
|
{ |
|
ResetClientsideFrame(); |
|
} |
|
#endif |
|
} |
|
return; |
|
} |
|
} |
|
} |
|
|
|
#endif // !CLIENT_DLL |
|
|
|
#if defined( TF_CLIENT_DLL ) |
|
// It's okay to draw attached entities with these models. |
|
const char* g_modelWhiteList[] = |
|
{ |
|
"models/weapons/w_models/w_toolbox.mdl", |
|
"models/weapons/w_models/w_sapper.mdl", |
|
|
|
// Canteens can change model based on the powerup type... all of these alternates are ok! |
|
"models/player/items/mvm_loot/all_class/mvm_flask_krit.mdl", |
|
"models/player/items/mvm_loot/all_class/mvm_flask_uber.mdl", |
|
"models/player/items/mvm_loot/all_class/mvm_flask_tele.mdl", |
|
"models/player/items/mvm_loot/all_class/mvm_flask_ammo.mdl", |
|
"models/player/items/mvm_loot/all_class/mvm_flask_build.mdl", |
|
|
|
TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL, |
|
|
|
"models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack.mdl", |
|
"models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack_open.mdl", |
|
|
|
}; |
|
|
|
#define HALLOWEEN_KART_MODEL "models/player/items/taunts/bumpercar/parts/bumpercar.mdl" |
|
#define HALLOWEEN_KART_CAGE_MODEL "models/props_halloween/bumpercar_cage.mdl" |
|
#endif |
|
|
|
#if defined( CLIENT_DLL ) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: TF prevents drawing of any entity attached to players that aren't items in the inventory of the player. |
|
// This is to prevent servers creating fake cosmetic items and attaching them to players. |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::ValidateEntityAttachedToPlayer( bool &bShouldRetry ) |
|
{ |
|
bShouldRetry = false; |
|
|
|
// We only use this variable in debug or on the client. |
|
#if defined( _DEBUG ) || defined( TF_CLIENT_DLL ) |
|
bool bItemDebugValidation = false; |
|
#endif // defined( _DEBUG ) || defined( TF_CLIENT_DLL ) |
|
|
|
#ifdef _DEBUG |
|
bItemDebugValidation = item_debug_validation.GetBool(); |
|
|
|
// Always valid in debug if item_debug_validation is disabled |
|
if ( !bItemDebugValidation ) |
|
return true; |
|
#endif // _DEBUG |
|
|
|
#if defined( TF_CLIENT_DLL ) |
|
// Always valid in item testing mode |
|
if ( TFGameRules()->IsInItemTestingMode() ) |
|
return true; |
|
|
|
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() ); |
|
|
|
// If we're not carried by a player, we're not valid. This prevents them |
|
// parenting hats to ents that they then parent to the player. |
|
if ( !pOwner ) |
|
{ |
|
//Msg( "NO OWNER SET! %i\n", m_iNumOwnerValidationRetries ); |
|
bShouldRetry = ( m_iNumOwnerValidationRetries < 500 ); |
|
m_iNumOwnerValidationRetries++; |
|
return false; |
|
} |
|
|
|
C_BaseEntity *pVM = pOwner->GetViewModel(); |
|
|
|
// The owner entity must also be a move parent of this entity. |
|
bool bPlayerIsParented = false; |
|
C_BaseEntity *pEntity = this; |
|
while ( (pEntity = pEntity->GetMoveParent()) != NULL ) |
|
{ |
|
if ( pOwner == pEntity || pVM == pEntity ) |
|
{ |
|
bPlayerIsParented = true; |
|
break; |
|
} |
|
} |
|
|
|
if ( !bPlayerIsParented ) |
|
{ |
|
//Msg( "NOT PARENTED! %i\n", m_iNumOwnerValidationRetries ); |
|
bShouldRetry = ( m_iNumOwnerValidationRetries < 500 ); |
|
m_iNumOwnerValidationRetries++; |
|
return false; |
|
} |
|
|
|
m_iNumOwnerValidationRetries = 0; |
|
|
|
// We only need this in debug (for item_debug_validation) or PvE mode |
|
bool bOwnerIsBot = pOwner->IsABot(); // THIS IS INSECURE -- DO NOT USE THIS OUTSIDE OF DEBUG OR PVE MODE |
|
|
|
// Allow bots to use anything in PvE mode |
|
if ( bOwnerIsBot && TFGameRules()->IsPVEModeActive() ) |
|
return true; |
|
|
|
int iClass = pOwner->GetPlayerClass()->GetClassIndex(); |
|
int iTeam = pOwner->GetTeamNumber(); |
|
|
|
// Allow all weapons parented to the local player |
|
if ( pOwner == C_BasePlayer::GetLocalPlayer() ) |
|
{ |
|
// They can change the owner entity, so we have to keep checking. |
|
bShouldRetry = true; |
|
return true; |
|
} |
|
|
|
// HACK: For now, if our owner is a disguised spy, we assume everything is valid. |
|
if ( (pOwner->m_Shared.InCond( TF_COND_DISGUISED ) || pOwner->m_Shared.InCond( TF_COND_DISGUISING )) && iClass == TF_CLASS_SPY ) |
|
{ |
|
bShouldRetry = true; // Keep checking in case the player switches class or becomes no longer disguised |
|
return true; |
|
} |
|
|
|
// If our owner is a disguised spy, we validate everything based |
|
// on the items carried by the person we're disguised as. |
|
/*if ( pOwner->m_Shared.InCond( TF_COND_DISGUISED ) ) |
|
{ |
|
// DAMN: This won't work. If our disguise target is a player we've never seen before, |
|
// we won't have a client entity, and hence we don't have their inventory. |
|
C_TFPlayer *pDisguiseTarget = ToTFPlayer( pOwner->m_Shared.GetDisguiseTarget() ); |
|
if ( pDisguiseTarget && pDisguiseTarget != pOwner ) |
|
{ |
|
pOwner = pDisguiseTarget; |
|
iClass = pOwner->GetPlayerClass()->GetClassIndex(); |
|
} |
|
else |
|
{ |
|
// We're not disguised as a specific player. Make sure we lookup base weapons with the disguise class. |
|
iClass = pOwner->m_Shared.GetDisguiseClass(); |
|
} |
|
} |
|
*/ |
|
|
|
#if defined(TF_DLL) || defined(TF_CLIENT_DLL) |
|
if ( m_bValidatedAttachedEntity ) |
|
return true; |
|
#endif // TF_DLL || TF_CLIENT_DLL |
|
|
|
const char *pszClientModel = modelinfo->GetModelName( GetModel() ); |
|
if ( pszClientModel && g_modelWhiteList[0] ) |
|
{ |
|
// Certain builder models are okay to have. |
|
for ( int i=0; i<ARRAYSIZE( g_modelWhiteList ); ++i ) |
|
{ |
|
if ( FStrEq( pszClientModel, g_modelWhiteList[i] ) ) |
|
return true; |
|
} |
|
} |
|
|
|
// Halloween Karts |
|
if ( pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) ) |
|
{ |
|
if ( FStrEq( pszClientModel, HALLOWEEN_KART_MODEL ) ) |
|
return true; |
|
|
|
if ( FStrEq( pszClientModel, HALLOWEEN_KART_CAGE_MODEL ) ) |
|
return true; |
|
} |
|
|
|
// If our player doesn't have an inventory, we're not valid. |
|
CTFPlayerInventory *pInv = pOwner->Inventory(); |
|
if ( !pInv ) |
|
return false; |
|
|
|
// check if this is a custom taunt prop |
|
if ( pOwner->m_Shared.InCond( TF_COND_TAUNTING ) ) |
|
{ |
|
const char* pszCustomTauntProp = NULL; |
|
int iClassTaunt = pOwner->GetPlayerClass()->GetClassIndex(); |
|
CEconItemView *pMiscItemView = pInv->GetItemInLoadout( iClassTaunt, pOwner->GetActiveTauntSlot() ); |
|
if ( pMiscItemView && pMiscItemView->IsValid() ) |
|
{ |
|
if ( pMiscItemView->GetStaticData()->GetTauntData() ) |
|
{ |
|
pszCustomTauntProp = pMiscItemView->GetStaticData()->GetTauntData()->GetProp( iClassTaunt ); |
|
if ( pszCustomTauntProp ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// If we've lost connection to the GC, let's just trust the server to avoid breaking the appearance for everyone. |
|
bool bSkipInventoryCheck = bItemDebugValidation && bOwnerIsBot; // will always be false in release builds |
|
if ( ( !pInv->GetSOC() || !pInv->GetSOC()->BIsInitialized() ) && !bSkipInventoryCheck ) |
|
{ |
|
bShouldRetry = true; |
|
return true; |
|
} |
|
|
|
CEconItemView *pScriptItem = GetAttributeContainer()->GetItem(); |
|
|
|
// If the item isn't valid, we're probably an extra wearable for another item. See if our model is |
|
// a model specified as the extra wearable for any of the items we have equipped. |
|
if ( !pScriptItem->IsValid() ) |
|
{ |
|
// Uninitialized client models return their model as '?' |
|
if ( pszClientModel && pszClientModel[0] != '?' ) |
|
{ |
|
CSteamID steamIDForPlayer; |
|
pOwner->GetSteamID( &steamIDForPlayer ); |
|
|
|
for ( int i = 0; i < CLASS_LOADOUT_POSITION_COUNT; i++ ) |
|
{ |
|
CEconItemView *pItem = TFInventoryManager()->GetItemInLoadoutForClass( iClass, i, &steamIDForPlayer ); |
|
if ( pItem && pItem->IsValid() ) |
|
{ |
|
const char *pszAttached = pItem->GetExtraWearableModel(); |
|
if ( pszAttached && pszAttached[0] ) |
|
{ |
|
if ( FStrEq( pszClientModel, pszAttached ) ) |
|
return true; |
|
} |
|
|
|
pszAttached = pItem->GetExtraWearableViewModel(); |
|
if ( pszAttached && pszAttached[0] ) |
|
{ |
|
if ( FStrEq( pszClientModel, pszAttached ) ) |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
else if ( pszClientModel && pszClientModel[0] == '?' ) |
|
{ |
|
bShouldRetry = true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// Skip this check for bots if item_debug_validation is enabled. |
|
if ( !pInv->GetInventoryItemByItemID( pScriptItem->GetItemID() ) && !bSkipInventoryCheck ) |
|
{ |
|
// If it's a base item, we allow it. |
|
CEconItemView *pBaseItem = TFInventoryManager()->GetBaseItemForClass( iClass, pScriptItem->GetStaticData()->GetLoadoutSlot(iClass) ); |
|
if ( *pScriptItem != *pBaseItem ) |
|
{ |
|
const wchar_t *pwzItemName = pScriptItem->GetItemName(); |
|
|
|
char szItemName[ MAX_ITEM_NAME_LENGTH ]; |
|
ILocalize::ConvertUnicodeToANSI( pwzItemName, szItemName, sizeof( szItemName ) ); |
|
|
|
#ifdef _DEBUG |
|
Warning("Item '%s' attached to %s, but it's not in his inventory.\n", szItemName, pOwner->GetPlayerName() ); |
|
#endif |
|
return false; |
|
} |
|
} |
|
|
|
// Our model has to match the model in our script |
|
const char *pszScriptModel = pScriptItem->GetWorldDisplayModel(); |
|
if ( !pszScriptModel ) |
|
{ |
|
pszScriptModel = pScriptItem->GetPlayerDisplayModel( iClass, iTeam ); |
|
} |
|
|
|
if ( pszClientModel && pszClientModel[0] && pszClientModel[0] != '?' ) |
|
{ |
|
// A model was set on the entity, let's make sure it matches the model in the script. |
|
if ( !pszScriptModel || !pszScriptModel[0] ) |
|
return false; |
|
|
|
if ( FStrEq( pszClientModel, pszScriptModel ) == false ) |
|
{ |
|
#if defined( STAGING_ONLY ) && defined _DEBUG |
|
CUtlString strScriptModel( pszScriptModel ); |
|
strScriptModel.FixSlashes(); |
|
CUtlString strClientModel( pszClientModel ); |
|
strClientModel.FixSlashes(); |
|
AssertMsg( strScriptModel != strClientModel, "Model path separator differs between script and client!" ); |
|
#endif // STAGING_ONLY |
|
// The regular model didn't work...let's try the Alt version if it exists |
|
const char *pszScriptModelAlt = pScriptItem->GetStaticData()->GetPlayerDisplayModelAlt( iClass ); |
|
if ( !pszScriptModelAlt || !pszScriptModelAlt[0] || ( FStrEq( pszClientModel, pszScriptModelAlt ) == false ) ) |
|
{ |
|
// The Alt model didn't work... let's try the vision filtered display model if it happens to exist |
|
pszScriptModel = pScriptItem->GetVisionFilteredDisplayModel(); |
|
if ( !pszScriptModel || !pszScriptModel[0] ) |
|
return false; |
|
|
|
if ( FStrEq( pszClientModel, pszScriptModel ) == false ) |
|
{ |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// The client model was not set, so check that there isn't a model set in the script either. |
|
if ( pszScriptModel && pszScriptModel[0] ) |
|
{ |
|
if ( pszClientModel[0] == '?' ) |
|
bShouldRetry = true; |
|
|
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set a material override for this entity via code |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::SetMaterialOverride( int team, const char *pszMaterial ) |
|
{ |
|
if ( team >= 0 && team < TEAM_VISUAL_SECTIONS ) |
|
{ |
|
m_MaterialOverrides[ team ].Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::SetMaterialOverride( int team, CMaterialReference &ref ) |
|
{ |
|
if ( team >= 0 && team < TEAM_VISUAL_SECTIONS ) |
|
{ |
|
m_MaterialOverrides[ team ].Init( ref ); |
|
} |
|
} |
|
|
|
|
|
// Deal with recording |
|
void CEconEntity::GetToolRecordingState( KeyValues *msg ) |
|
{ |
|
#ifndef _XBOX |
|
BaseClass::GetToolRecordingState( msg ); |
|
|
|
bool bUseOverride = ( GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS ) && m_MaterialOverrides[ GetTeamNumber() ].IsValid(); |
|
if ( bUseOverride ) |
|
{ |
|
msg->SetString( "materialOverride", m_MaterialOverrides[ GetTeamNumber() ]->GetName() ); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
#ifndef DOTA_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void C_ViewmodelAttachmentModel::SetOuter( CEconEntity *pOuter ) |
|
{ |
|
m_hOuter = pOuter; |
|
SetOwnerEntity( pOuter ); |
|
|
|
CEconItemView *pItem = pOuter->GetAttributeContainer()->GetItem(); |
|
if ( pItem->IsValid() ) |
|
{ |
|
m_bAlwaysFlip = pItem->GetStaticData()->ShouldFlipViewmodels(); |
|
} |
|
} |
|
|
|
bool C_ViewmodelAttachmentModel::InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup ) |
|
{ |
|
if ( !BaseClass::InitializeAsClientEntity( pszModelName, renderGroup ) ) |
|
return false; |
|
|
|
AddEffects( EF_BONEMERGE ); |
|
AddEffects( EF_BONEMERGE_FASTCULL ); |
|
|
|
// Invisible by default, and made visible->drawn->made invisible when the viewmodel is drawn |
|
AddEffects( EF_NODRAW ); |
|
return true; |
|
} |
|
|
|
int C_ViewmodelAttachmentModel::InternalDrawModel( int flags ) |
|
{ |
|
#ifdef TF_CLIENT_DLL |
|
CMatRenderContextPtr pRenderContext( materials ); |
|
if ( cl_flipviewmodels.GetBool() != m_bAlwaysFlip ) |
|
{ |
|
pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); |
|
} |
|
#endif |
|
int r = BaseClass::InternalDrawModel( flags ); |
|
|
|
#ifdef TF_CLIENT_DLL |
|
pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); |
|
#endif |
|
|
|
return r; |
|
} |
|
|
|
bool C_ViewmodelAttachmentModel::OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo ) |
|
{ |
|
if ( !BaseClass::OnPostInternalDrawModel( pInfo ) ) |
|
return false; |
|
|
|
if ( !m_hOuter ) |
|
return true; |
|
if ( !m_hOuter->GetAttributeContainer() ) |
|
return true; |
|
if ( !m_hOuter->GetAttributeContainer()->GetItem() ) |
|
return true; |
|
|
|
DrawEconEntityAttachedModels( this, GetOuter(), pInfo, kAttachedModelDisplayFlag_ViewModel ); |
|
return true; |
|
} |
|
|
|
void C_ViewmodelAttachmentModel::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask ) |
|
{ |
|
BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask ); |
|
|
|
// Will it blend? |
|
|
|
if ( !m_hOuter ) |
|
return; |
|
|
|
m_hOuter->ViewModelAttachmentBlending( hdr, pos, q, currentTime, boneMask ); |
|
} |
|
|
|
void FormatViewModelAttachment( Vector &vOrigin, bool bInverse ); |
|
void C_ViewmodelAttachmentModel::FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld ) |
|
{ |
|
Vector vecOrigin; |
|
MatrixPosition( attachmentToWorld, vecOrigin ); |
|
::FormatViewModelAttachment( vecOrigin, false ); |
|
PositionMatrix( vecOrigin, attachmentToWorld ); |
|
} |
|
int C_ViewmodelAttachmentModel::GetSkin( void ) |
|
{ |
|
if ( m_hOuter != NULL ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = m_hOuter->MyCombatWeaponPointer(); |
|
|
|
if ( pWeapon ) |
|
{ |
|
int nSkin = pWeapon->GetSkinOverride(); |
|
if ( nSkin != -1 ) |
|
{ |
|
return nSkin; |
|
} |
|
} |
|
else |
|
{ |
|
// some models like the Festive Targe don't have combat pointers but they still need to get the correct skin |
|
if ( m_hOuter->GetAttributeContainer() ) |
|
{ |
|
CEconItemView *pItem = m_hOuter->GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->IsValid() && GetOwnerViaInterface() ) |
|
{ |
|
return pItem->GetSkin( GetOwnerViaInterface()->GetTeamNumber(), true ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return BaseClass::GetSkin(); |
|
} |
|
|
|
#endif // !defined( DOTA_DLL ) |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::Release( void ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE ); |
|
|
|
// Remove all effects associated with this econ entity, not just turn them off |
|
C_BaseEntity *pEffectOwnerWM = this; |
|
C_BaseEntity *pEffectOwnerVM = NULL; |
|
|
|
bool bExtraWearable = false; |
|
bool bExtraWearableVM = false; |
|
|
|
CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>( this ); |
|
if ( pWeapon ) |
|
{ |
|
pEffectOwnerVM = pWeapon->GetPlayerOwner() ? pWeapon->GetPlayerOwner()->GetViewModel() : NULL; |
|
if ( pWeapon->m_hExtraWearable.Get() ) |
|
{ |
|
pEffectOwnerWM = pWeapon->m_hExtraWearable.Get(); |
|
bExtraWearable = true; |
|
} |
|
|
|
if ( pWeapon->m_hExtraWearableViewModel.Get() ) |
|
{ |
|
pEffectOwnerVM = pWeapon->m_hExtraWearableViewModel.Get(); |
|
bExtraWearableVM = true; |
|
} |
|
// always kill all effects for VM |
|
if ( pEffectOwnerVM ) |
|
{ |
|
pEffectOwnerVM->ParticleProp()->StopEmission( NULL, false, true ); |
|
} |
|
} |
|
|
|
pEffectOwnerWM->ParticleProp()->StopEmission( NULL, false, true ); |
|
|
|
#endif |
|
if ( m_hViewmodelAttachment ) |
|
{ |
|
m_hViewmodelAttachment->Release(); |
|
} |
|
|
|
BaseClass::Release(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::SetDormant( bool bDormant ) |
|
{ |
|
// If I'm burning, stop the burning sounds |
|
if ( !IsDormant() && bDormant && m_nParticleSystemsCreated != PARTICLE_SYSTEM_STATE_NOT_VISIBLE ) |
|
{ |
|
SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE ); |
|
} |
|
|
|
BaseClass::SetDormant( bDormant ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::OnPreDataChanged( DataUpdateType_t type ) |
|
{ |
|
BaseClass::OnPreDataChanged( type ); |
|
|
|
m_iOldTeam = m_iTeamNum; |
|
} |
|
|
|
IMaterial *CreateTempMaterialForPlayerLogo( int iPlayerIndex, player_info_t *info, char *texname, int nchars ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::OnDataChanged( DataUpdateType_t updateType ) |
|
{ |
|
// If we were just created, setup from the script files |
|
if ( updateType == DATA_UPDATE_CREATED ) |
|
{ |
|
InitializeAttributes(); |
|
m_nParticleSystemsCreated = PARTICLE_SYSTEM_STATE_NOT_VISIBLE; |
|
m_bAttachmentDirty = true; |
|
} |
|
|
|
BaseClass::OnDataChanged( updateType ); |
|
|
|
GetAttributeContainer()->OnDataChanged( updateType ); |
|
|
|
if ( updateType == DATA_UPDATE_CREATED ) |
|
{ |
|
CEconItemView *pItem = m_AttributeManager.GetItem(); |
|
|
|
#if defined(_DEBUG) |
|
if ( item_debug.GetBool() ) |
|
{ |
|
DebugDescribe(); |
|
} |
|
#endif |
|
|
|
// Find & cache for easy leaf code usage |
|
for ( int team = 0; team < TEAM_VISUAL_SECTIONS; team++ ) |
|
{ |
|
const char *pszMaterial = pItem->GetStaticData()->GetMaterialOverride( team ); |
|
if ( pszMaterial ) |
|
{ |
|
m_MaterialOverrides[team].Init( pszMaterial, TEXTURE_GROUP_CLIENT_EFFECTS ); |
|
} |
|
} |
|
|
|
#ifdef TF_CLIENT_DLL |
|
// If we're carried by a player, let him know he should recalc his bodygroups. |
|
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pPlayer ) |
|
{ |
|
pPlayer->SetBodygroupsDirty(); |
|
} |
|
|
|
//Warning("Forcing recalc of visiblity for %d\n", entindex()); |
|
m_bValidatedOwner = false; |
|
m_iNumOwnerValidationRetries = 0; |
|
UpdateVisibility(); |
|
#endif // TF_CLIENT_DLL |
|
} |
|
|
|
UpdateAttachmentModels(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::UpdateAttachmentModels( void ) |
|
{ |
|
#ifndef DOTA_DLL |
|
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 iTeamNumber = GetTeamNumber(); |
|
{ |
|
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 ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Update the state of attachment models for this item |
|
bool bItemNeedsAttachment = pItemDef && (pItemDef->ShouldAttachToHands() || pItemDef->ShouldAttachToHandsVMOnly()); |
|
if ( bItemNeedsAttachment ) |
|
{ |
|
bool bShouldShowAttachment = false; |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwnerEntity() ); |
|
if ( pOwner && !pOwner->ShouldDrawThisPlayer() ) |
|
{ |
|
// Drawing the viewmodel |
|
bShouldShowAttachment = true; |
|
} |
|
|
|
if ( bShouldShowAttachment && AttachmentModelsShouldBeVisible() ) |
|
{ |
|
if ( !m_hViewmodelAttachment ) |
|
{ |
|
C_BaseViewModel *vm = pOwner->GetViewModel( 0 ); |
|
if ( vm ) |
|
{ |
|
C_ViewmodelAttachmentModel *pEnt = new class C_ViewmodelAttachmentModel; |
|
if ( !pEnt ) |
|
return; |
|
|
|
pEnt->SetOuter( this ); |
|
|
|
int iClass = 0; |
|
#if defined( TF_DLL ) || defined( TF_CLIENT_DLL ) |
|
CTFPlayer *pTFPlayer = ToTFPlayer( pOwner ); |
|
if ( pTFPlayer ) |
|
{ |
|
iClass = pTFPlayer->GetPlayerClass()->GetClassIndex(); |
|
} |
|
#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL ) |
|
if ( pEnt->InitializeAsClientEntity( pItem->GetPlayerDisplayModel( iClass, pOwner->GetTeamNumber() ), RENDER_GROUP_VIEW_MODEL_OPAQUE ) == false ) |
|
return; |
|
|
|
m_hViewmodelAttachment = pEnt; |
|
m_hViewmodelAttachment->SetParent( vm ); |
|
m_hViewmodelAttachment->SetLocalOrigin( vec3_origin ); |
|
m_hViewmodelAttachment->UpdatePartitionListEntry(); |
|
m_hViewmodelAttachment->CollisionProp()->UpdatePartition(); |
|
m_hViewmodelAttachment->UpdateVisibility(); |
|
|
|
m_bAttachmentDirty = true; |
|
} |
|
} |
|
else if ( m_hViewmodelAttachment ) |
|
{ |
|
// If a player changes team, we may need to update the skin on the attachment weapon model |
|
if ( m_iOldTeam != m_iTeamNum ) |
|
{ |
|
m_bAttachmentDirty = true; |
|
} |
|
} |
|
|
|
// We can't pull data from the viewmodel until we're actually the active weapon. |
|
if ( m_bAttachmentDirty && m_hViewmodelAttachment ) |
|
{ |
|
pOwner = ToBasePlayer( GetOwnerEntity() ); |
|
C_BaseViewModel *vm = pOwner->GetViewModel( 0 ); |
|
if ( vm && vm->GetWeapon() == this ) |
|
{ |
|
m_hViewmodelAttachment->m_nSkin = vm->GetSkin(); |
|
m_bAttachmentDirty = false; |
|
} |
|
} |
|
return; |
|
} |
|
} |
|
|
|
// If we get here we shouldn't have an attachment. |
|
if ( m_hViewmodelAttachment ) |
|
{ |
|
m_hViewmodelAttachment->Release(); |
|
} |
|
|
|
#endif // !defined( DOTA_DLL ) |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::HasCustomParticleSystems( void ) const |
|
{ |
|
return m_bHasParticleSystems; |
|
} |
|
|
|
//-----------------------------------------------------------------z------------ |
|
// Purpose: Create / Destroy particle systems on this item as appropriate |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::UpdateParticleSystems( void ) |
|
{ |
|
if ( !HasCustomParticleSystems() ) |
|
return; |
|
|
|
ParticleSystemState_t nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE; |
|
if ( IsEffectActive( EF_NODRAW ) || !ShouldDraw() ) |
|
{ |
|
nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE; |
|
} |
|
else if ( !GetOwnerEntity() && !IsDormant() ) |
|
{ |
|
nVisible = PARTICLE_SYSTEM_STATE_VISIBLE; |
|
} |
|
else if ( GetOwnerEntity() && !GetOwnerEntity()->IsDormant() && GetOwnerEntity()->IsPlayer() && GetOwnerEntity()->IsAlive() ) |
|
{ |
|
nVisible = PARTICLE_SYSTEM_STATE_VISIBLE; |
|
} |
|
|
|
if ( nVisible == PARTICLE_SYSTEM_STATE_NOT_VISIBLE ) |
|
{ |
|
#if defined(TF_CLIENT_DLL) || defined(TF_DLL) |
|
// Make sure the entity we're attaching to is being drawn |
|
CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this ); |
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( pLocalPlayer && pLocalPlayer == GetOwnerEntity() && pLocalPlayer->GetViewModel() && pLocalPlayer->GetViewModel()->GetWeapon() == pWeapon && !C_BasePlayer::ShouldDrawLocalPlayer() ) |
|
{ |
|
nVisible = PARTICLE_SYSTEM_STATE_VISIBLE_VM; |
|
} |
|
#endif |
|
} |
|
|
|
if ( nVisible != PARTICLE_SYSTEM_STATE_NOT_VISIBLE && !ShouldDrawParticleSystems() ) |
|
{ |
|
nVisible = PARTICLE_SYSTEM_STATE_NOT_VISIBLE; |
|
} |
|
|
|
SetParticleSystemsVisible( nVisible ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::ShouldDrawParticleSystems( void ) |
|
{ |
|
#if defined(TF_CLIENT_DLL) || defined(TF_DLL) |
|
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pPlayer ) |
|
{ |
|
bool bStealthed = pPlayer->m_Shared.IsStealthed(); |
|
if ( bStealthed ) |
|
return false; |
|
bool bDisguised = pPlayer->m_Shared.InCond( TF_COND_DISGUISED ); |
|
if ( bDisguised ) |
|
{ |
|
CTFWeaponBase *pWeapon = dynamic_cast<CTFWeaponBase*>( this ); |
|
bool bDisguiseWeapon = pWeapon && pWeapon->m_bDisguiseWeapon; |
|
if ( !bDisguiseWeapon ) |
|
{ |
|
return false; |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
// Make sure the entity we're attaching to is being drawn |
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( pLocalPlayer ) |
|
{ |
|
C_BaseEntity *pEffectOwner = this; |
|
if ( pLocalPlayer == GetOwnerEntity() && pLocalPlayer->GetViewModel() && !C_BasePlayer::ShouldDrawLocalPlayer() ) |
|
{ |
|
pEffectOwner = pLocalPlayer->GetViewModel(); |
|
} |
|
|
|
if ( !pEffectOwner->ShouldDraw() ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) |
|
{ |
|
if ( !InternalFireEvent( origin, angles, event, options ) ) |
|
{ |
|
BaseClass::FireEvent( origin, angles, event, options ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) |
|
{ |
|
return InternalFireEvent( origin, angles, event, options ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::InternalFireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) |
|
{ |
|
switch( event ) |
|
{ |
|
case AE_CL_BODYGROUP_SET_VALUE_CMODEL_WPN: |
|
if ( m_hViewmodelAttachment ) |
|
{ |
|
// Translate it into a set bodygroup event on our attached weapon |
|
m_hViewmodelAttachment->FireEvent( origin, angles, AE_CL_BODYGROUP_SET_VALUE, options ); |
|
} |
|
return true; |
|
break; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Does this model use delayed flex weights? |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::UsesFlexDelayedWeights() |
|
{ |
|
return m_flFlexDelayedWeight != NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Rendering callback to allow the client to set up all the model specific flex weights |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) |
|
{ |
|
if ( GetModelPtr() && GetModelPtr()->numflexcontrollers() ) |
|
{ |
|
if ( IsEffectActive( EF_BONEMERGE ) && GetMoveParent() ) |
|
{ |
|
C_BaseFlex *pParentFlex = dynamic_cast<C_BaseFlex*>( GetMoveParent() ); |
|
if ( pParentFlex ) |
|
{ |
|
// BUGBUG: We have a bug with SetCustomModel that causes a disagreement between the studio header here and the one used in l_studio.cpp CModelRender::DrawModelExecute |
|
// So when we hit that case, let's not do any work because otherwise we'd crash since the array sizes (m_flFlexDelayedWeight vs pFlexWeights) don't match. |
|
// Note that this check is duplicated in C_BaseFlex::SetupLocalWeights. |
|
AssertMsg( nFlexWeightCount == m_cFlexDelayedWeight, "Disagreement between the number of flex weights. Do the studio headers match?" ); |
|
if ( nFlexWeightCount != m_cFlexDelayedWeight ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( pParentFlex->SetupGlobalWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ) ) |
|
{ |
|
// convert the flex controllers into actual flex values |
|
C_BaseFlex::RunFlexRules( GetModelPtr(), pFlexWeights ); |
|
|
|
// aim the eyes |
|
// SetViewTarget( hdr ); // FIXME: Not enough info yet |
|
|
|
// process local versions of the delay weights |
|
if ( pFlexDelayedWeights ) |
|
{ |
|
C_BaseFlex::RunFlexDelay( nFlexWeightCount, pFlexWeights, m_flFlexDelayedWeight, m_flFlexDelayTime ); |
|
memcpy( pFlexDelayedWeights, m_flFlexDelayedWeight, sizeof( float ) * nFlexWeightCount ); |
|
} |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ); |
|
return; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
static void cc_dump_particlemanifest() |
|
{ |
|
Msg("Dumping particle list:\n"); |
|
for ( int i = 0; i < g_pParticleSystemMgr->GetParticleSystemCount(); i++ ) |
|
{ |
|
const char *pParticleSystemName = g_pParticleSystemMgr->GetParticleSystemNameFromIndex(i); |
|
Msg(" %d: %s\n", i, pParticleSystemName ); |
|
} |
|
} |
|
static ConCommand dump_particlemanifest( "dump_particlemanifest", cc_dump_particlemanifest, "Dump the list of particles loaded.", FCVAR_CHEAT ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::GetEconParticleSystems( CUtlVector<const attachedparticlesystem_t *> *out_pvecParticleSystems ) const |
|
{ |
|
Assert( out_pvecParticleSystems ); |
|
|
|
const CEconItemView *pEconItemView = m_AttributeManager.GetItem(); |
|
if ( pEconItemView ) |
|
{ |
|
const GameItemDefinition_t *pItemDef = pEconItemView->GetStaticData(); |
|
|
|
// Count static particles included in the item definition -- these are things like |
|
// the kritzkrieg particles or the milk splash particles. |
|
const int iStaticParticleCount = pItemDef->GetNumAttachedParticles( GetTeamNumber() ); |
|
for ( int i = 0; i < iStaticParticleCount; i++ ) |
|
{ |
|
out_pvecParticleSystems->AddToTail( pItemDef->GetAttachedParticleData( GetTeamNumber(), i ) ); |
|
} |
|
|
|
// Do we have a particle effect that goes along with our specific quality? Self-made |
|
// and community items have a sparkle, for example. |
|
const int iQualityParticleType = pEconItemView->GetQualityParticleType(); |
|
if ( iQualityParticleType > 0 ) |
|
{ |
|
out_pvecParticleSystems->AddToTail( GetItemSchema()->GetAttributeControlledParticleSystem( iQualityParticleType ) ); |
|
} |
|
} |
|
|
|
// Do we have particle systems added on via static attributes (ie., pipe smoke)? |
|
// Note that this is functionally identical to the dynamic unusual particles. We don't support |
|
// having multiple attributes of the same type with independent values so we split these out |
|
// at a higher level, limiting ourself to one of each. |
|
int iStaticParticleEffect = 0; |
|
CALL_ATTRIB_HOOK_INT( iStaticParticleEffect, set_attached_particle_static ); |
|
if ( iStaticParticleEffect > 0 ) |
|
{ |
|
out_pvecParticleSystems->AddToTail( GetItemSchema()->GetAttributeControlledParticleSystem( iStaticParticleEffect ) ); |
|
} |
|
|
|
// Do we have particle systems added on dynamically (ie., unusuals?)? |
|
int iDynamicParticleEffect = 0; |
|
int iIsThrowableTrail = 0; |
|
CALL_ATTRIB_HOOK_INT( iDynamicParticleEffect, set_attached_particle ); |
|
CALL_ATTRIB_HOOK_INT( iIsThrowableTrail, throwable_particle_trail_only ); |
|
|
|
#if defined(TF_CLIENT_DLL) |
|
#ifdef STAGING_ONLY |
|
if ( pEconItemView ) |
|
{ |
|
const GameItemDefinition_t *pItemDef = pEconItemView->GetStaticData(); |
|
|
|
int iSlot = pItemDef->GetLoadoutSlot( 0 ); |
|
if ( unusual_force_weapon_effect.GetInt() > 0 ) |
|
{ |
|
if ( iSlot == LOADOUT_POSITION_PRIMARY || iSlot == LOADOUT_POSITION_SECONDARY || iSlot == LOADOUT_POSITION_MELEE ) |
|
{ |
|
iDynamicParticleEffect = unusual_force_weapon_effect.GetInt(); |
|
} |
|
} |
|
if ( unusual_force_cosmetic_effect.GetInt() > 0 ) |
|
{ |
|
if ( iSlot == LOADOUT_POSITION_MISC ) |
|
{ |
|
iDynamicParticleEffect = unusual_force_cosmetic_effect.GetInt(); |
|
} |
|
} |
|
} |
|
#endif |
|
#endif |
|
|
|
if ( iDynamicParticleEffect > 0 && !iIsThrowableTrail ) |
|
{ |
|
attachedparticlesystem_t *pSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iDynamicParticleEffect ); |
|
|
|
if ( pSystem ) |
|
{ |
|
#if defined(TF_CLIENT_DLL) || defined(TF_DLL) |
|
// TF Team Color Particles |
|
static char pszFullname[256]; |
|
if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pSystem->pszSystemName, "_teamcolor_red" )) |
|
{ |
|
V_StrSubst( pSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pszFullname, 256 ); |
|
pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pszFullname ); |
|
|
|
} |
|
else if ( GetTeamNumber() == TF_TEAM_RED && V_stristr( pSystem->pszSystemName, "_teamcolor_blue" )) |
|
{ |
|
// Guard against accidentally giving out the blue team color (support tool) |
|
V_StrSubst( pSystem->pszSystemName, "_teamcolor_blue", "_teamcolor_red", pszFullname, 256 ); |
|
pSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pszFullname ); |
|
} |
|
#endif |
|
if ( pSystem ) |
|
{ |
|
out_pvecParticleSystems->AddToTail( pSystem ); |
|
} |
|
} |
|
} |
|
|
|
// Scan the particle system |
|
// - Clean up our list in case we fed in bad data from the schema or wherever. |
|
for ( int i = out_pvecParticleSystems->Count() - 1; i >= 0; i-- ) |
|
{ |
|
if ( !(*out_pvecParticleSystems)[i] || |
|
!(*out_pvecParticleSystems)[i]->pszSystemName || |
|
!(*out_pvecParticleSystems)[i]->pszSystemName[0] ) |
|
{ |
|
out_pvecParticleSystems->FastRemove( i ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::SetParticleSystemsVisible( ParticleSystemState_t nState ) |
|
{ |
|
if ( nState == m_nParticleSystemsCreated ) |
|
{ |
|
bool bDirty = false; |
|
|
|
#if defined(TF_CLIENT_DLL) || defined(TF_DLL) |
|
CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this ); |
|
if ( pWeapon ) |
|
{ |
|
if ( pWeapon->m_hExtraWearable.Get() ) |
|
{ |
|
bDirty = !( pWeapon->m_hExtraWearable->m_nParticleSystemsCreated == nState ); |
|
pWeapon->m_hExtraWearable->m_nParticleSystemsCreated = nState; |
|
} |
|
|
|
if ( pWeapon->m_hExtraWearableViewModel.Get() ) |
|
{ |
|
bDirty = !( pWeapon->m_hExtraWearableViewModel->m_nParticleSystemsCreated == nState ); |
|
pWeapon->m_hExtraWearableViewModel->m_nParticleSystemsCreated = nState; |
|
} |
|
} |
|
#endif |
|
|
|
if ( !bDirty ) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
CUtlVector<const attachedparticlesystem_t *> vecParticleSystems; |
|
GetEconParticleSystems( &vecParticleSystems ); |
|
|
|
FOR_EACH_VEC( vecParticleSystems, i ) |
|
{ |
|
const attachedparticlesystem_t *pSystem = vecParticleSystems[i]; |
|
Assert( pSystem ); |
|
Assert( pSystem->pszSystemName ); |
|
Assert( pSystem->pszSystemName[0] ); |
|
|
|
// Ignore custom particles. Weapons handle them in custom fashions. |
|
if ( pSystem->iCustomType ) |
|
continue; |
|
|
|
UpdateSingleParticleSystem( nState != PARTICLE_SYSTEM_STATE_NOT_VISIBLE, pSystem ); |
|
} |
|
|
|
m_nParticleSystemsCreated = nState; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CEconEntity::UpdateSingleParticleSystem( bool bVisible, const attachedparticlesystem_t *pSystem ) |
|
{ |
|
Assert( pSystem ); |
|
|
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( !pLocalPlayer ) |
|
return; |
|
|
|
C_BaseEntity *pEffectOwnerWM = this; |
|
C_BaseEntity *pEffectOwnerVM = NULL; |
|
|
|
bool bExtraWearable = false; |
|
bool bExtraWearableVM = false; |
|
|
|
CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( this ); |
|
if ( pWeapon ) |
|
{ |
|
pEffectOwnerVM = pWeapon->GetPlayerOwner() ? pWeapon->GetPlayerOwner()->GetViewModel() : NULL; |
|
if ( pWeapon->m_hExtraWearable.Get() ) |
|
{ |
|
pEffectOwnerWM = pWeapon->m_hExtraWearable.Get(); |
|
bExtraWearable = true; |
|
} |
|
|
|
if ( pWeapon->m_hExtraWearableViewModel.Get() ) |
|
{ |
|
pEffectOwnerVM = pWeapon->m_hExtraWearableViewModel.Get(); |
|
bExtraWearableVM = true; |
|
} |
|
} |
|
|
|
C_BaseEntity *pEffectOwner = pEffectOwnerWM; |
|
bool bIsVM = false; |
|
C_BasePlayer *pOwner = ToBasePlayer(GetOwnerEntity()); |
|
bool bDrawThisEffect = true; |
|
if ( !pOwner->ShouldDrawThisPlayer() ) |
|
{ |
|
// only draw effects designated for this |
|
if ( !pSystem->bDrawInViewModel ) |
|
{ |
|
bDrawThisEffect = false; |
|
} |
|
|
|
C_BaseViewModel *pLocalPlayerVM = pLocalPlayer->GetViewModel(); |
|
if ( pLocalPlayerVM && pLocalPlayerVM->GetOwningWeapon() == this ) |
|
{ |
|
bIsVM = true; |
|
pEffectOwner = pEffectOwnerVM; |
|
} |
|
} |
|
|
|
const char *pszAttachmentName = pSystem->pszControlPoints[0]; |
|
if ( bIsVM && bExtraWearableVM ) |
|
pszAttachmentName = "attach_fob_v"; |
|
if ( !bIsVM && bExtraWearable ) |
|
pszAttachmentName = "attach_fob"; |
|
|
|
int iAttachment = INVALID_PARTICLE_ATTACHMENT; |
|
if ( pszAttachmentName && pszAttachmentName[0] && pEffectOwner->GetBaseAnimating() ) |
|
{ |
|
iAttachment = pEffectOwner->GetBaseAnimating()->LookupAttachment( pszAttachmentName ); |
|
} |
|
|
|
// Stop it on both the viewmodel & the world model, because it may be removed due to first/thirdperson switch |
|
// Get Full name |
|
const CEconItemView *pEconItemView = m_AttributeManager.GetItem(); |
|
static char pszTempName[256]; |
|
static char pszTempNameVM[256]; |
|
const char* pszSystemName = pSystem->pszSystemName; |
|
|
|
|
|
// Weapon Remap for a Base Effect to be used on a specific weapon |
|
if ( pSystem->bUseSuffixName && pEconItemView && pEconItemView->GetItemDefinition()->GetParticleSuffix() ) |
|
{ |
|
V_strcpy_safe( pszTempName, pszSystemName ); |
|
V_strcat_safe( pszTempName, "_" ); |
|
V_strcat_safe( pszTempName, pEconItemView->GetItemDefinition()->GetParticleSuffix() ); |
|
pszSystemName = pszTempName; |
|
} |
|
|
|
if ( pSystem->bHasViewModelSpecificEffect ) |
|
{ |
|
V_strcpy_safe( pszTempNameVM, pszSystemName ); |
|
V_strcat_safe( pszTempNameVM, "_vm" ); |
|
|
|
// VM doesnt exist so fall back to regular |
|
if ( g_pParticleSystemMgr->FindParticleSystem( pszTempNameVM ) == NULL ) |
|
{ |
|
V_strcpy_safe( pszTempNameVM, pszSystemName ); |
|
} |
|
|
|
if ( bIsVM ) |
|
{ |
|
pszSystemName = pszTempNameVM; |
|
} |
|
} |
|
|
|
// Check that the effect is valid |
|
if ( g_pParticleSystemMgr->FindParticleSystem( pszSystemName ) == NULL ) |
|
return; |
|
|
|
if ( iAttachment != INVALID_PARTICLE_ATTACHMENT ) |
|
{ |
|
pEffectOwnerWM->ParticleProp()->StopParticlesWithNameAndAttachment( pszSystemName, iAttachment, true ); |
|
|
|
if ( pEffectOwnerVM ) |
|
{ |
|
if ( pSystem->bHasViewModelSpecificEffect ) |
|
{ |
|
pEffectOwnerVM->ParticleProp()->StopParticlesWithNameAndAttachment( pszTempNameVM, iAttachment, true ); |
|
} |
|
pEffectOwnerVM->ParticleProp()->StopParticlesWithNameAndAttachment( pszSystemName, iAttachment, true ); |
|
} |
|
} |
|
else |
|
{ |
|
pEffectOwnerWM->ParticleProp()->StopParticlesNamed( pszSystemName, true ); |
|
|
|
if ( pEffectOwnerVM ) |
|
{ |
|
if ( pSystem->bHasViewModelSpecificEffect ) |
|
{ |
|
pEffectOwnerVM->ParticleProp()->StopParticlesNamed( pszTempNameVM, true ); |
|
} |
|
pEffectOwnerVM->ParticleProp()->StopParticlesNamed( pszSystemName, true ); |
|
} |
|
} |
|
|
|
if ( !bDrawThisEffect ) |
|
return; |
|
|
|
// do not generate a viewmodel effect if there is no weapon else it is in your face |
|
if ( !pWeapon && bIsVM ) |
|
{ |
|
Assert( 0 ); |
|
Warning( "Cannot create a Viewmodel Particle Effect [%s] when there is no Viewmodel Weapon", pszSystemName ); |
|
return; |
|
} |
|
|
|
if ( bVisible && pEffectOwner ) |
|
{ |
|
HPARTICLEFFECT pEffect = NULL; |
|
// We can't have fastcull on if we want particles attached to us |
|
//if ( !bIsVM ) |
|
{ |
|
RemoveEffects( EF_BONEMERGE_FASTCULL ); |
|
} |
|
|
|
if ( iAttachment != INVALID_PARTICLE_ATTACHMENT ) |
|
{ |
|
pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_POINT_FOLLOW, pszAttachmentName ); |
|
} |
|
else |
|
{ |
|
// Attachments can fall back to following root bones if the attachment point wasn't found |
|
if ( pSystem->bFollowRootBone ) |
|
{ |
|
pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_ROOTBONE_FOLLOW ); |
|
} |
|
else |
|
{ |
|
pEffect = pEffectOwner->ParticleProp()->Create( pszSystemName, PATTACH_ABSORIGIN_FOLLOW ); |
|
} |
|
} |
|
|
|
if ( pEffect ) |
|
{ |
|
// update the control points if necessary |
|
for ( int i=1; i<ARRAYSIZE( pSystem->pszControlPoints ); ++i ) |
|
{ |
|
const char *pszControlPointName = pSystem->pszControlPoints[i]; |
|
if ( pszControlPointName && pszControlPointName[0] != '\0' ) |
|
{ |
|
pEffectOwner->ParticleProp()->AddControlPoint( pEffect, i, this, PATTACH_POINT_FOLLOW, pszControlPointName ); |
|
} |
|
} |
|
|
|
if ( bIsVM ) |
|
{ |
|
pEffect->SetIsViewModelEffect( true ); |
|
ClientLeafSystem()->SetRenderGroup( pEffect->RenderHandle(), RENDER_GROUP_VIEW_MODEL_TRANSLUCENT ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::InitializeAsClientEntity( const char *pszModelName, RenderGroup_t renderGroup ) |
|
{ |
|
m_bClientside = true; |
|
return BaseClass::InitializeAsClientEntity( pszModelName, renderGroup ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get an econ material override for the given team. |
|
// Returns: NULL if there is no override. |
|
//----------------------------------------------------------------------------- |
|
IMaterial* CEconEntity::GetEconWeaponMaterialOverride( int iTeam ) |
|
{ |
|
if ( iTeam >= 0 && iTeam < TEAM_VISUAL_SECTIONS && m_MaterialOverrides[ iTeam ].IsValid() ) |
|
return m_MaterialOverrides[ iTeam ]; |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
bool CEconEntity::ShouldDraw() |
|
{ |
|
if ( ShouldHideForVisionFilterFlags() ) |
|
{ |
|
return false; |
|
} |
|
|
|
return BaseClass::ShouldDraw(); |
|
} |
|
|
|
bool CEconEntity::ShouldHideForVisionFilterFlags( void ) |
|
{ |
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
if ( pItem && pItem->IsValid() ) |
|
{ |
|
CEconItemDefinition *pData = pItem->GetStaticData(); |
|
if ( pData ) |
|
{ |
|
int nVisionFilterFlags = pData->GetVisionFilterFlags(); |
|
if ( nVisionFilterFlags != 0 ) |
|
{ |
|
// Only visible if the local player has an item that allows them to see it (Pyro Goggles) |
|
if ( !IsLocalPlayerUsingVisionFilterFlags( nVisionFilterFlags, true ) ) |
|
{ |
|
// They didn't have the correct vision flags |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CEconEntity::IsTransparent( void ) |
|
{ |
|
#ifdef TF_CLIENT_DLL |
|
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() ); |
|
if ( pPlayer ) |
|
{ |
|
return pPlayer->IsTransparent(); |
|
} |
|
#endif // TF_CLIENT_DLL |
|
|
|
return BaseClass::IsTransparent(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::ViewModel_IsTransparent( void ) |
|
{ |
|
if ( m_hViewmodelAttachment != NULL && m_hViewmodelAttachment->IsTransparent() ) |
|
{ |
|
return true; |
|
} |
|
return IsTransparent(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::ViewModel_IsUsingFBTexture( void ) |
|
{ |
|
if ( m_hViewmodelAttachment != NULL && m_hViewmodelAttachment->UsesPowerOfTwoFrameBufferTexture() ) |
|
{ |
|
return true; |
|
} |
|
return UsesPowerOfTwoFrameBufferTexture(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::IsOverridingViewmodel( void ) |
|
{ |
|
bool bUseOverride = (GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS) && m_MaterialOverrides[GetTeamNumber()].IsValid(); |
|
bUseOverride = bUseOverride || (m_hViewmodelAttachment != NULL) || ( m_AttributeManager.GetItem()->GetStaticData()->GetNumAttachedModels( GetTeamNumber() ) > 0 ); |
|
return bUseOverride; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CEconEntity::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags ) |
|
{ |
|
int ret = 0; |
|
#ifndef DOTA_DLL |
|
bool bIsAttachmentTranslucent = m_hViewmodelAttachment.Get() ? m_hViewmodelAttachment->IsTransparent() : false; |
|
bool bUseOverride = false; |
|
|
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); |
|
bool bAttachesToHands = ( pItem->IsValid() && (pItem->GetStaticData()->ShouldAttachToHands() || pItem->GetStaticData()->ShouldAttachToHandsVMOnly())); |
|
|
|
// If the attachment is translucent, we need to render the viewmodel first |
|
if ( bIsAttachmentTranslucent ) |
|
{ |
|
ret = pViewmodel->DrawOverriddenViewmodel( flags ); |
|
} |
|
|
|
if ( flags & STUDIO_RENDER ) |
|
{ |
|
// If there is some other material override, it's probably the client asking for us to render invuln or the |
|
// spy cloaking. Those are way more important than ours, so do them instead. |
|
IMaterial* pOverrideMaterial = NULL; |
|
OverrideType_t nDontcare = OVERRIDE_NORMAL; |
|
modelrender->GetMaterialOverride( &pOverrideMaterial, &nDontcare ); |
|
bool bIgnoreOverride = pOverrideMaterial != NULL; |
|
|
|
bUseOverride = !bIgnoreOverride && (GetTeamNumber() >= 0 && GetTeamNumber() < TEAM_VISUAL_SECTIONS) && m_MaterialOverrides[GetTeamNumber()].IsValid(); |
|
if ( bUseOverride ) |
|
{ |
|
modelrender->ForcedMaterialOverride( m_MaterialOverrides[GetTeamNumber()] ); |
|
flags |= STUDIO_NO_OVERRIDE_FOR_ATTACH; |
|
} |
|
|
|
if ( m_hViewmodelAttachment ) |
|
{ |
|
m_hViewmodelAttachment->RemoveEffects( EF_NODRAW ); |
|
m_hViewmodelAttachment->DrawModel( flags ); |
|
m_hViewmodelAttachment->AddEffects( EF_NODRAW ); |
|
} |
|
|
|
// if we are attached to the hands, then we DO NOT want have an override material when we draw our view model |
|
if ( bAttachesToHands && bUseOverride ) |
|
{ |
|
modelrender->ForcedMaterialOverride( NULL ); |
|
bUseOverride = false; |
|
} |
|
} |
|
|
|
if ( !bIsAttachmentTranslucent ) |
|
{ |
|
ret = pViewmodel->DrawOverriddenViewmodel( flags ); |
|
} |
|
|
|
if ( bUseOverride ) |
|
{ |
|
modelrender->ForcedMaterialOverride( NULL ); |
|
} |
|
#endif // !defined( DOTA_DLL ) |
|
return ret; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::OnInternalDrawModel( ClientModelRenderInfo_t *pInfo ) |
|
{ |
|
if ( !BaseClass::OnInternalDrawModel( pInfo ) ) |
|
return false; |
|
|
|
DrawEconEntityAttachedModels( this, this, pInfo, kAttachedModelDisplayFlag_WorldModel ); |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CEconEntity::LookupAttachment( const char *pAttachmentName ) |
|
{ |
|
if ( m_hViewmodelAttachment ) |
|
return m_hViewmodelAttachment->LookupAttachment( pAttachmentName ); |
|
|
|
return BaseClass::LookupAttachment( pAttachmentName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::GetAttachment( int number, matrix3x4_t &matrix ) |
|
{ |
|
if ( m_hViewmodelAttachment ) |
|
return m_hViewmodelAttachment->GetAttachment( number, matrix ); |
|
|
|
return BaseClass::GetAttachment( number, matrix ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::GetAttachment( int number, Vector &origin ) |
|
{ |
|
if ( m_hViewmodelAttachment ) |
|
return m_hViewmodelAttachment->GetAttachment( number, origin ); |
|
|
|
return BaseClass::GetAttachment( number, origin ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::GetAttachment( int number, Vector &origin, QAngle &angles ) |
|
{ |
|
if ( m_hViewmodelAttachment ) |
|
return m_hViewmodelAttachment->GetAttachment( number, origin, angles ); |
|
|
|
return BaseClass::GetAttachment( number, origin, angles ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel ) |
|
{ |
|
if ( m_hViewmodelAttachment ) |
|
return m_hViewmodelAttachment->GetAttachmentVelocity( number, originVel, angleVel ); |
|
|
|
return BaseClass::GetAttachmentVelocity( number, originVel, angleVel ); |
|
} |
|
|
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Hides or shows masked bodygroups associated with this item. |
|
//----------------------------------------------------------------------------- |
|
bool CEconEntity::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState ) |
|
{ |
|
if ( !pOwner ) |
|
return false; |
|
|
|
CAttributeContainer *pCont = GetAttributeContainer(); |
|
if ( !pCont ) |
|
return false; |
|
|
|
CEconItemView *pItem = pCont->GetItem(); |
|
if ( !pItem ) |
|
return false; |
|
|
|
const CEconItemDefinition *pItemDef = pItem->GetItemDefinition(); |
|
if ( !pItemDef ) |
|
return false; |
|
|
|
int iNumBodyGroups = pItemDef->GetNumModifiedBodyGroups( 0 ); |
|
for ( int i=0; i<iNumBodyGroups; ++i ) |
|
{ |
|
int iBody = 0; |
|
const char *pszBodyGroup = pItemDef->GetModifiedBodyGroup( 0, i, iBody ); |
|
if ( iBody != iState ) |
|
continue; |
|
|
|
int iBodyGroup = pOwner->FindBodygroupByName( pszBodyGroup ); |
|
|
|
if ( iBodyGroup == -1 ) |
|
continue; |
|
|
|
pOwner->SetBodygroup( iBodyGroup, iState ); |
|
} |
|
|
|
// Handle per-style bodygroup hiding |
|
const CEconStyleInfo *pStyle = pItemDef->GetStyleInfo( pItem->GetStyle() ); |
|
if ( pStyle ) |
|
{ |
|
FOR_EACH_VEC( pStyle->GetAdditionalHideBodygroups(), i ) |
|
{ |
|
int iBodyGroup = pOwner->FindBodygroupByName( pStyle->GetAdditionalHideBodygroups()[i] ); |
|
|
|
if ( iBodyGroup == -1 ) |
|
continue; |
|
|
|
pOwner->SetBodygroup( iBodyGroup, iState ); |
|
} |
|
|
|
// should we override this model bodygroup |
|
if ( pStyle->GetBodygroupName() != NULL ) |
|
{ |
|
int iBodyGroup = pOwner->FindBodygroupByName( pStyle->GetBodygroupName() ); |
|
if ( iBodyGroup != -1 ) |
|
{ |
|
SetBodygroup( iBodyGroup, pStyle->GetBodygroupSubmodelIndex() ); |
|
} |
|
} |
|
} |
|
|
|
// Handle world model bodygroup overrides |
|
int iBodyOverride = pItemDef->GetWorldmodelBodygroupOverride( pOwner->GetTeamNumber() ); |
|
int iBodyStateOverride = pItemDef->GetWorldmodelBodygroupStateOverride( pOwner->GetTeamNumber() ); |
|
if ( iBodyOverride > -1 && iBodyStateOverride > -1 ) |
|
{ |
|
pOwner->SetBodygroup( iBodyOverride, iBodyStateOverride ); |
|
} |
|
|
|
// Handle view model bodygroup overrides |
|
iBodyOverride = pItemDef->GetViewmodelBodygroupOverride( pOwner->GetTeamNumber() ); |
|
iBodyStateOverride = pItemDef->GetViewmodelBodygroupStateOverride( pOwner->GetTeamNumber() ); |
|
if ( iBodyOverride > -1 && iBodyStateOverride > -1 ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( pOwner ); |
|
if ( pPlayer ) |
|
{ |
|
CBaseViewModel *pVM = pPlayer->GetViewModel(); |
|
if ( pVM && pVM->GetModelPtr() ) |
|
{ |
|
pVM->SetBodygroup( iBodyOverride, iBodyStateOverride ); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CBaseAttributableItem::CBaseAttributableItem() |
|
{ |
|
}
|
|
|