//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: The various ammo types for HL2 // //=============================================================================// #include "cbase.h" #include "props.h" #include "items.h" #include "item_dynamic_resupply.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" const char *pszItemCrateModelName[] = { "models/items/item_item_crate.mdl", "models/items/item_beacon_crate.mdl", }; //----------------------------------------------------------------------------- // A breakable crate that drops items //----------------------------------------------------------------------------- class CItem_ItemCrate : public CPhysicsProp { public: DECLARE_CLASS( CItem_ItemCrate, CPhysicsProp ); DECLARE_DATADESC(); void Precache( void ); void Spawn( void ); virtual int ObjectCaps() { return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION; }; virtual int OnTakeDamage( const CTakeDamageInfo &info ); void InputKill( inputdata_t &data ); virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); protected: virtual void OnBreak( const Vector &vecVelocity, const AngularImpulse &angVel, CBaseEntity *pBreaker ); private: // Crate types. Add more! enum CrateType_t { CRATE_SPECIFIC_ITEM = 0, CRATE_TYPE_COUNT, }; enum CrateAppearance_t { CRATE_APPEARANCE_DEFAULT = 0, CRATE_APPEARANCE_RADAR_BEACON, }; private: CrateType_t m_CrateType; string_t m_strItemClass; int m_nItemCount; string_t m_strAlternateMaster; CrateAppearance_t m_CrateAppearance; COutputEvent m_OnCacheInteraction; }; LINK_ENTITY_TO_CLASS(item_item_crate, CItem_ItemCrate); //----------------------------------------------------------------------------- // Save/load: //----------------------------------------------------------------------------- BEGIN_DATADESC( CItem_ItemCrate ) DEFINE_KEYFIELD( m_CrateType, FIELD_INTEGER, "CrateType" ), DEFINE_KEYFIELD( m_strItemClass, FIELD_STRING, "ItemClass" ), DEFINE_KEYFIELD( m_nItemCount, FIELD_INTEGER, "ItemCount" ), DEFINE_KEYFIELD( m_strAlternateMaster, FIELD_STRING, "SpecificResupply" ), DEFINE_KEYFIELD( m_CrateAppearance, FIELD_INTEGER, "CrateAppearance" ), DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ), DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ), END_DATADESC() //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_ItemCrate::Precache( void ) { // Set this here to quiet base prop warnings PrecacheModel( pszItemCrateModelName[m_CrateAppearance] ); SetModel( pszItemCrateModelName[m_CrateAppearance] ); BaseClass::Precache(); if ( m_CrateType == CRATE_SPECIFIC_ITEM ) { if ( NULL_STRING != m_strItemClass ) { // Don't precache if this is a null string. UTIL_PrecacheOther( STRING(m_strItemClass) ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_ItemCrate::Spawn( void ) { if ( g_pGameRules->IsAllowedToSpawn( this ) == false ) { UTIL_Remove( this ); return; } DisableAutoFade(); SetModelName( AllocPooledString( pszItemCrateModelName[m_CrateAppearance] ) ); if ( NULL_STRING == m_strItemClass ) { Warning( "CItem_ItemCrate(%i): CRATE_SPECIFIC_ITEM with NULL ItemClass string (deleted)!!!\n", entindex() ); UTIL_Remove( this ); return; } Precache( ); SetModel( pszItemCrateModelName[m_CrateAppearance] ); AddEFlags( EFL_NO_ROTORWASH_PUSH ); BaseClass::Spawn( ); } //----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void CItem_ItemCrate::InputKill( inputdata_t &data ) { UTIL_Remove( this ); } //----------------------------------------------------------------------------- // Item crates blow up immediately //----------------------------------------------------------------------------- int CItem_ItemCrate::OnTakeDamage( const CTakeDamageInfo &info ) { if ( info.GetDamageType() & DMG_AIRBOAT ) { CTakeDamageInfo dmgInfo = info; dmgInfo.ScaleDamage( 10.0 ); return BaseClass::OnTakeDamage( dmgInfo ); } return BaseClass::OnTakeDamage( info ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_ItemCrate::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { float flDamageScale = 1.0f; if ( FClassnameIs( pEvent->pEntities[!index], "prop_vehicle_airboat" ) || FClassnameIs( pEvent->pEntities[!index], "prop_vehicle_jeep" ) ) { flDamageScale = 100.0f; } m_impactEnergyScale *= flDamageScale; BaseClass::VPhysicsCollision( index, pEvent ); m_impactEnergyScale /= flDamageScale; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_ItemCrate::OnBreak( const Vector &vecVelocity, const AngularImpulse &angImpulse, CBaseEntity *pBreaker ) { // FIXME: We could simply store the name of an entity to put into the crate // as a string entered in by worldcraft. Should we? I'd do it for sure // if it was easy to get a dropdown with all entity types in it. m_OnCacheInteraction.FireOutput(pBreaker,this); for ( int i = 0; i < m_nItemCount; ++i ) { CBaseEntity *pSpawn = NULL; switch( m_CrateType ) { case CRATE_SPECIFIC_ITEM: pSpawn = CreateEntityByName( STRING(m_strItemClass) ); break; default: break; } if ( !pSpawn ) return; // Give a little randomness... Vector vecOrigin; CollisionProp()->RandomPointInBounds( Vector(0.25, 0.25, 0.25), Vector( 0.75, 0.75, 0.75 ), &vecOrigin ); pSpawn->SetAbsOrigin( vecOrigin ); QAngle vecAngles; vecAngles.x = random->RandomFloat( -20.0f, 20.0f ); vecAngles.y = random->RandomFloat( 0.0f, 360.0f ); vecAngles.z = random->RandomFloat( -20.0f, 20.0f ); pSpawn->SetAbsAngles( vecAngles ); Vector vecActualVelocity; vecActualVelocity.Random( -10.0f, 10.0f ); // vecActualVelocity += vecVelocity; pSpawn->SetAbsVelocity( vecActualVelocity ); QAngle angVel; AngularImpulseToQAngle( angImpulse, angVel ); pSpawn->SetLocalAngularVelocity( angVel ); // If we're creating an item, it can't be picked up until it comes to rest // But only if it wasn't broken by a vehicle CItem *pItem = dynamic_cast(pSpawn); if ( pItem && !pBreaker->GetServerVehicle()) { pItem->ActivateWhenAtRest(); } pSpawn->Spawn(); // Avoid missing items drops by a dynamic resupply because they don't think immediately if ( FClassnameIs( pSpawn, "item_dynamic_resupply" ) ) { if ( m_strAlternateMaster != NULL_STRING ) { DynamicResupply_InitFromAlternateMaster( pSpawn, m_strAlternateMaster ); } if ( i == 0 ) { pSpawn->AddSpawnFlags( SF_DYNAMICRESUPPLY_ALWAYS_SPAWN ); } pSpawn->SetNextThink( gpGlobals->curtime ); } } } void CItem_ItemCrate::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) { BaseClass::OnPhysGunPickup( pPhysGunUser, reason ); m_OnCacheInteraction.FireOutput( pPhysGunUser, this ); if ( reason == PUNTED_BY_CANNON && m_CrateAppearance != CRATE_APPEARANCE_RADAR_BEACON ) { Vector vForward; AngleVectors( pPhysGunUser->EyeAngles(), &vForward, NULL, NULL ); Vector vForce = Pickup_PhysGunLaunchVelocity( this, vForward, PHYSGUN_FORCE_PUNTED ); AngularImpulse angular = AngularImpulse( 0, 0, 0 ); IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics ) { pPhysics->AddVelocity( &vForce, &angular ); } TakeDamage( CTakeDamageInfo( pPhysGunUser, pPhysGunUser, GetHealth(), DMG_GENERIC ) ); } }