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.
861 lines
23 KiB
861 lines
23 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "econ_wearable.h"
|
||
|
#include "vcollide_parse.h"
|
||
|
|
||
|
#ifdef CLIENT_DLL
|
||
|
#include "functionproxy.h"
|
||
|
#include "c_te_effect_dispatch.h"
|
||
|
#endif // CLIENT_DLL
|
||
|
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
#include "c_team.h"
|
||
|
#include "tf_shareddefs.h"
|
||
|
#include "tf_weapon_jar.h"
|
||
|
#include "c_tf_player.h"
|
||
|
#endif // TF_CLIENT_DLL
|
||
|
|
||
|
#ifdef TF_DLL
|
||
|
#include "tf_player.h"
|
||
|
#endif // TF_DLL
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( wearable_item, CEconWearable );
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( EconWearable, DT_WearableItem )
|
||
|
|
||
|
// Network Table --
|
||
|
BEGIN_NETWORK_TABLE( CEconWearable, DT_WearableItem )
|
||
|
END_NETWORK_TABLE()
|
||
|
// -- Network Table
|
||
|
|
||
|
// Data Desc --
|
||
|
BEGIN_DATADESC( CEconWearable )
|
||
|
END_DATADESC()
|
||
|
// -- Data Desc
|
||
|
|
||
|
PRECACHE_REGISTER( wearable_item );
|
||
|
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableItem, DT_TFWearableItem )
|
||
|
|
||
|
// Network Table --
|
||
|
BEGIN_NETWORK_TABLE( CTFWearableItem, DT_TFWearableItem )
|
||
|
END_NETWORK_TABLE()
|
||
|
// -- Network Table
|
||
|
|
||
|
// Data Desc --
|
||
|
BEGIN_DATADESC( CTFWearableItem )
|
||
|
END_DATADESC()
|
||
|
// -- Data Desc
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// SHARED CODE
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
CEconWearable::CEconWearable()
|
||
|
{
|
||
|
m_bAlwaysAllow = false;
|
||
|
};
|
||
|
|
||
|
void CEconWearable::InternalSetPlayerDisplayModel( void )
|
||
|
{
|
||
|
int iClass = 0;
|
||
|
int iTeam = 0;
|
||
|
|
||
|
#if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
|
||
|
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
|
||
|
if ( pTFPlayer )
|
||
|
{
|
||
|
iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
|
||
|
iTeam = pTFPlayer->GetTeamNumber();
|
||
|
}
|
||
|
#endif // defined( TF_DLL ) || defined( TF_CLIENT_DLL )
|
||
|
|
||
|
// Set our model to the player model
|
||
|
CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||
|
if ( pItem && pItem->IsValid() )
|
||
|
{
|
||
|
const char *pszPlayerDisplayModel = pItem->GetPlayerDisplayModel( iClass, iTeam );
|
||
|
if ( pszPlayerDisplayModel )
|
||
|
{
|
||
|
if ( pItem->GetStaticData()->IsContentStreamable() )
|
||
|
{
|
||
|
modelinfo->RegisterDynamicModel( pszPlayerDisplayModel, IsClient() );
|
||
|
|
||
|
if ( pItem->GetVisionFilteredDisplayModel() && pItem->GetVisionFilteredDisplayModel()[ 0 ] != '\0' )
|
||
|
{
|
||
|
modelinfo->RegisterDynamicModel( pItem->GetVisionFilteredDisplayModel(), IsClient() );
|
||
|
}
|
||
|
}
|
||
|
SetModel( pszPlayerDisplayModel );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set up the item. GC data may not be available here depending on
|
||
|
// where we're called from.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::Spawn( void )
|
||
|
{
|
||
|
InitializeAttributes();
|
||
|
|
||
|
Precache();
|
||
|
|
||
|
InternalSetPlayerDisplayModel();
|
||
|
|
||
|
BaseClass::Spawn();
|
||
|
|
||
|
AddEffects( EF_BONEMERGE );
|
||
|
AddEffects( EF_BONEMERGE_FASTCULL );
|
||
|
|
||
|
#if !defined( CLIENT_DLL )
|
||
|
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
||
|
SetBlocksLOS( false );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Player touches the item. Currently wearables don't appear in the
|
||
|
// world, so this is only called directly during equipment assignment.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::GiveTo( CBaseEntity *pOther )
|
||
|
{
|
||
|
CBasePlayer *pPlayer = ToBasePlayer(pOther);
|
||
|
if ( !pPlayer )
|
||
|
return;
|
||
|
|
||
|
#if !defined( CLIENT_DLL )
|
||
|
pPlayer->EquipWearable( this );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::RemoveFrom( CBaseEntity *pOther )
|
||
|
{
|
||
|
CBasePlayer *pPlayer = ToBasePlayer(pOther);
|
||
|
if ( !pPlayer )
|
||
|
return;
|
||
|
|
||
|
#if !defined( CLIENT_DLL )
|
||
|
pPlayer->RemoveWearable( this );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CEconWearable::GetSkin( void )
|
||
|
{
|
||
|
CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call.
|
||
|
if ( pItem )
|
||
|
{
|
||
|
int iSkin = pItem->GetSkin( GetTeamNumber() );
|
||
|
if ( iSkin > -1 )
|
||
|
{
|
||
|
return iSkin;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ( GetTeamNumber() == (LAST_SHARED_TEAM+1) ) ? 0 : 1;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Attaches the item to the player.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::Equip( CBasePlayer* pOwner )
|
||
|
{
|
||
|
if ( !CanEquip( pOwner ) )
|
||
|
{
|
||
|
RemoveFrom( pOwner );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SetTouch( NULL );
|
||
|
SetAbsVelocity( vec3_origin );
|
||
|
|
||
|
CBaseEntity *pFollowEntity = pOwner;
|
||
|
|
||
|
if ( IsViewModelWearable() )
|
||
|
{
|
||
|
pFollowEntity = pOwner->GetViewModel();
|
||
|
}
|
||
|
|
||
|
FollowEntity( pFollowEntity, true );
|
||
|
|
||
|
SetOwnerEntity( pOwner );
|
||
|
|
||
|
ReapplyProvision();
|
||
|
|
||
|
ChangeTeam( pOwner->GetTeamNumber() );
|
||
|
m_nSkin = GetSkin();
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
UpdateModelToClass();
|
||
|
UpdateBodygroups( pOwner, true );
|
||
|
PlayAnimForPlaybackEvent( WAP_ON_SPAWN );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Remove item from the player.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::UnEquip( CBasePlayer* pOwner )
|
||
|
{
|
||
|
#ifdef CLIENT_DLL
|
||
|
SetParticleSystemsVisible( PARTICLE_SYSTEM_STATE_NOT_VISIBLE );
|
||
|
#endif
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
UpdateBodygroups( pOwner, false );
|
||
|
#endif
|
||
|
|
||
|
StopFollowingEntity();
|
||
|
SetOwnerEntity( NULL );
|
||
|
|
||
|
ReapplyProvision();
|
||
|
}
|
||
|
/*
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Hides or shows masked bodygroups associated with this item.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CEconWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
|
||
|
{
|
||
|
if ( !pOwner )
|
||
|
return false;
|
||
|
|
||
|
CAttributeContainer *pCont = GetAttributeContainer();
|
||
|
if ( !pCont )
|
||
|
return false;
|
||
|
|
||
|
CEconItemView *pItem = pCont->GetItem();
|
||
|
if ( !pItem )
|
||
|
return false;
|
||
|
|
||
|
int iTeam = pOwner->GetTeamNumber();
|
||
|
int iNumBodyGroups = pItem->GetNumModifiedBodyGroups( iTeam );
|
||
|
for ( int i=0; i<iNumBodyGroups; ++i )
|
||
|
{
|
||
|
int iBody = 0;
|
||
|
const char *pszBodyGroup = pItem->GetModifiedBodyGroup( iTeam, i, iBody );
|
||
|
int iBodyGroup = pOwner->FindBodygroupByName( pszBodyGroup );
|
||
|
|
||
|
if ( iBodyGroup == -1 )
|
||
|
continue;
|
||
|
|
||
|
pOwner->SetBodygroup( iBodyGroup, iState );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
*/
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::OnWearerDeath( void )
|
||
|
{
|
||
|
#ifdef CLIENT_DLL
|
||
|
UpdateParticleSystems();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CEconWearable::GetDropType()
|
||
|
{
|
||
|
CAttributeContainer *pCont = GetAttributeContainer();
|
||
|
if ( !pCont )
|
||
|
return 0;
|
||
|
|
||
|
CEconItemView *pItem = pCont->GetItem();
|
||
|
if ( pItem )
|
||
|
return pItem->GetDropType();
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Ensures that a player's correct body groups are enabled on client respawn.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::UpdateWearableBodyGroups( CBasePlayer* pPlayer )
|
||
|
{
|
||
|
if ( !pPlayer )
|
||
|
return;
|
||
|
|
||
|
for ( int i=0; i<pPlayer->GetNumWearables(); ++i )
|
||
|
{
|
||
|
CEconWearable* pItem = pPlayer->GetWearable(i);
|
||
|
if ( !pItem )
|
||
|
continue;
|
||
|
|
||
|
// Dynamic models which are not yet rendering do not modify bodygroups
|
||
|
if ( pItem->IsDynamicModelLoading() )
|
||
|
continue;
|
||
|
|
||
|
// On the client, ignore items that aren't valid.
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
if ( pItem->EntityDeemedInvalid() )
|
||
|
continue;
|
||
|
#endif
|
||
|
|
||
|
int nVisibleState = 1;
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
if ( pItem->ShouldHideForVisionFilterFlags() )
|
||
|
{
|
||
|
// Items that shouldn't draw (pyro-vision filtered) shouldn't change any body group states
|
||
|
// unless they have no model (hatless hats)
|
||
|
nVisibleState = 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pItem->UpdateBodygroups( pPlayer, nVisibleState );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFWearableItem::CTFWearableItem()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// SERVER ONLY CODE
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#if defined( GAME_DLL )
|
||
|
|
||
|
#endif
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CLIENT ONLY CODE
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#if defined( CLIENT_DLL )
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Mirror should draw logic.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ShadowType_t CEconWearable::ShadowCastType()
|
||
|
{
|
||
|
if ( ShouldDraw() )
|
||
|
{
|
||
|
return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
|
||
|
}
|
||
|
|
||
|
return SHADOWS_NONE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CEconWearable::ShouldDraw( void )
|
||
|
{
|
||
|
CBasePlayer *pPlayerOwner = ToBasePlayer( GetOwnerEntity() );
|
||
|
if ( !pPlayerOwner )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool bUseViewModel = !pPlayerOwner->ShouldDrawThisPlayer();
|
||
|
|
||
|
// Don't show view models if we're drawing the real player, and don't show non view models if using view models.
|
||
|
if ( bUseViewModel )
|
||
|
{
|
||
|
// VM mode.
|
||
|
if ( !IsViewModelWearable() )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Non-viewmodel mode.
|
||
|
if ( IsViewModelWearable() )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !ShouldDrawWhenPlayerIsDead() && !pPlayerOwner->IsAlive() )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( pPlayerOwner->GetTeamNumber() == TEAM_SPECTATOR )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return BaseClass::ShouldDraw();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::OnDataChanged( DataUpdateType_t updateType )
|
||
|
{
|
||
|
BaseClass::OnDataChanged( updateType );
|
||
|
|
||
|
if ( updateType == DATA_UPDATE_CREATED )
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
|
||
|
// Update our visibility in case our parents' has changed.
|
||
|
UpdateVisibility();
|
||
|
UpdateParticleSystems();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEconWearable::ClientThink( void )
|
||
|
{
|
||
|
BaseClass::ClientThink();
|
||
|
|
||
|
UpdateParticleSystems();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CEconWearable::ShouldDrawParticleSystems( void )
|
||
|
{
|
||
|
// Make sure the entity we're attaching to is being drawn
|
||
|
CBasePlayer *pPlayerOwner = ToBasePlayer( GetOwnerEntity() );
|
||
|
if ( !pPlayerOwner )
|
||
|
{
|
||
|
Assert ( "CEconWearable has no owner?" ); // Not sure what this means - is is visible or not?
|
||
|
return false;
|
||
|
}
|
||
|
if ( pPlayerOwner->ShouldDrawThisPlayer() )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
RenderGroup_t CEconWearable::GetRenderGroup()
|
||
|
{
|
||
|
if ( IsViewModelWearable() )
|
||
|
return RENDER_GROUP_VIEW_MODEL_TRANSLUCENT;
|
||
|
|
||
|
return BaseClass::GetRenderGroup();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Wearable tint colors
|
||
|
//-----------------------------------------------------------------------------
|
||
|
class CProxyItemTintColor : public CResultProxy
|
||
|
{
|
||
|
public:
|
||
|
void OnBind( void *pC_BaseEntity )
|
||
|
{
|
||
|
Assert( m_pResult );
|
||
|
Vector vResult = Vector( 0, 0, 0 );
|
||
|
|
||
|
if ( pC_BaseEntity )
|
||
|
{
|
||
|
CEconItemView *pScriptItem = NULL;
|
||
|
|
||
|
IClientRenderable *pRend = (IClientRenderable *)pC_BaseEntity;
|
||
|
C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
|
||
|
if ( pEntity )
|
||
|
{
|
||
|
CEconEntity *pItem = dynamic_cast< CEconEntity* >( pEntity );
|
||
|
if ( pItem )
|
||
|
{
|
||
|
pScriptItem = pItem->GetAttributeContainer()->GetItem();
|
||
|
}
|
||
|
else if ( pEntity->GetOwnerEntity() )
|
||
|
{
|
||
|
// Try the owner (for viewmodels, etc).
|
||
|
pEntity = pEntity->GetOwnerEntity();
|
||
|
pItem = dynamic_cast< CEconEntity* >( pEntity );
|
||
|
if ( pItem )
|
||
|
{
|
||
|
pScriptItem = pItem->GetAttributeContainer()->GetItem();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Proxy data can be a script created item itself, if we're in a vgui CModelPanel
|
||
|
pScriptItem = dynamic_cast< CEconItemView* >( pRend );
|
||
|
}
|
||
|
|
||
|
#ifdef TF_CLIENT_DLL
|
||
|
if ( !pScriptItem )
|
||
|
{
|
||
|
// Might be a throwable
|
||
|
CTFWeaponBaseGrenadeProj *pProjectile = dynamic_cast< CTFWeaponBaseGrenadeProj* >( pEntity );
|
||
|
if ( pProjectile )
|
||
|
{
|
||
|
CEconEntity *pItem = dynamic_cast< CEconEntity* >( pProjectile->GetLauncher() );
|
||
|
if ( pItem )
|
||
|
{
|
||
|
pScriptItem = pItem->GetAttributeContainer()->GetItem();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pScriptItem && pScriptItem->IsValid() )
|
||
|
{
|
||
|
const bool bAltColor = pEntity && pEntity->GetTeam() > 0
|
||
|
? pEntity->GetTeam()->GetTeamNumber() == TF_TEAM_BLUE
|
||
|
: pScriptItem->GetFlags() & kEconItemFlagClient_ForceBlueTeam
|
||
|
? true
|
||
|
: false;
|
||
|
|
||
|
int iModifiedRGB = pScriptItem->GetModifiedRGBValue( bAltColor );
|
||
|
if ( iModifiedRGB )
|
||
|
{
|
||
|
// The attrib returns a packed RGB with values between 0 & 255 packed into the bottom 3 bytes.
|
||
|
Color clr = Color( ((iModifiedRGB & 0xFF0000) >> 16), ((iModifiedRGB & 0xFF00) >> 8), (iModifiedRGB & 0xFF) );
|
||
|
|
||
|
vResult.x = clamp( clr.r() * (1.f / 255.0f), 0.f, 1.0f );
|
||
|
vResult.y = clamp( clr.g() * (1.f / 255.0f), 0.f, 1.0f );
|
||
|
vResult.z = clamp( clr.b() * (1.f / 255.0f), 0.f, 1.0f );
|
||
|
}
|
||
|
}
|
||
|
#endif // TF_CLIENT_DLL
|
||
|
}
|
||
|
|
||
|
m_pResult->SetVecValue( vResult.x, vResult.y, vResult.z );
|
||
|
}
|
||
|
};
|
||
|
EXPOSE_INTERFACE( CProxyItemTintColor, IMaterialProxy, "ItemTintColor" IMATERIAL_PROXY_INTERFACE_VERSION );
|
||
|
|
||
|
|
||
|
|
||
|
//============================================================================================================================
|
||
|
extern ConVar r_propsmaxdist;
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
C_EconWearableGib::C_EconWearableGib()
|
||
|
{
|
||
|
m_fDeathTime = -1;
|
||
|
m_iHealth = 0;
|
||
|
m_bParented = false;
|
||
|
m_bDelayedInit = false;
|
||
|
}
|
||
|
|
||
|
C_EconWearableGib::~C_EconWearableGib()
|
||
|
{
|
||
|
PhysCleanupFrictionSounds( this );
|
||
|
VPhysicsDestroyObject();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_EconWearableGib::Initialize( bool bWillBeParented )
|
||
|
{
|
||
|
m_bParented = bWillBeParented;
|
||
|
return InitializeAsClientEntity( STRING( GetModelName() ), RENDER_GROUP_OPAQUE_ENTITY );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CStudioHdr* C_EconWearableGib::OnNewModel()
|
||
|
{
|
||
|
CStudioHdr* pCStudioHdr = BaseClass::OnNewModel();
|
||
|
if ( m_bDelayedInit && !IsDynamicModelLoading() )
|
||
|
{
|
||
|
FinishModelInitialization();
|
||
|
}
|
||
|
return pCStudioHdr;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_EconWearableGib::SpawnClientEntity( void )
|
||
|
{
|
||
|
if ( !IsDynamicModelLoading() )
|
||
|
{
|
||
|
FinishModelInitialization();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_bDelayedInit = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_EconWearableGib::FinishModelInitialization( void )
|
||
|
{
|
||
|
UpdateThinkState();
|
||
|
|
||
|
const model_t *mod = GetModel();
|
||
|
if ( mod )
|
||
|
{
|
||
|
Vector mins, maxs;
|
||
|
modelinfo->GetModelBounds( mod, mins, maxs );
|
||
|
SetCollisionBounds( mins, maxs );
|
||
|
}
|
||
|
|
||
|
if ( !m_bParented )
|
||
|
{
|
||
|
// Create the object in the physics system
|
||
|
solid_t tmpSolid;
|
||
|
if ( !PhysModelParseSolid( tmpSolid, this, GetModelIndex() ) )
|
||
|
{
|
||
|
DevMsg("C_EconWearableGib::FinishModelInitialization: PhysModelParseSolid failed for entity %i.\n", GetModelIndex() );
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pPhysicsObject = VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
|
||
|
|
||
|
if ( !m_pPhysicsObject )
|
||
|
{
|
||
|
// failed to create a physics object
|
||
|
DevMsg(" C_EconWearableGib::FinishModelInitialization: VPhysicsInitNormal() failed for %s.\n", STRING(GetModelName()) );
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Spawn();
|
||
|
|
||
|
if ( m_fadeMinDist < 0 )
|
||
|
{
|
||
|
// start fading out at 75% of r_propsmaxdist
|
||
|
m_fadeMaxDist = r_propsmaxdist.GetFloat();
|
||
|
m_fadeMinDist = r_propsmaxdist.GetFloat() * 0.75f;
|
||
|
}
|
||
|
|
||
|
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
||
|
|
||
|
UpdatePartitionListEntry();
|
||
|
|
||
|
CollisionProp()->UpdatePartition();
|
||
|
|
||
|
SetBlocksLOS( false ); // this should be a small object
|
||
|
|
||
|
// Set up shadows; do it here so that objects can change shadowcasting state
|
||
|
CreateShadow();
|
||
|
|
||
|
UpdateVisibility();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_EconWearableGib::Spawn()
|
||
|
{
|
||
|
BaseClass::Spawn();
|
||
|
m_takedamage = DAMAGE_EVENTS_ONLY;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_EconWearableGib::ValidateEntityAttachedToPlayer( bool &bShouldRetry )
|
||
|
{
|
||
|
bShouldRetry = false;
|
||
|
|
||
|
// Always valid as long as we're not parented to anything
|
||
|
return (GetMoveParent() == NULL);
|
||
|
}
|
||
|
|
||
|
#define WEARABLE_FADEOUT_TIME 1.0f
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Figure out if we need to think or not
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool C_EconWearableGib::UpdateThinkState( void )
|
||
|
{
|
||
|
if ( m_fDeathTime > 0 )
|
||
|
{
|
||
|
// If we're in the active fadeout portion, think rapidly. Otherwise, wait for that time.
|
||
|
if ( (m_fDeathTime - gpGlobals->curtime) > WEARABLE_FADEOUT_TIME )
|
||
|
{
|
||
|
SetNextClientThink( m_fDeathTime - WEARABLE_FADEOUT_TIME );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
SetNextClientThink( CLIENT_THINK_NEVER );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_EconWearableGib::ClientThink( void )
|
||
|
{
|
||
|
if ( (m_fDeathTime > 0) && ((m_fDeathTime - gpGlobals->curtime) <= WEARABLE_FADEOUT_TIME) )
|
||
|
{
|
||
|
if ( m_fDeathTime <= gpGlobals->curtime )
|
||
|
{
|
||
|
Release(); // Die
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// fade out
|
||
|
float alpha = (m_fDeathTime - gpGlobals->curtime) / WEARABLE_FADEOUT_TIME;
|
||
|
SetRenderMode( kRenderTransTexture );
|
||
|
SetRenderColorA( alpha * 256 );
|
||
|
}
|
||
|
|
||
|
UpdateThinkState();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_EconWearableGib::StartFadeOut( float fDelay )
|
||
|
{
|
||
|
m_fDeathTime = gpGlobals->curtime + fDelay + WEARABLE_FADEOUT_TIME;
|
||
|
UpdateThinkState();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void C_EconWearableGib::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
|
||
|
{
|
||
|
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
||
|
|
||
|
if( !pPhysicsObject )
|
||
|
return;
|
||
|
|
||
|
Vector dir = pTrace->endpos - pTrace->startpos;
|
||
|
int iDamage = 0;
|
||
|
|
||
|
if ( iDamageType & DMG_BLAST )
|
||
|
{
|
||
|
iDamage = VectorLength( dir );
|
||
|
dir *= 500; // adjust impact strenght
|
||
|
|
||
|
// apply force at object mass center
|
||
|
pPhysicsObject->ApplyForceCenter( dir );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Vector hitpos;
|
||
|
|
||
|
VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
|
||
|
VectorNormalize( dir );
|
||
|
|
||
|
// guess avg damage
|
||
|
if ( iDamageType == DMG_BULLET )
|
||
|
{
|
||
|
iDamage = 30;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iDamage = 50;
|
||
|
}
|
||
|
|
||
|
dir *= 4000; // adjust impact strenght
|
||
|
|
||
|
// apply force where we hit it
|
||
|
pPhysicsObject->ApplyForceOffset( dir, hitpos );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
#ifdef _DEBUG
|
||
|
#include "econ_item_system.h"
|
||
|
|
||
|
static CUtlVector< const char * > s_possibleModels;
|
||
|
static CUtlVector< const GameItemDefinition_t * > s_possibleDefinitions;
|
||
|
void Dbg_TestDynamicWearableGibs( void )
|
||
|
{
|
||
|
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
|
||
|
if ( !pLocalPlayer )
|
||
|
return;
|
||
|
|
||
|
C_EconWearableGib *pEntity = new C_EconWearableGib();
|
||
|
if ( !pEntity )
|
||
|
return;
|
||
|
|
||
|
Vector forward;
|
||
|
pLocalPlayer->EyeVectors( &forward );
|
||
|
trace_t tr;
|
||
|
UTIL_TraceLine( pLocalPlayer->EyePosition(), pLocalPlayer->EyePosition() + (forward * 256), MASK_NPCSOLID, pLocalPlayer, COLLISION_GROUP_NONE, &tr );
|
||
|
|
||
|
Vector position = tr.endpos;
|
||
|
|
||
|
if ( s_possibleModels.Count() == 0 )
|
||
|
{
|
||
|
FOR_EACH_MAP( ItemSystem()->GetItemSchema()->GetItemDefinitionMap(), nDefn )
|
||
|
{
|
||
|
const GameItemDefinition_t *pDefn = dynamic_cast<GameItemDefinition_t *>( ItemSystem()->GetItemSchema()->GetItemDefinitionMap()[nDefn] );
|
||
|
if ( !pDefn )
|
||
|
continue;
|
||
|
|
||
|
const char *pszModel = pDefn->GetPlayerDisplayModel( 0 );
|
||
|
if ( pszModel && pszModel[0] && pszModel[0] != '?' && pDefn->BLoadOnDemand() && pDefn->GetDropType() == ITEM_DROP_TYPE_DROP )
|
||
|
{
|
||
|
s_possibleModels.AddToTail( pszModel );
|
||
|
s_possibleDefinitions.AddToTail( pDefn );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Assert( s_possibleModels.Count() );
|
||
|
|
||
|
int spawnIndex = random->RandomInt( 0, s_possibleModels.Count() - 1 );
|
||
|
const char *pszModelName = s_possibleModels[ spawnIndex ];
|
||
|
const GameItemDefinition_t *pDefn = s_possibleDefinitions[ spawnIndex ];
|
||
|
Msg( "Spawning: %s\n", pszModelName );
|
||
|
pEntity->SetModelName( AllocPooledString( pszModelName ) );
|
||
|
pEntity->SetAbsOrigin( position );
|
||
|
pEntity->SetAbsAngles( vec3_angle );
|
||
|
pEntity->SetOwnerEntity( pLocalPlayer );
|
||
|
pEntity->ChangeTeam( pLocalPlayer->GetTeamNumber() ); // our gibs will match our team; this will probably not be used for anything besides team coloring
|
||
|
// Copy the script created item data over
|
||
|
pEntity->GetAttributeContainer()->GetItem()->Init( pDefn->GetDefinitionIndex(), pDefn->GetQuality(), pDefn->GetMinLevel(), true );
|
||
|
|
||
|
if ( !pEntity->Initialize( false ) )
|
||
|
{
|
||
|
pEntity->Release();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pEntity->StartFadeOut( 15.0f );
|
||
|
return;
|
||
|
|
||
|
IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
|
||
|
if ( !pPhysicsObject )
|
||
|
{
|
||
|
pEntity->Release();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// randomize velocity by 5%
|
||
|
Vector rndVel = Vector(0,0,100);
|
||
|
pPhysicsObject->AddVelocity( &rndVel, &vec3_origin );
|
||
|
}
|
||
|
static ConCommand dbg_testdynamicwearablegib( "dbg_testdynamicwearablegib", Dbg_TestDynamicWearableGibs, "", FCVAR_CHEAT );
|
||
|
#endif // _DEBUG
|
||
|
#endif
|
||
|
|
||
|
#endif // client only
|