Team Fortress 2 Source Code as on 22/4/2020
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.

1154 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_jar.h"
  8. #include "decals.h"
  9. // Client specific.
  10. #ifdef CLIENT_DLL
  11. #include "c_tf_player.h"
  12. // Server specific.
  13. #else
  14. #include "soundent.h"
  15. #include "te_effect_dispatch.h"
  16. #include "tf_player.h"
  17. #include "func_break.h"
  18. #include "func_nogrenades.h"
  19. #include "Sprite.h"
  20. #include "tf_fx.h"
  21. #include "tf_team.h"
  22. #include "tf_gamestats.h"
  23. #include "tf_gamerules.h"
  24. #include "particle_parse.h"
  25. #include "bone_setup.h"
  26. #endif
  27. //=============================================================================
  28. //
  29. // Weapon Jar tables.
  30. //
  31. IMPLEMENT_NETWORKCLASS_ALIASED( TFJar, DT_TFWeaponJar )
  32. BEGIN_NETWORK_TABLE( CTFJar, DT_TFWeaponJar )
  33. END_NETWORK_TABLE()
  34. BEGIN_PREDICTION_DATA( CTFJar )
  35. END_PREDICTION_DATA()
  36. LINK_ENTITY_TO_CLASS( tf_weapon_jar, CTFJar );
  37. PRECACHE_WEAPON_REGISTER( tf_weapon_jar );
  38. IMPLEMENT_NETWORKCLASS_ALIASED( TFJarMilk, DT_TFWeaponJarMilk )
  39. BEGIN_NETWORK_TABLE( CTFJarMilk, DT_TFWeaponJarMilk )
  40. END_NETWORK_TABLE()
  41. LINK_ENTITY_TO_CLASS( tf_weapon_jar_milk, CTFJarMilk );
  42. PRECACHE_WEAPON_REGISTER( tf_weapon_jar_milk );
  43. IMPLEMENT_NETWORKCLASS_ALIASED( TFCleaver, DT_TFWeaponCleaver )
  44. BEGIN_NETWORK_TABLE( CTFCleaver, DT_TFWeaponCleaver )
  45. END_NETWORK_TABLE()
  46. LINK_ENTITY_TO_CLASS( tf_weapon_cleaver, CTFCleaver );
  47. PRECACHE_WEAPON_REGISTER( tf_weapon_cleaver );
  48. // Projectile tables.
  49. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Jar, DT_TFProjectile_Jar )
  50. BEGIN_NETWORK_TABLE( CTFProjectile_Jar, DT_TFProjectile_Jar )
  51. END_NETWORK_TABLE()
  52. LINK_ENTITY_TO_CLASS( tf_projectile_jar, CTFProjectile_Jar );
  53. PRECACHE_WEAPON_REGISTER( tf_projectile_jar );
  54. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_JarMilk, DT_TFProjectile_JarMilk )
  55. BEGIN_NETWORK_TABLE( CTFProjectile_JarMilk, DT_TFProjectile_JarMilk )
  56. END_NETWORK_TABLE()
  57. LINK_ENTITY_TO_CLASS( tf_projectile_jar_milk, CTFProjectile_JarMilk );
  58. PRECACHE_WEAPON_REGISTER( tf_projectile_jar_milk );
  59. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Cleaver, DT_TFProjectile_Cleaver )
  60. BEGIN_NETWORK_TABLE( CTFProjectile_Cleaver, DT_TFProjectile_Cleaver )
  61. END_NETWORK_TABLE()
  62. LINK_ENTITY_TO_CLASS( tf_projectile_cleaver, CTFProjectile_Cleaver );
  63. PRECACHE_WEAPON_REGISTER( tf_projectile_cleaver );
  64. #define TF_JAR_LAUNCH_SPEED 1000.f
  65. #define TF_CLEAVER_LAUNCH_SPEED 7000.f
  66. #define TF_WEAPON_PEEJAR_MODEL "models/weapons/c_models/urinejar.mdl"
  67. #define TF_WEAPON_FESTIVE_PEEJAR_MODEL "models/weapons/c_models/c_xms_urinejar.mdl"
  68. #ifdef STAGING_ONLY
  69. #define TF_WEAPON_MILKJAR_MODEL "models/workshop/weapons/c_models/c_madmilk/c_madmilk.mdl"
  70. #define TF_WEAPON_CLEAVER_MODEL "models/workshop_partner/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl"
  71. #else
  72. #define TF_WEAPON_MILKJAR_MODEL "models/weapons/c_models/c_madmilk/c_madmilk.mdl"
  73. #define TF_WEAPON_CLEAVER_MODEL "models/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl"
  74. #endif
  75. #define TF_WEAPON_PEEJAR_EXPLODE_SOUND "Jar.Explode"
  76. #define TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND "Cleaver.ImpactFlesh"
  77. #define TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND "Cleaver.ImpactWorld"
  78. #ifdef STAGING_ONLY
  79. #define TF_WEAPON_WATER_BALLOON_KILL_SOUND "Game.PenetrationKill"
  80. #define TF_WEAPON_WATER_BALLOON_HIT_SOUND "Weapon_waterbomb.hit"
  81. #define TF_WEAPON_WATER_BALLOON_SCORE_SOUND "Weapon_waterbomb.score"
  82. #define TF_BREAD_MODEL "models/props_gameplay/small_loaf.mdl"
  83. #define TF_WATERBALLOON_RADIUS 32
  84. #define TF_WATERBALLOON_CHARGEDRADIUS 64
  85. #define TF_WATERBALLOON_EXPLODE_SOUND "Weapon_waterbomb.explode"
  86. #endif
  87. //=============================================================================
  88. //
  89. // Weapon Jar functions.
  90. //
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. CTFJar::CTFJar()
  95. {
  96. }
  97. float CTFJar::GetProjectileSpeed( void )
  98. {
  99. return TF_JAR_LAUNCH_SPEED;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose:
  103. //-----------------------------------------------------------------------------
  104. void CTFJar::PrimaryAttack( void )
  105. {
  106. CTFPlayer *pPlayer = GetTFPlayerOwner();
  107. if ( !pPlayer )
  108. return;
  109. int iJarCount = pPlayer->GetAmmoCount( m_iPrimaryAmmoType );
  110. if ( iJarCount == 0 )
  111. return;
  112. if ( ( pPlayer->GetWaterLevel() == WL_Eyes ) && !CanThrowUnderWater() )
  113. return;
  114. BaseClass::PrimaryAttack();
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose:
  118. //-----------------------------------------------------------------------------
  119. CBaseEntity *CTFJar::FireJar( CTFPlayer *pPlayer )
  120. {
  121. StartEffectBarRegen();
  122. SetContextThink( &CTFJar::TossJarThink, gpGlobals->curtime + 0.1f, "TOSS_JAR_THINK" );
  123. return NULL;
  124. }
  125. #ifdef GAME_DLL
  126. //-----------------------------------------------------------------------------
  127. // Purpose:
  128. //-----------------------------------------------------------------------------
  129. CTFProjectile_Jar *CTFJar::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity,
  130. const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  131. {
  132. return CTFProjectile_Jar::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo );
  133. }
  134. #endif
  135. #ifdef GAME_DLL
  136. Vector CTFJar::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp )
  137. {
  138. return ( ( vecForward * GetProjectileSpeed() ) + ( vecUp * 200.0f ) + ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) +
  139. ( random->RandomFloat( -10.0f, 10.0f ) * vecUp ) );
  140. }
  141. #endif
  142. //-----------------------------------------------------------------------------
  143. // Purpose:
  144. //-----------------------------------------------------------------------------
  145. void CTFJar::TossJarThink( void )
  146. {
  147. CTFPlayer *pPlayer = GetTFPlayerOwner();
  148. if ( !pPlayer )
  149. return;
  150. PlayWeaponShootSound();
  151. #ifdef GAME_DLL
  152. Vector vecForward, vecRight, vecUp;
  153. AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp );
  154. float fRight = 8.f;
  155. if ( IsViewModelFlipped() )
  156. {
  157. fRight *= -1;
  158. }
  159. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  160. vecSrc += vecForward * 16.0f + vecRight * fRight + vecUp * -6.0f;
  161. trace_t trace;
  162. Vector vecEye = pPlayer->EyePosition();
  163. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  164. UTIL_TraceHull( vecEye, vecSrc, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  165. // If we started in solid, don't let them fire at all
  166. if ( trace.startsolid )
  167. return;
  168. Vector vecVelocity = GetVelocityVector( vecForward, vecRight, vecUp );
  169. CTFProjectile_Jar *pProjectile = CreateJarProjectile( trace.endpos, pPlayer->EyeAngles(), vecVelocity,
  170. GetAngularImpulse(), pPlayer, GetTFWpnData() );
  171. if ( pProjectile )
  172. {
  173. pProjectile->SetCritical( IsCurrentAttackACrit() );
  174. pProjectile->SetLauncher( this );
  175. }
  176. if ( ShouldSpeakWhenFiring() )
  177. {
  178. pPlayer->SpeakWeaponFire( MP_CONCEPT_JARATE_LAUNCH );
  179. }
  180. #endif
  181. }
  182. //-----------------------------------------------------------------------------
  183. void CTFJar::GetProjectileEntityName( CAttribute_String *attrProjectileEntityName )
  184. {
  185. static CSchemaAttributeDefHandle pAttrDef_ProjectileEntityName( "projectile entity name" );
  186. CEconItemView *pItem = GetAttributeContainer()->GetItem();
  187. if ( pAttrDef_ProjectileEntityName && pItem )
  188. {
  189. //CAttribute_String attrProjectileEntityName;
  190. pItem->FindAttribute( pAttrDef_ProjectileEntityName, attrProjectileEntityName );
  191. }
  192. }
  193. #ifdef GAME_DLL
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. //-----------------------------------------------------------------------------
  197. CTFProjectile_Jar::CTFProjectile_Jar()
  198. {
  199. m_vCollisionVelocity = Vector( 0,0,0 );
  200. m_iProjectileType = TF_PROJECTILE_JAR;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. //-----------------------------------------------------------------------------
  205. void CTFProjectile_Jar::Precache()
  206. {
  207. PrecacheModel( TF_WEAPON_PEEJAR_MODEL );
  208. PrecacheModel( TF_WEAPON_FESTIVE_PEEJAR_MODEL );
  209. PrecacheModel( "models/weapons/c_models/c_breadmonster/c_breadmonster.mdl" );
  210. PrecacheScriptSound( TF_WEAPON_PEEJAR_EXPLODE_SOUND );
  211. BaseClass::Precache();
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. void CTFProjectile_Jar::SetCustomPipebombModel()
  217. {
  218. // Check for Model Override
  219. int iProjectile = 0;
  220. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  221. if ( pThrower && pThrower->GetActiveWeapon() )
  222. {
  223. CALL_ATTRIB_HOOK_INT_ON_OTHER( pThrower->GetActiveWeapon(), iProjectile, override_projectile_type );
  224. switch ( iProjectile )
  225. {
  226. case TF_PROJECTILE_FESTIVE_JAR :
  227. m_iProjectileType = iProjectile;
  228. SetModel( TF_WEAPON_FESTIVE_PEEJAR_MODEL );
  229. return;
  230. case TF_PROJECTILE_BREADMONSTER_JARATE:
  231. case TF_PROJECTILE_BREADMONSTER_MADMILK:
  232. m_iProjectileType = iProjectile;
  233. SetModel( "models/weapons/c_models/c_breadmonster/c_breadmonster.mdl" );
  234. return;
  235. }
  236. }
  237. SetModel( TF_WEAPON_PEEJAR_MODEL );
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose:
  241. //-----------------------------------------------------------------------------
  242. CTFProjectile_Jar* CTFProjectile_Jar::Create( const Vector &position, const QAngle &angles,
  243. const Vector &velocity, const AngularImpulse &angVelocity,
  244. CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  245. {
  246. CTFProjectile_Jar *pGrenade = static_cast<CTFProjectile_Jar*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar", position, angles, pOwner ) );
  247. if ( pGrenade )
  248. {
  249. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  250. pGrenade->SetPipebombMode();
  251. DispatchSpawn( pGrenade );
  252. pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo );
  253. #ifdef _X360
  254. if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE )
  255. {
  256. pGrenade->SetDamage( TF_WEAPON_GRENADE_XBOX_DAMAGE );
  257. }
  258. #endif
  259. pGrenade->m_flFullDamage = 0;
  260. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  261. }
  262. return pGrenade;
  263. }
  264. extern void ExtinguishPlayer( CEconEntity *pExtinguisher, CTFPlayer *pOwner, CTFPlayer *pTarget, const char *pExtinguisherName );
  265. void JarExplode( int iEntIndex, CTFPlayer *pAttacker, CBaseEntity *pOriginalWeapon, CBaseEntity *pWeapon, const Vector& vContactPoint, int iTeam, float flRadius, ETFCond cond, float flDuration, const char *pszImpactEffect )
  266. {
  267. // Splash!
  268. CPVSFilter particleFilter( vContactPoint );
  269. TE_TFParticleEffect( particleFilter, 0.0, pszImpactEffect, vContactPoint, vec3_angle );
  270. // Explosion effect.
  271. CBroadcastRecipientFilter soundFilter;
  272. Vector vecOrigin = vContactPoint;
  273. CBaseEntity::EmitSound( soundFilter, iEntIndex, TF_WEAPON_PEEJAR_EXPLODE_SOUND, &vecOrigin );
  274. // Treat this trace exactly like radius damage
  275. CTraceFilterIgnorePlayers traceFilter( pAttacker, COLLISION_GROUP_PROJECTILE );
  276. // Splash pee on everyone nearby.
  277. CBaseEntity *pListOfEntities[32];
  278. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, vContactPoint, flRadius, FL_CLIENT );
  279. for ( int i = 0; i < iEntities; ++i )
  280. {
  281. CTFPlayer *pPlayer = ToTFPlayer( pListOfEntities[i] );
  282. if ( !pPlayer || !pPlayer->IsAlive() )
  283. continue;
  284. // Do a quick trace to see if there's any geometry in the way.
  285. // Pee isn't stopped by other entities. Splishy splashy.
  286. trace_t trace;
  287. UTIL_TraceLine( vContactPoint, pPlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  288. if ( trace.DidHitWorld() )
  289. continue;
  290. // Drench the target.
  291. if ( pPlayer->GetTeamNumber() != iTeam )
  292. {
  293. if ( pPlayer->m_Shared.IsInvulnerable() )
  294. continue;
  295. if ( pPlayer->m_Shared.InCond( TF_COND_PHASE ) || pPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  296. continue;
  297. if ( !pPlayer->CanGetWet() )
  298. continue;
  299. pPlayer->m_Shared.AddCond( cond, flDuration, pAttacker );
  300. pPlayer->m_Shared.SetPeeAttacker( pAttacker );
  301. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_JARATE_HIT );
  302. if ( pAttacker )
  303. {
  304. if ( pPlayer->IsPlayerClass( TF_CLASS_SPY ) && pPlayer->m_Shared.GetPercentInvisible() == 1.0f )
  305. {
  306. pAttacker->AwardAchievement( ACHIEVEMENT_TF_SNIPER_JARATE_REVEAL_SPY );
  307. }
  308. float flStun = 1.0f;
  309. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAttacker, flStun, applies_snare_effect );
  310. if ( flStun != 1.0f )
  311. {
  312. pPlayer->m_Shared.StunPlayer( flDuration, flStun, TF_STUN_MOVEMENT, pAttacker );
  313. }
  314. // Stats tracking?
  315. if ( cond == TF_COND_URINE || cond == TF_COND_MAD_MILK )
  316. {
  317. if ( TFGameRules() && TFGameRules()->IsPVEModeActive() )
  318. {
  319. // These if statements are intentionally split to avoid falling through to the normal kKillEaterEvent_PeeVictims event if we're in
  320. // IsPVEModeActive() but not a robot, or don't have the stun.
  321. if ( pPlayer->GetTeamNumber() == TF_TEAM_PVE_INVADERS && flStun != 1.0f )
  322. {
  323. EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, kKillEaterEvent_RobotsSlowed );
  324. }
  325. }
  326. else
  327. {
  328. EconEntity_OnOwnerKillEaterEvent( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, kKillEaterEvent_PeeVictims );
  329. }
  330. }
  331. // Tell the clients involved in the jarate
  332. CRecipientFilter involved_filter;
  333. involved_filter.AddRecipient( pPlayer );
  334. involved_filter.AddRecipient( pAttacker );
  335. UserMessageBegin( involved_filter, "PlayerJarated" );
  336. WRITE_BYTE( pAttacker->entindex() );
  337. WRITE_BYTE( pPlayer->entindex() );
  338. MessageEnd();
  339. const char *pszEvent = NULL;
  340. switch( cond )
  341. {
  342. case TF_COND_URINE:
  343. pszEvent = "jarate_attack";
  344. break;
  345. case TF_COND_MAD_MILK:
  346. pszEvent = "milk_attack";
  347. break;
  348. }
  349. if ( pszEvent && pszEvent[0] )
  350. {
  351. UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"%s\" against \"%s<%i><%s><%s>\" with \"%s\" (attacker_position \"%d %d %d\") (victim_position \"%d %d %d\")\n",
  352. pAttacker->GetPlayerName(),
  353. pAttacker->GetUserID(),
  354. pAttacker->GetNetworkIDString(),
  355. pAttacker->GetTeam()->GetName(),
  356. pszEvent,
  357. pPlayer->GetPlayerName(),
  358. pPlayer->GetUserID(),
  359. pPlayer->GetNetworkIDString(),
  360. pPlayer->GetTeam()->GetName(),
  361. "tf_weapon_jar",
  362. (int)pAttacker->GetAbsOrigin().x,
  363. (int)pAttacker->GetAbsOrigin().y,
  364. (int)pAttacker->GetAbsOrigin().z,
  365. (int)pPlayer->GetAbsOrigin().x,
  366. (int)pPlayer->GetAbsOrigin().y,
  367. (int)pPlayer->GetAbsOrigin().z );
  368. }
  369. }
  370. }
  371. else
  372. {
  373. if ( pAttacker && pPlayer->m_Shared.InCond( TF_COND_BURNING ) )
  374. {
  375. ExtinguishPlayer( dynamic_cast<CEconEntity *>( pWeapon ), pAttacker, pPlayer, "tf_weapon_jar" );
  376. // Return some percentage of the jar to the thrown weapon if extinguishing an ally
  377. auto pLauncher = dynamic_cast< CTFWeaponBase* >( pOriginalWeapon );
  378. if ( pLauncher && pAttacker != pPlayer && pLauncher->HasEffectBarRegeneration() )
  379. {
  380. float fCooldown = 1.0f;
  381. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, fCooldown, extinguish_reduces_cooldown );
  382. fCooldown = 1.0f - fCooldown;
  383. if ( fCooldown > 0 )
  384. {
  385. if ( pLauncher->GetEffectBarProgress() < fCooldown )
  386. {
  387. float fDuration = pLauncher->GetEffectBarRechargeTime();
  388. float fIncrement = fDuration * fCooldown;
  389. pLauncher->DecrementBarRegenTime( fIncrement );
  390. }
  391. }
  392. }
  393. }
  394. }
  395. }
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. //-----------------------------------------------------------------------------
  400. void CTFProjectile_Jar::Explode( trace_t *pTrace, int bitsDamageType )
  401. {
  402. SetModelName( NULL_STRING );//invisible
  403. AddSolidFlags( FSOLID_NOT_SOLID );
  404. m_takedamage = DAMAGE_NO;
  405. // Pull out of the wall a bit.
  406. if ( pTrace->fraction != 1.0 )
  407. {
  408. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  409. }
  410. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  411. JarExplode( entindex(), pThrower, GetOriginalLauncher(), GetLauncher(), GetAbsOrigin(), GetTeamNumber(), GetDamageRadius(), GetEffectCondition(), 10.f, GetImpactEffect() );
  412. // Debug radius draw.
  413. //DrawRadius( GetDamageRadius() );
  414. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  415. SetTouch( NULL );
  416. AddEffects( EF_NODRAW );
  417. SetAbsVelocity( vec3_origin );
  418. }
  419. //-----------------------------------------------------------------------------
  420. void CTFProjectile_Jar::PipebombTouch( CBaseEntity *pOther )
  421. {
  422. if ( pOther == GetThrower() )
  423. return;
  424. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  425. return;
  426. if ( !pOther->IsWorld() && !pOther->IsPlayer() )
  427. return;
  428. // Don't collide with teammate if we're still in the grace period.
  429. if ( pOther->IsPlayer() && pOther->GetTeamNumber() == GetTeamNumber() && !CanCollideWithTeammates() )
  430. {
  431. // Exception to this rule - if we're a jar or milk, and our potential victim is on fire, then allow collision after all.
  432. // If we're a jar or milk, then still allow collision if our potential victim is on fire.
  433. if (m_iProjectileType == TF_PROJECTILE_JAR || m_iProjectileType == TF_PROJECTILE_JAR_MILK)
  434. {
  435. auto victim = ToTFPlayer(pOther);
  436. if (!victim->m_Shared.InCond(TF_COND_BURNING))
  437. {
  438. return;
  439. }
  440. }
  441. else
  442. {
  443. return;
  444. }
  445. }
  446. // Handle hitting skybox (disappear).
  447. trace_t pTrace;
  448. Vector velDir = GetAbsVelocity();
  449. if ( velDir.IsZero() && pOther && pOther->IsPlayer() )
  450. {
  451. velDir = pOther->WorldSpaceCenter() - GetAbsOrigin();
  452. }
  453. VectorNormalize( velDir );
  454. Vector vecSpot = GetAbsOrigin() - velDir * 32;
  455. UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
  456. if ( pTrace.fraction < 1.0 && pTrace.surface.flags & SURF_SKY )
  457. {
  458. UTIL_Remove( this );
  459. return;
  460. }
  461. // If we already touched a surface then we're not exploding on contact anymore.
  462. if ( m_bTouched == true )
  463. return;
  464. OnHit( pOther );
  465. if ( m_iProjectileType == TF_PROJECTILE_BREADMONSTER_JARATE || m_iProjectileType == TF_PROJECTILE_BREADMONSTER_MADMILK )
  466. {
  467. OnBreadMonsterHit( pOther, &pTrace );
  468. }
  469. if ( ExplodesOnHit() )
  470. {
  471. // Save this entity as enemy, they will take 100% damage if applicable
  472. m_hEnemy = pOther;
  473. Explode( &pTrace, GetDamageType() );
  474. }
  475. }
  476. //-----------------------------------------------------------------------------
  477. void CTFProjectile_Jar::OnBreadMonsterHit( CBaseEntity *pOther, trace_t *pTrace )
  478. {
  479. if ( m_iProjectileType != TF_PROJECTILE_BREADMONSTER_JARATE && m_iProjectileType != TF_PROJECTILE_BREADMONSTER_MADMILK )
  480. return;
  481. CTFPlayer *pVictim = ToTFPlayer( pOther );
  482. if ( !pVictim || pVictim->GetTeamNumber() == GetTeamNumber() )
  483. return;
  484. // This is a player on the other team, attach a breadmonster
  485. CTFPlayer *pOwner = ToTFPlayer( GetThrower() );
  486. // Attach Breadmonster to Victim
  487. CreateStickyAttachmentToTarget( pOwner, pVictim, pTrace );
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose:
  491. //-----------------------------------------------------------------------------
  492. void CTFProjectile_Jar::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  493. {
  494. BaseClass::VPhysicsCollision( index, pEvent );
  495. int otherIndex = !index;
  496. CBaseEntity *pHitEntity = pEvent->pEntities[otherIndex];
  497. if ( !pHitEntity )
  498. return;
  499. if ( pHitEntity->IsWorld() )
  500. {
  501. OnHitWorld();
  502. }
  503. // Break if we hit the world.
  504. bool bIsDynamicProp = ( NULL != dynamic_cast<CDynamicProp *>( pHitEntity ) );
  505. if ( ExplodesOnHit() && pHitEntity && ( pHitEntity->IsWorld() || bIsDynamicProp ) )
  506. {
  507. // Explode immediately next frame. (Can't explode in the collision callback.)
  508. m_vCollisionVelocity = pEvent->preVelocity[index];
  509. SetContextThink( &CTFProjectile_Jar::VPhysicsCollisionThink, gpGlobals->curtime, "JarCollisionThink" );
  510. }
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Purpose: Handles exploding after a vphysics collision has happened.
  514. // This prevents changing collision properties during the vphysics callback itself.
  515. //-----------------------------------------------------------------------------
  516. void CTFProjectile_Jar::VPhysicsCollisionThink( void )
  517. {
  518. if ( !ExplodesOnHit() )
  519. return;
  520. trace_t pTrace;
  521. Vector velDir = m_vCollisionVelocity;
  522. VectorNormalize( velDir );
  523. Vector vecSpot = GetAbsOrigin() - velDir * 16;
  524. UTIL_TraceLine( vecSpot, vecSpot + velDir * 32, MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
  525. Explode( &pTrace, GetDamageType() );
  526. }
  527. //-----------------------------------------------------------------------------
  528. bool CTFProjectile_Jar::PositionArrowOnBone( mstudiobbox_t *pBox, CBaseAnimating *pOtherAnim )
  529. {
  530. CStudioHdr *pStudioHdr = pOtherAnim->GetModelPtr();
  531. if ( !pStudioHdr )
  532. return false;
  533. mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pOtherAnim->GetHitboxSet() );
  534. if ( !set )
  535. return false;
  536. if ( !set->numhitboxes ) // Target must have hit boxes.
  537. return false;
  538. if ( pBox->bone < 0 || pBox->bone >= pStudioHdr->numbones() ) // Bone index must be valid.
  539. return false;
  540. CBoneCache *pCache = pOtherAnim->GetBoneCache();
  541. if ( !pCache )
  542. return false;
  543. matrix3x4_t *bone_matrix = pCache->GetCachedBone( pBox->bone );
  544. if ( !bone_matrix )
  545. return false;
  546. Vector vecBoxAbsMins, vecBoxAbsMaxs;
  547. TransformAABB( *bone_matrix, pBox->bbmin, pBox->bbmax, vecBoxAbsMins, vecBoxAbsMaxs );
  548. // Adjust the arrow so it isn't exactly in the center of the box.
  549. Vector position;
  550. Vector vecDelta = vecBoxAbsMaxs - vecBoxAbsMins;
  551. float frand = (float)rand() / VALVE_RAND_MAX;
  552. position.x = vecBoxAbsMins.x + vecDelta.x*0.6f - vecDelta.x*frand*0.2f;
  553. frand = (float)rand() / VALVE_RAND_MAX;
  554. position.y = vecBoxAbsMins.y + vecDelta.y*0.6f - vecDelta.y*frand*0.2f;
  555. frand = (float)rand() / VALVE_RAND_MAX;
  556. position.z = vecBoxAbsMins.z + vecDelta.z*0.6f - vecDelta.z*frand*0.2f;
  557. SetAbsOrigin( position );
  558. return true;
  559. }
  560. //-----------------------------------------------------------------------------
  561. void CTFProjectile_Jar::GetBoneAttachmentInfo( mstudiobbox_t *pBox, CBaseAnimating *pOtherAnim, Vector &bonePosition, QAngle &boneAngles, int &boneIndexAttached, int &physicsBoneIndex )
  562. {
  563. // Find a bone to stick to.
  564. matrix3x4_t arrowWorldSpace;
  565. MatrixCopy( EntityToWorldTransform(), arrowWorldSpace );
  566. // Get the bone info so we can follow the bone.
  567. boneIndexAttached = pBox->bone;
  568. physicsBoneIndex = pOtherAnim->GetPhysicsBone( boneIndexAttached );
  569. matrix3x4_t boneToWorld;
  570. pOtherAnim->GetBoneTransform( boneIndexAttached, boneToWorld );
  571. Vector attachedBonePos;
  572. QAngle attachedBoneAngles;
  573. pOtherAnim->GetBonePosition( boneIndexAttached, attachedBonePos, attachedBoneAngles );
  574. // Transform my current position/orientation into the hit bone's space.
  575. matrix3x4_t worldToBone, localMatrix;
  576. MatrixInvert( boneToWorld, worldToBone );
  577. ConcatTransforms( worldToBone, arrowWorldSpace, localMatrix );
  578. MatrixAngles( localMatrix, boneAngles, bonePosition );
  579. }
  580. //-----------------------------------------------------------------------------
  581. void CTFProjectile_Jar::CreateStickyAttachmentToTarget( CTFPlayer *pOwner, CTFPlayer *pVictim, trace_t *trace )
  582. {
  583. // Dont stick to the sky!
  584. if ( trace->surface.flags & SURF_SKY )
  585. {
  586. return;
  587. }
  588. // If I hit a player, remove the jar and replace with the face eater version
  589. CStudioHdr *pStudioHdr = NULL;
  590. mstudiohitboxset_t *set = NULL;
  591. pStudioHdr = pVictim->GetModelPtr();
  592. if ( pStudioHdr )
  593. {
  594. set = pStudioHdr->pHitboxSet( pVictim->GetHitboxSet() );
  595. }
  596. // Look for nearest hitbox
  597. mstudiobbox_t *closest_box = NULL;
  598. if ( trace->m_pEnt && trace->m_pEnt->GetTeamNumber() != GetTeamNumber() )
  599. {
  600. closest_box = set->pHitbox( trace->hitbox );
  601. }
  602. if ( closest_box )
  603. {
  604. if ( !PositionArrowOnBone( closest_box, pVictim ) )
  605. return;
  606. // See if we're supposed to stick in the target.
  607. Vector bonePosition = vec3_origin;
  608. QAngle boneAngles = QAngle( 0, 0, 0 );
  609. int boneIndexAttached = -1;
  610. int physicsBoneIndex = -1;
  611. GetBoneAttachmentInfo( closest_box, pVictim, bonePosition, boneAngles, boneIndexAttached, physicsBoneIndex );
  612. IGameEvent * event = gameeventmanager->CreateEvent( "arrow_impact" );
  613. if ( event )
  614. {
  615. event->SetInt( "attachedEntity", pVictim->entindex() );
  616. event->SetInt( "shooter", pOwner->entindex() );
  617. event->SetInt( "attachedEntity", pVictim->entindex() );
  618. event->SetInt( "boneIndexAttached", boneIndexAttached );
  619. event->SetFloat( "bonePositionX", bonePosition.x );
  620. event->SetFloat( "bonePositionY", bonePosition.y );
  621. event->SetFloat( "bonePositionZ", bonePosition.z );
  622. event->SetFloat( "boneAnglesX", boneAngles.x );
  623. event->SetFloat( "boneAnglesY", boneAngles.y );
  624. event->SetFloat( "boneAnglesZ", boneAngles.z );
  625. event->SetInt( "projectileType", GetProjectileType() );
  626. event->SetBool( "isCrit", IsCritical() );
  627. gameeventmanager->FireEvent( event );
  628. }
  629. }
  630. }
  631. #endif
  632. #ifdef CLIENT_DLL
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. const char *CTFProjectile_Jar::GetTrailParticleName( void )
  637. {
  638. if ( GetTeamNumber() == TF_TEAM_BLUE )
  639. {
  640. return "peejar_trail_blu";
  641. }
  642. else
  643. {
  644. return "peejar_trail_red";
  645. }
  646. }
  647. #endif
  648. #ifdef GAME_DLL
  649. //-----------------------------------------------------------------------------
  650. // Purpose:
  651. //-----------------------------------------------------------------------------
  652. CTFProjectile_Jar *CTFJarMilk::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity,
  653. const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  654. {
  655. return CTFProjectile_JarMilk::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo );
  656. }
  657. #endif
  658. //-----------------------------------------------------------------------------
  659. // Purpose:
  660. //-----------------------------------------------------------------------------
  661. void CTFProjectile_JarMilk::Precache()
  662. {
  663. PrecacheModel( TF_WEAPON_MILKJAR_MODEL );
  664. BaseClass::Precache();
  665. }
  666. #ifdef GAME_DLL
  667. //-----------------------------------------------------------------------------
  668. // Purpose:
  669. //-----------------------------------------------------------------------------
  670. CTFProjectile_JarMilk* CTFProjectile_JarMilk::Create( const Vector &position, const QAngle &angles,
  671. const Vector &velocity, const AngularImpulse &angVelocity,
  672. CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  673. {
  674. CTFProjectile_JarMilk *pGrenade = static_cast<CTFProjectile_JarMilk*>( CBaseEntity::CreateNoSpawn( "tf_projectile_jar_milk", position, angles, pOwner ) );
  675. if ( pGrenade )
  676. {
  677. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  678. pGrenade->SetPipebombMode();
  679. DispatchSpawn( pGrenade );
  680. pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo );
  681. #ifdef _X360
  682. if ( pGrenade->m_iType != TF_GL_MODE_REMOTE_DETONATE )
  683. {
  684. pGrenade->SetDamage( TF_WEAPON_GRENADE_XBOX_DAMAGE );
  685. }
  686. #endif
  687. pGrenade->m_flFullDamage = 0;
  688. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  689. }
  690. return pGrenade;
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose:
  694. //-----------------------------------------------------------------------------
  695. void CTFProjectile_JarMilk::SetCustomPipebombModel()
  696. {
  697. // Check for Model Override
  698. int iProjectile = 0;
  699. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  700. if ( pThrower && pThrower->GetActiveWeapon() )
  701. {
  702. CALL_ATTRIB_HOOK_INT_ON_OTHER( pThrower->GetActiveWeapon(), iProjectile, override_projectile_type );
  703. switch ( iProjectile )
  704. {
  705. case TF_PROJECTILE_BREADMONSTER_JARATE:
  706. case TF_PROJECTILE_BREADMONSTER_MADMILK:
  707. m_iProjectileType = iProjectile;
  708. SetModel( "models/weapons/c_models/c_breadmonster/c_breadmonster_milk.mdl" );
  709. return;
  710. }
  711. }
  712. SetModel( TF_WEAPON_MILKJAR_MODEL );
  713. }
  714. #endif
  715. #ifdef CLIENT_DLL
  716. //-----------------------------------------------------------------------------
  717. // Purpose:
  718. //-----------------------------------------------------------------------------
  719. const char* CTFJarMilk::ModifyEventParticles( const char* token )
  720. {
  721. if ( FStrEq( token, "energydrink_splash") )
  722. {
  723. CEconItemView *pItem = m_AttributeManager.GetItem();
  724. int iSystems = pItem->GetStaticData()->GetNumAttachedParticles( GetTeamNumber() );
  725. for ( int i = 0; i < iSystems; i++ )
  726. {
  727. attachedparticlesystem_t *pSystem = pItem->GetStaticData()->GetAttachedParticleData( GetTeamNumber(),i );
  728. if ( pSystem->iCustomType == 1 )
  729. {
  730. return pSystem->pszSystemName;
  731. }
  732. }
  733. }
  734. return BaseClass::ModifyEventParticles( token );
  735. }
  736. //-----------------------------------------------------------------------------
  737. // Purpose:
  738. //-----------------------------------------------------------------------------
  739. bool CTFJarMilk::Holster( CBaseCombatWeapon *pSwitchingTo )
  740. {
  741. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  742. if ( pOwner && pOwner->IsLocalPlayer() )
  743. {
  744. C_BaseEntity *pParticleEnt = pOwner->GetViewModel(0);
  745. if ( pParticleEnt )
  746. {
  747. pOwner->StopViewModelParticles( pParticleEnt );
  748. }
  749. }
  750. return BaseClass::Holster( pSwitchingTo );
  751. }
  752. #endif
  753. #ifdef GAME_DLL
  754. //-----------------------------------------------------------------------------
  755. // Purpose:
  756. //-----------------------------------------------------------------------------
  757. Vector CTFCleaver::GetVelocityVector( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp )
  758. {
  759. Vector vecVelocity;
  760. // Calculate the initial impulse on the item.
  761. vecVelocity = Vector( 0.0f, 0.0f, 0.0f );
  762. vecVelocity += vecForward * 10;
  763. vecVelocity += vecUp * 1;
  764. VectorNormalize( vecVelocity );
  765. vecVelocity *= 3000;
  766. return vecVelocity;
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose:
  770. //-----------------------------------------------------------------------------
  771. CTFProjectile_Jar *CTFCleaver::CreateJarProjectile( const Vector &position, const QAngle &angles, const Vector &velocity,
  772. const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  773. {
  774. return CTFProjectile_Cleaver::Create( position, angles, velocity, angVelocity, pOwner, weaponInfo, GetSkin() );
  775. }
  776. #endif
  777. //-----------------------------------------------------------------------------
  778. // Purpose:
  779. //-----------------------------------------------------------------------------
  780. float CTFCleaver::GetProjectileSpeed( void )
  781. {
  782. return TF_CLEAVER_LAUNCH_SPEED;
  783. }
  784. //-----------------------------------------------------------------------------
  785. void CTFCleaver::SecondaryAttack( void )
  786. {
  787. PrimaryAttack();
  788. }
  789. #ifdef CLIENT_DLL
  790. //-----------------------------------------------------------------------------
  791. // Purpose:
  792. //-----------------------------------------------------------------------------
  793. const char* CTFCleaver::ModifyEventParticles( const char* token )
  794. {
  795. return NULL;
  796. }
  797. //-----------------------------------------------------------------------------
  798. // Purpose:
  799. //-----------------------------------------------------------------------------
  800. bool CTFCleaver::Holster( CBaseCombatWeapon *pSwitchingTo )
  801. {
  802. return BaseClass::Holster( pSwitchingTo );
  803. }
  804. #endif
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. //-----------------------------------------------------------------------------
  808. void CTFProjectile_Cleaver::Precache()
  809. {
  810. PrecacheModel( TF_WEAPON_CLEAVER_MODEL );
  811. PrecacheScriptSound( TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND );
  812. PrecacheScriptSound( TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND );
  813. BaseClass::Precache();
  814. }
  815. //-----------------------------------------------------------------------------
  816. // Purpose:
  817. //-----------------------------------------------------------------------------
  818. void CTFProjectile_Cleaver::SetCustomPipebombModel()
  819. {
  820. SetModel( TF_WEAPON_CLEAVER_MODEL );
  821. }
  822. CTFProjectile_Cleaver::CTFProjectile_Cleaver()
  823. {
  824. #ifdef GAME_DLL
  825. m_bHitPlayer = false;
  826. m_bSoundPlayed = false;
  827. #endif
  828. }
  829. #ifdef GAME_DLL
  830. #define FLIGHT_TIME_TO_MAX_DMG 1.f
  831. //-----------------------------------------------------------------------------
  832. // Purpose:
  833. //-----------------------------------------------------------------------------
  834. void CTFProjectile_Cleaver::OnHit( CBaseEntity *pOther )
  835. {
  836. SetModelName( NULL_STRING );//invisible
  837. AddSolidFlags( FSOLID_NOT_SOLID );
  838. CTFPlayer *pOwner = ToTFPlayer( GetThrower() );
  839. if ( !pOwner )
  840. return;
  841. if ( !pOther || !pOther->IsPlayer() )
  842. return;
  843. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  844. if ( !pPlayer )
  845. return;
  846. // Can't bleed an invul player.
  847. if ( pPlayer->m_Shared.IsInvulnerable() || pPlayer->m_Shared.InCond( TF_COND_INVULNERABLE_WEARINGOFF ) )
  848. return;
  849. if ( pPlayer->GetTeamNumber() == pOwner->GetTeamNumber() )
  850. return;
  851. if ( TFGameRules() && TFGameRules()->IsTruceActive() && pOwner->IsTruceValidForEnt() )
  852. return;
  853. bool bIsCriticalHit = IsCritical();
  854. bool bIsMiniCrit = false;
  855. float flBleedTime = 5.0f;
  856. float flLifeTime = gpGlobals->curtime - m_flCreationTime;
  857. if ( flLifeTime >= FLIGHT_TIME_TO_MAX_DMG )
  858. {
  859. bIsMiniCrit = true;
  860. }
  861. // just do the bleed effect directly since the bleed
  862. // attribute comes from the inflictor, which is the cleaver.
  863. pPlayer->m_Shared.MakeBleed( pOwner, (CTFCleaver *)GetLauncher(), flBleedTime );
  864. // Give 'em a love tap.
  865. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  866. trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
  867. CBaseEntity *pInflictor = GetLauncher();
  868. CTakeDamageInfo info;
  869. info.SetAttacker( pOwner );
  870. info.SetInflictor( pInflictor );
  871. info.SetWeapon( pInflictor );
  872. info.SetDamage( GetDamage() );
  873. info.SetDamageCustom( bIsMiniCrit ? TF_DMG_CUSTOM_CLEAVER_CRIT : TF_DMG_CUSTOM_CLEAVER );
  874. info.SetDamagePosition( GetAbsOrigin() );
  875. int iDamageType = GetDamageType();
  876. if ( bIsCriticalHit )
  877. {
  878. iDamageType |= DMG_CRITICAL;
  879. }
  880. info.SetDamageType( iDamageType );
  881. // Hurt 'em.
  882. Vector dir;
  883. AngleVectors( GetAbsAngles(), &dir );
  884. pPlayer->DispatchTraceAttack( info, dir, pNewTrace );
  885. ApplyMultiDamage();
  886. // sound effects
  887. EmitSound_t params;
  888. params.m_flSoundTime = 0;
  889. params.m_pflSoundDuration = 0;
  890. params.m_pSoundName = TF_WEAPON_CLEAVER_IMPACT_FLESH_SOUND;
  891. CPASFilter filter( GetAbsOrigin() );
  892. filter.RemoveRecipient( pOwner );
  893. EmitSound( filter, entindex(), params );
  894. CSingleUserRecipientFilter attackerFilter( pOwner );
  895. EmitSound( attackerFilter, pOwner->entindex(), params );
  896. AddEffects( EF_NODRAW );
  897. SetAbsVelocity( vec3_origin );
  898. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime + 2, "RemoveThink" );
  899. SetTouch( NULL );
  900. m_bHitPlayer = true;
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Purpose:
  904. //-----------------------------------------------------------------------------
  905. void CTFProjectile_Cleaver::Explode( trace_t *pTrace, int bitsDamageType )
  906. {
  907. if ( !m_bHitPlayer )
  908. {
  909. if ( !m_bSoundPlayed )
  910. {
  911. EmitSound( TF_WEAPON_CLEAVER_IMPACT_WORLD_SOUND );
  912. m_bSoundPlayed = true;
  913. }
  914. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime + 2, "RemoveThink" );
  915. SetTouch( NULL );
  916. }
  917. }
  918. //-----------------------------------------------------------------------------
  919. // Purpose:
  920. //-----------------------------------------------------------------------------
  921. void CTFProjectile_Cleaver::Detonate( void )
  922. {
  923. trace_t tr;
  924. Vector vecSpot;// trace starts here!
  925. SetThink( NULL );
  926. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );
  927. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr);
  928. Explode( &tr, GetDamageType() );
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Purpose:
  932. //-----------------------------------------------------------------------------
  933. CTFProjectile_Cleaver* CTFProjectile_Cleaver::Create( const Vector &position, const QAngle &angles,
  934. const Vector &velocity, const AngularImpulse &angVelocity,
  935. CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo, int nSkin )
  936. {
  937. CTFProjectile_Cleaver *pGrenade = static_cast<CTFProjectile_Cleaver*>( CBaseEntity::CreateNoSpawn( "tf_projectile_cleaver", position, angles, pOwner ) );
  938. if ( pGrenade )
  939. {
  940. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  941. pGrenade->SetPipebombMode();
  942. DispatchSpawn( pGrenade );
  943. pGrenade->m_nSkin = nSkin;
  944. pGrenade->InitGrenade( velocity, angVelocity, pOwner, weaponInfo );
  945. pGrenade->m_flFullDamage = 0;
  946. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  947. }
  948. return pGrenade;
  949. }
  950. #else
  951. //-----------------------------------------------------------------------------
  952. // Purpose:
  953. //-----------------------------------------------------------------------------
  954. const char *CTFProjectile_Cleaver::GetTrailParticleName( void )
  955. {
  956. if ( GetTeamNumber() == TF_TEAM_BLUE )
  957. {
  958. return "peejar_trail_blu_glow";
  959. }
  960. else
  961. {
  962. return "peejar_trail_red_glow";
  963. }
  964. }
  965. #endif // GAME_DLL