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.

3546 lines
112 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_spellbook.h"
  8. #include "decals.h"
  9. #include "tf_gamerules.h"
  10. #include "tf_pumpkin_bomb.h"
  11. // Client specific.
  12. #ifdef CLIENT_DLL
  13. #include "c_basedoor.h"
  14. #include "c_tf_player.h"
  15. #include "IEffects.h"
  16. #include "bone_setup.h"
  17. #include "c_tf_gamestats.h"
  18. #include "iclientmode.h"
  19. #include <vgui_controls/AnimationController.h>
  20. #include "econ_notifications.h"
  21. #include "gc_clientsystem.h"
  22. #include "tf_logic_halloween_2014.h"
  23. #include "tf_hud_itemeffectmeter.h"
  24. extern void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  25. // Server specific.
  26. #else
  27. #include "doors.h"
  28. #include "tf_player.h"
  29. #include "tf_ammo_pack.h"
  30. #include "tf_gamestats.h"
  31. #include "ilagcompensationmanager.h"
  32. #include "collisionutils.h"
  33. #include "particle_parse.h"
  34. #include "tf_projectile_base.h"
  35. #include "tf_gamerules.h"
  36. #include "tf_fx.h"
  37. #include "takedamageinfo.h"
  38. #include "halloween/zombie/zombie.h"
  39. #include "halloween/eyeball_boss/eyeball_boss.h"
  40. #include "halloween/halloween_base_boss.h"
  41. #include "entity_healthkit.h"
  42. #include "eyeball_boss/teleport_vortex.h"
  43. #include "in_buttons.h"
  44. #include "halloween/merasmus/merasmus.h"
  45. #include "tf_weapon_grenade_pipebomb.h"
  46. #include "tf_obj_dispenser.h"
  47. #endif
  48. ConVar tf_test_spellindex( "tf_test_spellindex", "-1", FCVAR_CHEAT | FCVAR_REPLICATED, "Set to index to always get a specific spell" );
  49. #ifdef GAME_DLL
  50. ConVar tf_halloween_kart_rocketspell_speed( "tf_halloween_kart_rocketspell_speed", "1500", FCVAR_CHEAT );
  51. ConVar tf_halloween_kart_rocketspell_lifetime( "tf_halloween_kart_rocketspell_lifetime", "0.5f", FCVAR_CHEAT );
  52. ConVar tf_halloween_kart_rocketspell_force( "tf_halloween_kart_rocketspell_force", "900.0f", FCVAR_CHEAT );
  53. #endif
  54. extern ConVar tf_eyeball_boss_hover_height;
  55. extern ConVar tf_halloween_kart_normal_speed;
  56. extern ConVar tf_halloween_kart_dash_speed;
  57. //=============================================================================
  58. //
  59. // Weapon Tables
  60. //
  61. // SpellBook --
  62. IMPLEMENT_NETWORKCLASS_ALIASED( TFSpellBook, DT_TFWeaponSpellBook )
  63. BEGIN_NETWORK_TABLE( CTFSpellBook, DT_TFWeaponSpellBook )
  64. #ifdef CLIENT_DLL
  65. RecvPropInt( RECVINFO( m_iSelectedSpellIndex ) ),
  66. RecvPropInt( RECVINFO( m_iSpellCharges ) ),
  67. RecvPropFloat( RECVINFO( m_flTimeNextSpell ) ),
  68. RecvPropBool( RECVINFO( m_bFiredAttack ) ),
  69. #else
  70. SendPropInt( SENDINFO( m_iSelectedSpellIndex ) ),
  71. SendPropInt( SENDINFO( m_iSpellCharges ) ),
  72. SendPropFloat( SENDINFO( m_flTimeNextSpell ) ),
  73. SendPropBool( SENDINFO( m_bFiredAttack ) ),
  74. #endif
  75. END_NETWORK_TABLE()
  76. BEGIN_PREDICTION_DATA( CTFSpellBook )
  77. END_PREDICTION_DATA()
  78. LINK_ENTITY_TO_CLASS( tf_weapon_spellbook, CTFSpellBook );
  79. PRECACHE_WEAPON_REGISTER( tf_weapon_spellbook );
  80. // -- SpellBook
  81. #define SPELL_EMPTY -1
  82. #define SPELL_UNKNOWN -2
  83. #define SPELL_BOXING_GLOVE "models/props_halloween/hwn_spell_boxing_glove.mdl"
  84. //=============================================================================
  85. // Spell Data Structures
  86. //=============================================================================
  87. enum SpellType_t
  88. {
  89. SPELL_ROCKET,
  90. SPELL_JAR, // Explodes on Contact
  91. SPELL_SELF,
  92. };
  93. struct spell_data_t
  94. {
  95. spell_data_t(
  96. const char *pSpellUiName,
  97. int iSpellCharges,
  98. SpellType_t eSpelltype,
  99. const char *pSpellEntityName,
  100. bool (*pCastSpell)(CTFPlayer*),
  101. const char *pszCastSound,
  102. float flSpeedScale,
  103. int iCastContext,
  104. int iSpellContext,
  105. const char *pIconName,
  106. bool bAutoCast = false
  107. ) {
  108. m_pSpellUiName = pSpellUiName;
  109. m_eSpellType = eSpelltype;
  110. m_pSpellEntityName = pSpellEntityName;
  111. m_iSpellCharges = iSpellCharges;
  112. m_pCastSpell = pCastSpell;
  113. m_pszCastSound = pszCastSound;
  114. m_flSpeedScale = flSpeedScale;
  115. m_iCastContext = iCastContext;
  116. m_iSpellContext = iSpellContext;
  117. m_pIconName = pIconName;
  118. m_bAutoCast = bAutoCast;
  119. }
  120. const char * m_pSpellUiName;
  121. SpellType_t m_eSpellType;
  122. const char *m_pSpellEntityName;
  123. int m_iSpellCharges;
  124. const char *m_pszCastSound;
  125. float m_flSpeedScale;
  126. int m_iCastContext; // context for the spell caster
  127. int m_iSpellContext; // context for enemies who witness the spell
  128. bool (*m_pCastSpell)(CTFPlayer*);
  129. const char *m_pIconName;
  130. bool m_bAutoCast;
  131. };
  132. static const spell_data_t g_NormalSpellList[] =
  133. {
  134. spell_data_t( "#TF_Spell_Fireball", 2, SPELL_ROCKET, "tf_projectile_spellfireball", NULL, "Halloween.spell_fireball_cast", 1.f,MP_CONCEPT_PLAYER_CAST_BOMB_HEAD_CURSE, MP_CONCEPT_PLAYER_SPELL_BOMB_HEAD_CURSE, "spellbook_fireball" ),
  135. spell_data_t( "#TF_Spell_Bats", 2, SPELL_JAR, "tf_projectile_spellbats", NULL, "Halloween.spell_bat_cast", 1.f, MP_CONCEPT_PLAYER_CAST_MERASMUS_ZAP, MP_CONCEPT_PLAYER_SPELL_MERASMUS_ZAP, "spellbook_bats" ),
  136. spell_data_t( "#TF_Spell_OverHeal", 1, SPELL_SELF, NULL, CTFSpellBook::CastSelfHeal, "Halloween.spell_overheal", 1.f, MP_CONCEPT_PLAYER_CAST_SELF_HEAL, MP_CONCEPT_PLAYER_SPELL_SELF_HEAL, "spellbook_overheal" ),
  137. spell_data_t( "#TF_Spell_MIRV", 1, SPELL_JAR, "tf_projectile_spellmirv", NULL, "Halloween.spell_mirv_cast", 1.f, MP_CONCEPT_PLAYER_CAST_MIRV, MP_CONCEPT_PLAYER_SPELL_MIRV, "spellbook_mirv" ),
  138. spell_data_t( "#TF_Spell_BlastJump", 2, SPELL_SELF, NULL, CTFSpellBook::CastRocketJump, "Halloween.spell_blastjump", 1.f, MP_CONCEPT_PLAYER_CAST_BLAST_JUMP, MP_CONCEPT_PLAYER_SPELL_BLAST_JUMP, "spellbook_blastjump"),
  139. spell_data_t( "#TF_Spell_Stealth", 1, SPELL_SELF, NULL, CTFSpellBook::CastSelfStealth, "Halloween.spell_stealth", 1.f, MP_CONCEPT_PLAYER_CAST_STEALTH, MP_CONCEPT_PLAYER_SPELL_STEALTH, "spellbook_stealth"),
  140. spell_data_t( "#TF_Spell_Teleport", 2, SPELL_JAR, "tf_projectile_spelltransposeteleport", NULL, "Halloween.spell_teleport", 1.f, MP_CONCEPT_PLAYER_CAST_TELEPORT, MP_CONCEPT_PLAYER_SPELL_TELEPORT, "spellbook_teleport"),
  141. };
  142. static const int g_NavMeshSpells = 2; // Number of spells in this list that require a navmesh, they must be at the end of this array
  143. static const spell_data_t g_RareSpellList[] =
  144. {
  145. spell_data_t( "#TF_Spell_LightningBall", 1, SPELL_ROCKET, "tf_projectile_lightningorb", NULL, "Halloween.spell_lightning_cast", 0.4f, MP_CONCEPT_PLAYER_CAST_LIGHTNING_BALL, MP_CONCEPT_PLAYER_SPELL_LIGHTNING_BALL, "spellbook_lightning"),
  146. spell_data_t( "#TF_Spell_Athletic", 1, SPELL_SELF, NULL, CTFSpellBook::CastSelfSpeedBoost, "Halloween.spell_athletic", 1.f, MP_CONCEPT_PLAYER_CAST_MOVEMENT_BUFF, MP_CONCEPT_PLAYER_SPELL_MOVEMENT_BUFF, "spellbook_athletic"),
  147. spell_data_t( "#TF_Spell_Meteor", 1, SPELL_JAR, "tf_projectile_spellmeteorshower", NULL, "Halloween.spell_meteor_cast", 1.f, MP_CONCEPT_PLAYER_CAST_METEOR_SWARM, MP_CONCEPT_PLAYER_SPELL_METEOR_SWARM, "spellbook_meteor"),
  148. spell_data_t( "#TF_Spell_SpawnBoss", 1, SPELL_JAR, "tf_projectile_spellspawnboss", NULL, "Halloween.Merasmus_Spell", 1.f, MP_CONCEPT_PLAYER_CAST_MONOCULOUS, MP_CONCEPT_PLAYER_SPELL_MONOCULOUS, "spellbook_boss"),
  149. spell_data_t( "#TF_Spell_SkeletonHorde", 1, SPELL_JAR, "tf_projectile_spellspawnhorde", NULL, "Halloween.spell_skeleton_horde_cast", 1.f, MP_CONCEPT_PLAYER_CAST_SKELETON_HORDE, MP_CONCEPT_PLAYER_SPELL_SKELETON_HORDE, "spellbook_skeleton"),
  150. };
  151. static const spell_data_t g_KartSpellList[] =
  152. {
  153. // Kart Spells
  154. spell_data_t( "#TF_Spell_Fireball", 1, SPELL_ROCKET, "tf_projectile_spellkartorb", NULL, "Halloween.spell_fireball_cast", 1.f, MP_CONCEPT_PLAYER_CAST_MERASMUS_ZAP, MP_CONCEPT_PLAYER_SPELL_MERASMUS_ZAP, "../hud/Punchglove_icon" ),
  155. spell_data_t( "#TF_Spell_BlastJump", 1, SPELL_SELF, NULL, CTFSpellBook::CastKartRocketJump, "Halloween.spell_blastjump", 1.f, MP_CONCEPT_PLAYER_CAST_BLAST_JUMP, MP_CONCEPT_PLAYER_SPELL_BLAST_JUMP, "../hud/Parachute_icon"),
  156. spell_data_t( "#TF_Spell_OverHeal", 1, SPELL_SELF, NULL, CTFSpellBook::CastKartUber, "Halloween.spell_overheal", 1.f, MP_CONCEPT_PLAYER_CAST_SELF_HEAL, MP_CONCEPT_PLAYER_SPELL_SELF_HEAL, "spellbook_overheal" ),
  157. spell_data_t( "#TF_Spell_BombHead", 1, SPELL_SELF, NULL, CTFSpellBook::CastKartBombHead, "Halloween.spell_overheal", 1.f, MP_CONCEPT_PLAYER_CAST_FIREBALL, MP_CONCEPT_PLAYER_SPELL_FIREBALL, "../hud/bombhead_icon" ),
  158. };
  159. // Do not allow all spells in doomsday
  160. static const int g_doomsdayNormalSpellIndexList[] =
  161. {
  162. 0, //Fireball
  163. 0, //Fireball x2
  164. 2, //overheal
  165. 4, //Jump
  166. 5, //Stealth
  167. };
  168. static const int g_doomsdayRareSpellIndexList[] =
  169. {
  170. ARRAYSIZE( g_NormalSpellList ) + 0, // Lightning
  171. ARRAYSIZE( g_NormalSpellList ) + 1, // Mini
  172. ARRAYSIZE( g_NormalSpellList ) + 2, // Meteor
  173. ARRAYSIZE( g_NormalSpellList ) + 0, // Lightning
  174. ARRAYSIZE( g_NormalSpellList ) + 1, // Mini
  175. ARRAYSIZE( g_NormalSpellList ) + 2, // Meteor
  176. ARRAYSIZE( g_NormalSpellList ) + 3 // Boss / Monoculus. Smaller chance
  177. };
  178. // Regular SpellList
  179. // teleport and summons removed
  180. static const int g_generalSpellIndexList[] =
  181. {
  182. 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
  183. ARRAYSIZE ( g_NormalSpellList ) + 0,
  184. ARRAYSIZE ( g_NormalSpellList ) + 1,
  185. ARRAYSIZE ( g_NormalSpellList ) + 2
  186. };
  187. int GetTotalSpellCount( CTFPlayer *pPlayer )
  188. {
  189. int iSpellCount = ARRAYSIZE( g_NormalSpellList ) + ARRAYSIZE( g_RareSpellList );
  190. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  191. {
  192. iSpellCount += ARRAYSIZE( g_KartSpellList );
  193. }
  194. return iSpellCount;
  195. }
  196. bool IsRareSpell( int iSpellIndex )
  197. {
  198. if ( tf_test_spellindex.GetInt() > 0 )
  199. {
  200. iSpellIndex = tf_test_spellindex.GetInt();
  201. }
  202. return ( ( iSpellIndex >= ARRAYSIZE( g_NormalSpellList ) ) && ( iSpellIndex < ARRAYSIZE( g_NormalSpellList ) + ARRAYSIZE( g_RareSpellList ) ) );
  203. }
  204. const spell_data_t* GetSpellData( int iSpellIndex )
  205. {
  206. if ( tf_test_spellindex.GetInt() > -1 )
  207. {
  208. iSpellIndex = tf_test_spellindex.GetInt();
  209. }
  210. if ( iSpellIndex < 0 )
  211. return NULL;
  212. const int nNormalSpellCount = ARRAYSIZE( g_NormalSpellList );
  213. if ( iSpellIndex < nNormalSpellCount )
  214. return &g_NormalSpellList[ iSpellIndex ];
  215. const int nRareSpellRange = nNormalSpellCount + ARRAYSIZE( g_RareSpellList );
  216. if ( iSpellIndex < nRareSpellRange )
  217. return &g_RareSpellList[ iSpellIndex - nNormalSpellCount ];
  218. const int nKartSpellRange = nRareSpellRange + ARRAYSIZE( g_KartSpellList );
  219. if ( iSpellIndex < nKartSpellRange )
  220. return &g_KartSpellList[ iSpellIndex - nRareSpellRange];
  221. return NULL;
  222. }
  223. int GetSpellIndexFromContext( int iContext )
  224. {
  225. const int nNormalSpellCount = ARRAYSIZE( g_NormalSpellList );
  226. for ( int i=0; i<nNormalSpellCount; ++i )
  227. {
  228. if ( g_NormalSpellList[i].m_iSpellContext == iContext )
  229. {
  230. return i;
  231. }
  232. }
  233. const int nRareSpellCount = ARRAYSIZE( g_RareSpellList );
  234. for ( int i=0; i<nRareSpellCount; ++i )
  235. {
  236. if ( g_RareSpellList[i].m_iSpellContext == iContext )
  237. {
  238. return i + nNormalSpellCount;
  239. }
  240. }
  241. return -1;
  242. }
  243. //=============================================================================
  244. #ifdef CLIENT_DLL
  245. //=============================================================================
  246. // Ui Hud
  247. //=============================================================================
  248. extern ConVar cl_hud_minmode;
  249. DECLARE_HUDELEMENT_DEPTH( CHudSpellMenu, 2 );
  250. CHudSpellMenu::CHudSpellMenu( const char *pElementName ) : CHudElement( pElementName ), BaseClass ( NULL, "HudSpellMenu" )
  251. {
  252. Panel *pParent = g_pClientMode->GetViewport();
  253. SetParent( pParent );
  254. SetHiddenBits( HIDEHUD_MISCSTATUS | HIDEHUD_HEALTH | HIDEHUD_PLAYERDEAD );
  255. m_iNextRollTime = 0;
  256. m_flRollTickGap = 0.05f;
  257. m_bTickSoundA = false;
  258. m_bKillstreakMeterDrawing = false;
  259. m_pSpellIcon = new vgui::ImagePanel( this, "SpellIcon" );
  260. m_pKeyBinding = new CExLabel( this, "ActionText", "" );
  261. ListenForGameEvent( "inventory_updated" );
  262. ListenForGameEvent( "localplayer_respawn" );
  263. ListenForGameEvent( "localplayer_changeclass" );
  264. ListenForGameEvent( "post_inventory_application" );
  265. }
  266. //-----------------------------------------------------------------------------
  267. void CHudSpellMenu::ApplySchemeSettings( vgui::IScheme *pScheme )
  268. {
  269. BaseClass::ApplySchemeSettings( pScheme );
  270. KeyValues *pConditions = NULL;
  271. if ( m_bKillstreakMeterDrawing )
  272. {
  273. pConditions = new KeyValues( "conditions" );
  274. if ( pConditions )
  275. {
  276. AddSubKeyNamed( pConditions, "if_killstreak_visible" );
  277. }
  278. }
  279. // load control settings...
  280. LoadControlSettings( "resource/UI/HudSpellSelection.res", NULL, NULL, pConditions );
  281. SetVisible( false );
  282. UpdateSpellText( -1, -1 );
  283. if ( pConditions )
  284. {
  285. pConditions->deleteThis();
  286. }
  287. }
  288. //=============================================================================
  289. void CHudSpellMenu::OnTick( void )
  290. {
  291. bool bKillstreakMeterDrawing = false;
  292. CHudItemEffectMeter *pMeter = NULL;
  293. for ( int i = 0; i < IHudItemEffectMeterAutoList::AutoList().Count(); ++i )
  294. {
  295. pMeter = static_cast<CHudItemEffectMeter*>( IHudItemEffectMeterAutoList::AutoList()[i] );
  296. if ( pMeter->IsKillstreakMeter() ) // we found the killstreak meter
  297. {
  298. if ( pMeter->IsEnabled() )
  299. {
  300. bKillstreakMeterDrawing = true;
  301. }
  302. break;
  303. }
  304. }
  305. if ( m_bKillstreakMeterDrawing != bKillstreakMeterDrawing )
  306. {
  307. m_bKillstreakMeterDrawing = bKillstreakMeterDrawing;
  308. InvalidateLayout( false, true );
  309. }
  310. vgui::ivgui()->RemoveTickSignal( GetVPanel() );
  311. }
  312. //=============================================================================
  313. void CHudSpellMenu::FireGameEvent( IGameEvent * event )
  314. {
  315. if ( FStrEq( event->GetName(), "post_inventory_application" ) ||
  316. FStrEq( event->GetName(), "localplayer_respawn" ) ||
  317. FStrEq( event->GetName(), "localplayer_changeclass" ) ||
  318. FStrEq( event->GetName(), "inventory_updated" ) )
  319. {
  320. vgui::ivgui()->AddTickSignal( GetVPanel(), 10 );
  321. }
  322. }
  323. //=============================================================================
  324. bool CHudSpellMenu::ShouldDraw( void )
  325. {
  326. if ( TFGameRules() && TFGameRules()->IsUsingSpells() )
  327. {
  328. if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() && ( TFGameRules()->State_Get() != GR_STATE_RND_RUNNING ) )
  329. return false;
  330. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  331. if ( pPlayer && pPlayer->IsAlive() && !pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
  332. {
  333. CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( pPlayer->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  334. if ( pSpellBook )
  335. {
  336. UpdateSpellText( pSpellBook->m_iSelectedSpellIndex, pSpellBook->m_iSpellCharges );
  337. return CHudElement::ShouldDraw();
  338. }
  339. }
  340. }
  341. return false;
  342. }
  343. //=============================================================================
  344. void CHudSpellMenu::UpdateSpellText( int iSpellIndex, int iChargeCount )
  345. {
  346. if ( iSpellIndex == SPELL_EMPTY || ( iChargeCount <= 0 && iSpellIndex != SPELL_UNKNOWN ) )
  347. {
  348. SetDialogVariable( "counttext", "..." );
  349. //SetDialogVariable( "selectedspell", g_pVGuiLocalize->Find( pSpellData->m_pSpellUiName ) );
  350. m_pSpellIcon->SetImage( "spellbook_nospell" );
  351. m_flRollTickGap = 0.01f;
  352. m_iNextRollTime = 0;
  353. m_pKeyBinding->SetVisible( false );
  354. return;
  355. }
  356. m_pSpellIcon->SetVisible( true );
  357. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  358. if ( !pLocalPlayer )
  359. return;
  360. static wchar_t wLabel[256];
  361. if ( iSpellIndex == SPELL_UNKNOWN )
  362. {
  363. if ( m_iNextRollTime > gpGlobals->curtime )
  364. return;
  365. m_iNextRollTime = gpGlobals->curtime + m_flRollTickGap;
  366. m_flRollTickGap += 0.015f;
  367. static int s_iRandSpell = 0;
  368. s_iRandSpell = ( s_iRandSpell + 1 ) % GetTotalSpellCount( pLocalPlayer );
  369. const spell_data_t *pSpellData = GetSpellData( s_iRandSpell );
  370. SetDialogVariable( "counttext", "?" );
  371. m_pSpellIcon->SetImage( pSpellData->m_pIconName );
  372. pLocalPlayer->EmitSound( m_bTickSoundA ? "Halloween.spelltick_a" : "Halloween.spelltick_b" );
  373. m_bTickSoundA = !m_bTickSoundA;
  374. m_iPrevSelectedSpell = SPELL_UNKNOWN;
  375. m_pKeyBinding->SetVisible( false );
  376. }
  377. else
  378. {
  379. m_flRollTickGap = 0.01f;
  380. m_iNextRollTime = 0;
  381. const spell_data_t *pSpellData = GetSpellData( iSpellIndex );
  382. if ( pSpellData )
  383. {
  384. SetDialogVariable( "counttext", iChargeCount );
  385. m_pSpellIcon->SetImage( pSpellData->m_pIconName );
  386. if ( m_iPrevSelectedSpell != iSpellIndex && iSpellIndex != SPELL_EMPTY )
  387. {
  388. pLocalPlayer->EmitSound( "Halloween.spelltick_set" );
  389. }
  390. m_iPrevSelectedSpell = iSpellIndex;
  391. m_pKeyBinding->SetVisible( !cl_hud_minmode.GetBool() );
  392. // Action Key Text
  393. wchar_t wKeyReplaced[256];
  394. UTIL_ReplaceKeyBindings( g_pVGuiLocalize->Find( "#TF_Spell_Action" ), 0, wKeyReplaced, sizeof( wKeyReplaced ) );
  395. SetDialogVariable( "actiontext", wKeyReplaced );
  396. }
  397. }
  398. }
  399. //-----------------------------------------------------------------------------
  400. // CEquipSpellbookNotification
  401. //-----------------------------------------------------------------------------
  402. void CEquipSpellbookNotification::Accept()
  403. {
  404. m_bHasTriggered = true;
  405. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  406. if ( !pLocalInv )
  407. {
  408. MarkForDeletion();
  409. return;
  410. }
  411. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  412. if ( !pLocalPlayer )
  413. {
  414. MarkForDeletion();
  415. return;
  416. }
  417. // try to equip non-stock-spellbook first
  418. static CSchemaItemDefHandle pItemDef_Spellbook( "Basic Spellbook" );
  419. static CSchemaItemDefHandle pItemDef_Diary( "Secret Diary" );
  420. static CSchemaItemDefHandle pItemDef_FancySpellbook( "Halloween Spellbook" );
  421. Assert( pItemDef_Spellbook );
  422. Assert( pItemDef_Diary );
  423. Assert( pItemDef_FancySpellbook );
  424. CEconItemView *pSpellBook = NULL;
  425. if ( pItemDef_Spellbook && pItemDef_Diary && pItemDef_FancySpellbook )
  426. {
  427. for ( int i = 0 ; i < pLocalInv->GetItemCount() ; ++i )
  428. {
  429. CEconItemView *pItem = pLocalInv->GetItem( i );
  430. Assert( pItem );
  431. if ( pItem->GetItemDefinition() == pItemDef_Spellbook
  432. || pItem->GetItemDefinition() == pItemDef_Diary
  433. || pItem->GetItemDefinition() == pItemDef_FancySpellbook
  434. ) {
  435. pSpellBook = pItem;
  436. break;
  437. }
  438. }
  439. }
  440. // Default item becomes a spellbook in this mode
  441. itemid_t iItemId = INVALID_ITEM_ID;
  442. if ( pSpellBook )
  443. {
  444. iItemId = pSpellBook->GetItemID();
  445. }
  446. TFInventoryManager()->EquipItemInLoadout( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION, iItemId );
  447. // Tell the GC to tell server that we should respawn if we're in a respawn room
  448. GCSDK::CGCMsg< GCSDK::MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
  449. GCClientSystem()->BSendMessage( msg );
  450. MarkForDeletion();
  451. }
  452. //===========================================================================================
  453. void CEquipSpellbookNotification::UpdateTick()
  454. {
  455. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  456. if ( pLocalPlayer )
  457. {
  458. CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( pLocalPlayer->Weapon_OwnsThisID( TF_WEAPON_SPELLBOOK ) );
  459. if ( pSpellBook )
  460. {
  461. MarkForDeletion();
  462. }
  463. }
  464. }
  465. #endif // CLIENT_DLL
  466. //===========================================================================================
  467. //
  468. // CTFSpellBook
  469. //
  470. //===========================================================================================
  471. CTFSpellBook::CTFSpellBook()
  472. {
  473. m_iSelectedSpellIndex = -1;
  474. m_iSpellCharges = 0;
  475. m_flTimeNextSpell = 0;
  476. m_bFiredAttack = false;
  477. #ifdef CLIENT_DLL
  478. m_flTimeNextErrorSound = 0;
  479. m_hHandEffect = NULL;
  480. m_hHandEffectWeapon = NULL;
  481. #endif // CLIENT_DLL
  482. #ifdef GAME_DLL
  483. m_pStoredLastWpn = NULL;
  484. m_iPreviouslyCastSpell = -1;
  485. #endif // GAME_DLL
  486. }
  487. void CTFSpellBook::Precache()
  488. {
  489. PrecacheScriptSound( "Halloween.Merasmus_Spell" );
  490. PrecacheScriptSound( "Weapon_SniperRailgun_Large.SingleCrit" );
  491. PrecacheScriptSound( "Halloween.spelltick_a" );
  492. PrecacheScriptSound( "Halloween.spelltick_b" );
  493. PrecacheScriptSound( "Halloween.spelltick_set" );
  494. PrecacheScriptSound( "Halloween.spell_athletic" );
  495. PrecacheScriptSound( "Halloween.spell_bat_cast" );
  496. PrecacheScriptSound( "Halloween.spell_bat_impact" );
  497. PrecacheScriptSound( "Halloween.spell_blastjump" );
  498. PrecacheScriptSound( "Halloween.spell_fireball_cast" );
  499. PrecacheScriptSound( "Halloween.spell_fireball_impact" );
  500. PrecacheScriptSound( "Halloween.spell_lightning_cast" );
  501. PrecacheScriptSound( "Halloween.spell_lightning_impact" );
  502. PrecacheScriptSound( "Halloween.spell_meteor_cast" );
  503. PrecacheScriptSound( "Halloween.spell_meteor_impact" );
  504. PrecacheScriptSound( "Halloween.spell_mirv_cast" );
  505. PrecacheScriptSound( "Halloween.spell_mirv_explode_primary" );
  506. PrecacheScriptSound( "Halloween.spell_mirv_explode_secondary" );
  507. PrecacheScriptSound( "Halloween.spell_skeleton_horde_cast" );
  508. PrecacheScriptSound( "Halloween.spell_skeleton_horde_rise" );
  509. PrecacheScriptSound( "Halloween.spell_spawn_boss" );
  510. PrecacheScriptSound( "Halloween.spell_stealth" );
  511. PrecacheScriptSound( "Halloween.spell_teleport" );
  512. PrecacheScriptSound( "Halloween.spell_overheal" );
  513. PrecacheParticleSystem( "merasmus_zap" );
  514. PrecacheParticleSystem( "spell_cast_wheel_red" );
  515. PrecacheParticleSystem( "spell_cast_wheel_blue" );
  516. PrecacheParticleSystem( "Explosion_bubbles" );
  517. PrecacheParticleSystem( "ExplosionCore_buildings" );
  518. PrecacheParticleSystem( "water_splash01" );
  519. PrecacheParticleSystem( "healshot_trail_blue" );
  520. PrecacheParticleSystem( "healshot_trail_red" );
  521. PrecacheParticleSystem( "xms_snowburst" );
  522. PrecacheParticleSystem( "bomibomicon_ring" );
  523. PrecacheParticleSystem( "bombinomicon_burningdebris" );
  524. PrecacheParticleSystem( "merasmus_tp_bits" );
  525. PrecacheParticleSystem( "spell_fireball_tendril_parent_red" );
  526. PrecacheParticleSystem( "spell_fireball_tendril_parent_blue" );
  527. PrecacheParticleSystem( "spell_fireball_small_blue" );
  528. PrecacheParticleSystem( "spell_fireball_small_red" );
  529. PrecacheParticleSystem( "spell_lightningball_parent_blue" );
  530. PrecacheParticleSystem( "spell_lightningball_parent_red" );
  531. PrecacheParticleSystem( "spell_lightningball_hit_blue" );
  532. PrecacheParticleSystem( "spell_lightningball_hit_red" );
  533. PrecacheParticleSystem( "eyeboss_tp_vortex" );
  534. PrecacheParticleSystem( "spell_overheal_red" );
  535. PrecacheParticleSystem( "spell_overheal_blue" );
  536. PrecacheParticleSystem( "spell_teleport_red" );
  537. PrecacheParticleSystem( "spell_teleport_blue" );
  538. PrecacheParticleSystem( "spell_batball_red" );
  539. PrecacheParticleSystem( "spell_batball_blue" );
  540. PrecacheParticleSystem( "spell_batball_throw_red" );
  541. PrecacheParticleSystem( "spell_batball_throw_blue" );
  542. PrecacheParticleSystem( "spell_batball_impact_red" );
  543. PrecacheParticleSystem( "spell_batball_impact_blue" );
  544. PrecacheParticleSystem( "spell_pumpkin_mirv_goop_red" );
  545. PrecacheParticleSystem( "spell_pumpkin_mirv_goop_blue" );
  546. PrecacheParticleSystem( "spell_skeleton_goop_green" );
  547. PrecacheParticleSystem( "spellbook_rainbow" );
  548. PrecacheParticleSystem( "spellbook_major_burning" );
  549. PrecacheParticleSystem( "spellbook_minor_burning" );
  550. PrecacheModel( "models/props_mvm/mvm_human_skull_collide.mdl" );
  551. PrecacheModel( "models/props_lakeside_event/bomb_temp_hat.mdl" );
  552. PrecacheModel( SPELL_BOXING_GLOVE );
  553. PrecacheModel( "models/props_halloween/bombonomicon.mdl" ); // bomb head spell
  554. PrecacheParticleSystem( "halloween_rockettrail" );
  555. PrecacheParticleSystem( "ExplosionCore_MidAir" );
  556. #ifdef GAME_DLL
  557. CEyeballBoss::PrecacheEyeballBoss();
  558. CZombie::PrecacheZombie();
  559. #endif // GAME_DLL
  560. BaseClass::Precache();
  561. }
  562. //-----------------------------------------------------------------------------
  563. void CTFSpellBook::PrimaryAttack()
  564. {
  565. // cast spell
  566. if ( m_flTimeNextSpell > gpGlobals->curtime )
  567. return;
  568. CTFPlayer *pPlayer = GetTFPlayerOwner();
  569. if ( !pPlayer )
  570. return;
  571. bool bCastSuccessful = false;
  572. bCastSuccessful = CanCastSpell( pPlayer );
  573. if ( bCastSuccessful )
  574. {
  575. #ifdef GAME_DLL
  576. SpeakSpellConceptIfAllowed();
  577. // We need to do this before PrimaryAttack so we use the right spell index
  578. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  579. {
  580. CastKartSpell();
  581. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_KART_ACTION_SHOOT );
  582. }
  583. else
  584. {
  585. CastSpell( pPlayer, m_iSelectedSpellIndex );
  586. BaseClass::PrimaryAttack();
  587. }
  588. #endif
  589. #ifdef GAME_DLL
  590. // set a default time cast time if none added
  591. if ( m_flTimeNextSpell < gpGlobals->curtime )
  592. {
  593. m_flTimeNextSpell = gpGlobals->curtime + 0.5f;
  594. }
  595. #endif // GAME_DLL
  596. }
  597. #ifdef CLIENT_DLL
  598. else
  599. {
  600. if ( m_flTimeNextErrorSound < gpGlobals->curtime )
  601. {
  602. m_flTimeNextErrorSound = gpGlobals->curtime + 0.5f;
  603. pPlayer->EmitSound( "Player.DenyWeaponSelection" );
  604. }
  605. }
  606. #endif // CLIENT_DLL
  607. }
  608. //-----------------------------------------------------------------------------
  609. void CTFSpellBook::ItemBusyFrame( void )
  610. {
  611. #ifdef CLIENT_DLL
  612. if ( m_hHandEffectWeapon && m_hHandEffect )
  613. return;
  614. CTFPlayer *pPlayer = GetTFPlayerOwner();
  615. if ( !pPlayer )
  616. return;
  617. if ( IsFirstPersonView() )
  618. {
  619. m_hHandEffectWeapon = pPlayer->GetViewModel();
  620. }
  621. else
  622. {
  623. m_hHandEffectWeapon = pPlayer;
  624. }
  625. if ( !m_hHandEffectWeapon )
  626. return;
  627. if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
  628. {
  629. // Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
  630. return;
  631. }
  632. C_BaseAnimating* pBase = (C_BaseAnimating*)m_hHandEffectWeapon.Get();
  633. int iAttachment = pBase->C_BaseAnimating::LookupAttachment( "effect_hand_R" );
  634. // Start the muzzle flash, if a system hasn't already been started.
  635. if ( iAttachment > 0 )
  636. {
  637. const char *pszEffectName = GetHandEffect( GetAttributeContainer()->GetItem(), m_iSelectedSpellIndex >= ARRAYSIZE( g_NormalSpellList ) );
  638. if ( pszEffectName )
  639. {
  640. m_hHandEffect = pBase->ParticleProp()->Create( pszEffectName, PATTACH_POINT_FOLLOW, iAttachment );
  641. }
  642. }
  643. else
  644. {
  645. if ( m_hHandEffect )
  646. {
  647. m_hHandEffectWeapon->ParticleProp()->StopEmission( m_hHandEffect );
  648. m_hHandEffectWeapon = NULL;
  649. m_hHandEffect = NULL;
  650. }
  651. }
  652. #endif
  653. }
  654. //-----------------------------------------------------------------------------
  655. void CTFSpellBook::ItemHolsterFrame( void )
  656. {
  657. #ifdef CLIENT_DLL
  658. if ( !m_hHandEffectWeapon )
  659. return;
  660. // Stop the muzzle flash.
  661. if ( m_hHandEffect )
  662. {
  663. m_hHandEffectWeapon->ParticleProp()->StopEmission( m_hHandEffect );
  664. m_hHandEffectWeapon = NULL;
  665. m_hHandEffect = NULL;
  666. }
  667. #endif
  668. #ifdef GAME_DLL
  669. m_bFiredAttack = false;
  670. #endif
  671. }
  672. //-----------------------------------------------------------------------------
  673. void CTFSpellBook::ItemPostFrame( void )
  674. {
  675. BaseClass::ItemPostFrame();
  676. #ifdef CLIENT_DLL
  677. // attempt to attack then switch back
  678. if ( !m_bFiredAttack && m_iSpellCharges > 0 )
  679. {
  680. PrimaryAttack();
  681. if ( m_hHandEffect )
  682. {
  683. m_hHandEffectWeapon->ParticleProp()->StopEmission( m_hHandEffect );
  684. m_hHandEffectWeapon = NULL;
  685. m_hHandEffect = NULL;
  686. }
  687. }
  688. #endif
  689. #ifdef GAME_DLL
  690. if ( tf_test_spellindex.GetInt() > -1 )
  691. {
  692. SetSelectedSpell( tf_test_spellindex.GetInt() );
  693. }
  694. // attempt to attack then switch back
  695. if ( !m_bFiredAttack && m_iSpellCharges > 0 )
  696. {
  697. PrimaryAttack();
  698. m_bFiredAttack = true;
  699. }
  700. else
  701. {
  702. if ( m_flTimeNextSpell > gpGlobals->curtime )
  703. return;
  704. CTFPlayer *pPlayer = GetTFPlayerOwner();
  705. if ( !pPlayer )
  706. return;
  707. if ( pPlayer->Weapon_Switch( pPlayer->GetLastWeapon() ) )
  708. {
  709. if ( m_pStoredLastWpn != NULL && pPlayer->Weapon_CanSwitchTo( m_pStoredLastWpn.Get() ) )
  710. {
  711. pPlayer->Weapon_SetLast( m_pStoredLastWpn.Get() );
  712. m_pStoredLastWpn = NULL;
  713. }
  714. else
  715. {
  716. pPlayer->Weapon_SetLast( NULL );
  717. }
  718. m_bFiredAttack = false;
  719. }
  720. }
  721. #endif //GAME_DLL
  722. }
  723. //-----------------------------------------------------------------------------
  724. /* static */ const char* CTFSpellBook::GetHandEffect( CEconItemView *pItem, int iTier )
  725. {
  726. // if fancy spellbook //1069
  727. int defIndex = pItem->GetItemDefIndex();
  728. if ( defIndex == 1069 )
  729. {
  730. if ( iTier > 0 )
  731. {
  732. return "spellbook_major_burning";
  733. }
  734. else
  735. {
  736. return "spellbook_minor_burning";
  737. }
  738. }
  739. else if ( defIndex == 5605 ) // secret diary
  740. {
  741. return "spellbook_rainbow";
  742. }
  743. else // else Basic SpellBook
  744. {
  745. if ( iTier > 0 )
  746. {
  747. return "spellbook_major_fire";
  748. }
  749. else
  750. {
  751. return "spellbook_minor_fire";
  752. }
  753. }
  754. }
  755. //-----------------------------------------------------------------------------
  756. bool CTFSpellBook::HasASpellWithCharges()
  757. {
  758. return tf_test_spellindex.GetInt() > -1 || m_iSpellCharges > 0 || m_iSelectedSpellIndex == SPELL_UNKNOWN;
  759. }
  760. //-----------------------------------------------------------------------------
  761. bool CTFSpellBook::CanCastSpell( CTFPlayer *pPlayer )
  762. {
  763. if ( !pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART) && !pPlayer->CanAttack() )
  764. return false;
  765. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_THRILLER ) )
  766. return false;
  767. if ( tf_test_spellindex.GetInt() > -1 && tf_test_spellindex.GetInt() < GetTotalSpellCount( pPlayer ) )
  768. return true;
  769. return m_iSpellCharges > 0 && m_iSelectedSpellIndex >= 0 && m_iSelectedSpellIndex < GetTotalSpellCount( pPlayer );
  770. }
  771. //-----------------------------------------------------------------------------
  772. void CTFSpellBook::PaySpellCost( CTFPlayer *pPlayer )
  773. {
  774. m_iSpellCharges--;
  775. }
  776. //-----------------------------------------------------------------------------
  777. void CTFSpellBook::ClearSpell()
  778. {
  779. m_iSpellCharges = 0;
  780. #ifdef GAME_DLL
  781. // If rolling for a spell, clear that too
  782. m_iNextSpell = SPELL_EMPTY;
  783. #endif // GAME_DLL
  784. }
  785. //-----------------------------------------------------------------------------
  786. CBaseEntity *CTFSpellBook::FireJar( CTFPlayer *pPlayer )
  787. {
  788. #ifdef GAME_DLL
  789. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  790. {
  791. TossJarThink();
  792. }
  793. else
  794. {
  795. SetContextThink( &CTFJar::TossJarThink, gpGlobals->curtime + 0.01f, "TOSS_JAR_THINK" );
  796. }
  797. #endif
  798. return NULL;
  799. }
  800. #ifdef GAME_DLL
  801. //-----------------------------------------------------------------------------
  802. void CTFSpellBook::TossJarThink( void )
  803. {
  804. CTFPlayer *pPlayer = GetTFPlayerOwner();
  805. if ( !pPlayer )
  806. return;
  807. // Self casts
  808. const spell_data_t *pSpellData = GetSpellData( m_iPreviouslyCastSpell );
  809. if ( !pSpellData )
  810. return;
  811. if ( pSpellData->m_eSpellType == SPELL_SELF )
  812. {
  813. // Self casts
  814. if ( TFGameRules() )
  815. {
  816. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( pSpellData->m_iSpellContext, ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  817. }
  818. // Play a sound immediately for self-cast spells
  819. EmitSound( pSpellData->m_pszCastSound );
  820. pSpellData->m_pCastSpell( pPlayer );
  821. return;
  822. }
  823. Vector vecForward, vecRight, vecUp;
  824. AngleVectors( pPlayer->EyeAngles(), &vecForward, &vecRight, &vecUp );
  825. float fRight = 8.f;
  826. if ( IsViewModelFlipped() )
  827. {
  828. fRight *= -1;
  829. }
  830. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  831. // Make spell toss position at the hand
  832. vecSrc = vecSrc + (vecUp * -9.0f) + (vecRight * 7.0f) + (vecForward * 3.0f);
  833. Vector vecVelocity = GetVelocityVector( vecForward, vecRight, vecUp ) * pSpellData->m_flSpeedScale;
  834. QAngle angForward = pPlayer->EyeAngles();
  835. // Halloween Hack
  836. // Eye Angles slighty higher
  837. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  838. {
  839. // Add More up for Jar
  840. angForward = pPlayer->GetAbsAngles();
  841. if ( pSpellData->m_eSpellType == SPELL_JAR )
  842. {
  843. angForward.x -= 10.0f;
  844. }
  845. AngleVectors( angForward, &vecForward, &vecRight, &vecUp );
  846. vecVelocity = vecForward * tf_halloween_kart_rocketspell_speed.GetFloat();
  847. }
  848. trace_t trace;
  849. Vector vecEye = pPlayer->EyePosition();
  850. CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
  851. UTIL_TraceHull( vecEye, vecSrc, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
  852. // If we started in solid, don't let them fire at all
  853. if ( trace.startsolid )
  854. return;
  855. // Play a sound when we actually cast the projectile
  856. EmitSound( pSpellData->m_pszCastSound );
  857. switch ( pSpellData->m_eSpellType )
  858. {
  859. case SPELL_ROCKET :
  860. {
  861. //QAngle angForward;
  862. //GetProjectileFireSetup( pPlayer, Vector(0,0,0), &vecSrc, &angForward, false );
  863. CreateSpellRocket( trace.endpos, angForward, vecVelocity, GetAngularImpulse(), pPlayer, GetTFWpnData() );
  864. }
  865. break;
  866. case SPELL_JAR :
  867. {
  868. CreateSpellJar( trace.endpos, angForward, vecVelocity, GetAngularImpulse(), pPlayer, GetTFWpnData() );
  869. }
  870. break;
  871. case SPELL_SELF :
  872. break;
  873. }
  874. }
  875. //-----------------------------------------------------------------------------
  876. // Purpose:
  877. //-----------------------------------------------------------------------------
  878. void CTFSpellBook::CreateSpellRocket( const Vector &position, const QAngle &angles, const Vector &velocity,
  879. const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  880. {
  881. const spell_data_t* pSpellData = GetSpellData( m_iPreviouslyCastSpell );
  882. if ( !pSpellData )
  883. {
  884. return;
  885. }
  886. ASSERT( pSpellData->m_eSpellType == SPELL_ROCKET );
  887. CTFProjectile_Rocket *pRocket = static_cast<CTFProjectile_Rocket*>( CBaseEntity::CreateNoSpawn( pSpellData->m_pSpellEntityName, position, angles, pOwner ) );
  888. if ( pRocket )
  889. {
  890. pRocket->SetOwnerEntity( pOwner );
  891. pRocket->SetLauncher( this );
  892. Vector vForward;
  893. AngleVectors( angles, &vForward, NULL, NULL );
  894. pRocket->SetAbsVelocity( vForward * velocity.Length() );
  895. pRocket->SetDamage( weaponInfo.GetWeaponData(TF_WEAPON_PRIMARY_MODE).m_nDamage );
  896. pRocket->ChangeTeam( pOwner ? pOwner->GetTeamNumber() : TEAM_UNASSIGNED );
  897. IPhysicsObject *pPhysicsObject = pRocket->VPhysicsGetObject();
  898. if ( pPhysicsObject )
  899. {
  900. pPhysicsObject->AddVelocity( &velocity, &angVelocity );
  901. }
  902. DispatchSpawn( pRocket );
  903. }
  904. }
  905. //-----------------------------------------------------------------------------
  906. void CTFSpellBook::CreateSpellJar( const Vector &position, const QAngle &angles, const Vector &velocity,
  907. const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo )
  908. {
  909. const spell_data_t* pSpellData = GetSpellData( m_iPreviouslyCastSpell );
  910. if ( !pSpellData )
  911. {
  912. return;
  913. }
  914. ASSERT( pSpellData->m_eSpellType == SPELL_JAR );
  915. CTFProjectile_Jar *pGrenade = static_cast<CTFProjectile_Jar*>( CBaseEntity::CreateNoSpawn( pSpellData->m_pSpellEntityName, position, angles, pOwner ) );
  916. if ( pGrenade )
  917. {
  918. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  919. pGrenade->SetPipebombMode();
  920. DispatchSpawn( pGrenade );
  921. IPhysicsObject *pPhys = pGrenade->VPhysicsGetObject();
  922. if ( pPhys )
  923. {
  924. pPhys->SetMass( 5.0f );
  925. }
  926. pGrenade->InitGrenade( velocity, vec3_origin, pOwner, weaponInfo );
  927. pGrenade->m_flFullDamage = 0;
  928. pGrenade->ApplyLocalAngularVelocityImpulse( vec3_origin );
  929. }
  930. }
  931. //-----------------------------------------------------------------------------
  932. void CTFSpellBook::RollNewSpell( int iTier, bool bForceReroll /*= false*/ )
  933. {
  934. // do not do anything if we already have a spell for low tier, always roll for high tier
  935. if ( m_iSpellCharges > 0 && iTier == 0 && !bForceReroll )
  936. return;
  937. CTFPlayer *pPlayer = GetTFPlayerOwner();
  938. if ( !pPlayer )
  939. return;
  940. int iNextSpell = SPELL_EMPTY;
  941. // Halloween 2014
  942. // This is dumb, make spell lists better somehow
  943. if ( pPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_KART ) )
  944. {
  945. iNextSpell = RandomInt( ARRAYSIZE( g_NormalSpellList ) + ARRAYSIZE( g_RareSpellList ), GetTotalSpellCount( pPlayer ) - 1 );
  946. }
  947. else if ( iTier == 0 )
  948. {
  949. // Doomsday has special spell list
  950. if ( TFGameRules() && TFGameRules()->GetHalloweenScenario( ) == CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY )
  951. {
  952. iNextSpell = g_doomsdayNormalSpellIndexList[ RandomInt( 0, ARRAYSIZE( g_doomsdayNormalSpellIndexList ) - 1 ) ];
  953. }
  954. // Helltower has normal spell list
  955. else if ( TFGameRules() && TFGameRules()->GetHalloweenScenario( ) == CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER )
  956. {
  957. iNextSpell = RandomInt( 0, ARRAYSIZE( g_NormalSpellList ) - 1 );
  958. }
  959. // everyone else uses special list
  960. else
  961. {
  962. iNextSpell = g_generalSpellIndexList[ RandomInt( 0, ARRAYSIZE( g_generalSpellIndexList ) - 1 ) ];
  963. }
  964. }
  965. else // rare spell should not be the else
  966. {
  967. // Doomsday has special spell list
  968. if ( TFGameRules() && TFGameRules()->GetHalloweenScenario( ) == CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY )
  969. {
  970. iNextSpell = g_doomsdayRareSpellIndexList[ RandomInt( 0, ARRAYSIZE( g_doomsdayRareSpellIndexList ) - 1 ) ];
  971. }
  972. else
  973. {
  974. // g_NavMeshSpells
  975. // If there's no Nav mesh do not allow the upper range of spells (summons)
  976. int iIndexReduction = 1;
  977. if ( ( TheNavMesh == NULL ) || ( TheNavMesh->GetNavAreaCount() <= 0 ) )
  978. {
  979. iIndexReduction += g_NavMeshSpells;
  980. }
  981. iNextSpell = RandomInt( ARRAYSIZE( g_NormalSpellList ), GetTotalSpellCount( pPlayer ) - 1 );
  982. }
  983. }
  984. const float flRollTime = 2.f;
  985. m_iNextSpell = iNextSpell;
  986. SetSelectedSpell( SPELL_UNKNOWN );
  987. SetContextThink( &CTFSpellBook::RollNewSpellFinish, gpGlobals->curtime + flRollTime, "SpellRollFinish" );
  988. }
  989. //-----------------------------------------------------------------------------
  990. void CTFSpellBook::RollNewSpellFinish( void )
  991. {
  992. SetSelectedSpell( m_iNextSpell );
  993. if ( m_iNextSpell < 0 )
  994. return;
  995. // response rules
  996. CTFPlayer *pPlayer = GetTFPlayerOwner();
  997. if ( !pPlayer )
  998. {
  999. return;
  1000. }
  1001. int iConcept = MP_CONCEPT_NONE;
  1002. if ( m_iNextSpell < ARRAYSIZE( g_NormalSpellList ) )
  1003. {
  1004. iConcept = MP_CONCEPT_PLAYER_SPELL_PICKUP_COMMON;
  1005. }
  1006. else
  1007. {
  1008. iConcept = MP_CONCEPT_PLAYER_SPELL_PICKUP_RARE;
  1009. }
  1010. if ( iConcept != MP_CONCEPT_NONE )
  1011. {
  1012. pPlayer->SpeakConceptIfAllowed( iConcept );
  1013. }
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. void CTFSpellBook::SetSelectedSpell( int index )
  1017. {
  1018. m_iSelectedSpellIndex = index;
  1019. const spell_data_t *pSpellData = GetSpellData( m_iSelectedSpellIndex );
  1020. m_iSpellCharges = pSpellData ? pSpellData->m_iSpellCharges : 0;
  1021. if ( pSpellData && pSpellData->m_bAutoCast )
  1022. {
  1023. PrimaryAttack();
  1024. }
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. void CTFSpellBook::SpeakSpellConceptIfAllowed()
  1028. {
  1029. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1030. if ( !pPlayer || m_iSpellCharges <= 0 )
  1031. return;
  1032. const spell_data_t *pSpellData = GetSpellData( m_iSelectedSpellIndex );
  1033. if ( pSpellData )
  1034. {
  1035. pPlayer->SpeakConceptIfAllowed( pSpellData->m_iCastContext );
  1036. }
  1037. }
  1038. //------------------------------------------------------------------------------------------------------------------------------------
  1039. // KART FUNCTIONS
  1040. //------------------------------------------------------------------------------------------------------------------------------------
  1041. void CTFSpellBook::CastKartSpell()
  1042. {
  1043. #ifdef GAME_DLL
  1044. // cast spell time
  1045. if ( m_flTimeNextSpell > gpGlobals->curtime )
  1046. return;
  1047. CTFPlayer *pPlayer = GetTFPlayerOwner();
  1048. if ( !pPlayer )
  1049. return;
  1050. if ( m_iSpellCharges <= 0 )
  1051. {
  1052. if ( tf_test_spellindex.GetInt() < 0 || tf_test_spellindex.GetInt() > GetTotalSpellCount( pPlayer ) )
  1053. return;
  1054. }
  1055. // Save off what we cast for jar think
  1056. PaySpellCost( pPlayer );
  1057. m_iPreviouslyCastSpell = m_iSelectedSpellIndex;
  1058. FireProjectile( pPlayer );
  1059. m_flTimeNextSpell = gpGlobals->curtime + 0.5f;
  1060. // Create one off spell effect in front of the player
  1061. Vector origin = pPlayer->GetAbsOrigin();
  1062. CPVSFilter filter( origin );
  1063. if ( GetTeamNumber() == TF_TEAM_RED )
  1064. {
  1065. TE_TFParticleEffect( filter, 0.0, "spell_cast_wheel_red", origin + Vector( 0, 0, 100 ), vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1066. }
  1067. else
  1068. {
  1069. TE_TFParticleEffect( filter, 0.0, "spell_cast_wheel_blue", origin + Vector( 0, 0, 100 ), vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1070. }
  1071. #endif
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Individual spells
  1075. //-----------------------------------------------------------------------------
  1076. bool CTFSpellBook::CastSpell( CTFPlayer *pPlayer, int iSpellIndex )
  1077. {
  1078. if ( CanCastSpell( pPlayer ) )
  1079. {
  1080. PaySpellCost( pPlayer );
  1081. const spell_data_t *pSpellData = GetSpellData( m_iSelectedSpellIndex );
  1082. if ( !pSpellData)
  1083. return false;
  1084. if ( IsRareSpell( iSpellIndex ) )
  1085. {
  1086. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
  1087. {
  1088. pPlayer->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_RARE_SPELL );
  1089. }
  1090. }
  1091. // Save off what we cast for jar think
  1092. m_iPreviouslyCastSpell = m_iSelectedSpellIndex;
  1093. // Create one off spell effect in front of the player
  1094. Vector origin = pPlayer->GetAbsOrigin();
  1095. CPVSFilter filter( origin );
  1096. //const spell_data_t *pSpellData = GetSpellData( m_iSelectedSpellIndex );
  1097. if ( pSpellData && !FStrEq( pSpellData->m_pSpellUiName, "#TF_Spell_Stealth" ) ) // do NOT create for Stealth
  1098. {
  1099. if ( GetTeamNumber() == TF_TEAM_RED )
  1100. {
  1101. TE_TFParticleEffect( filter, 0.0, "spell_cast_wheel_red", origin + Vector( 0, 0, 100 ), vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1102. }
  1103. else
  1104. {
  1105. TE_TFParticleEffect( filter, 0.0, "spell_cast_wheel_blue", origin + Vector( 0, 0, 100 ), vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1106. }
  1107. }
  1108. return true;
  1109. }
  1110. return false;
  1111. }
  1112. #endif
  1113. //-----------------------------------------------------------------------------
  1114. bool CTFSpellBook::CastSelfHeal( CTFPlayer *pPlayer )
  1115. {
  1116. #ifdef GAME_DLL
  1117. Vector origin = pPlayer->GetAbsOrigin();
  1118. CPVSFilter filter( origin );
  1119. const char* pszEffectName = pPlayer->GetTeamNumber() == TF_TEAM_RED ? "spell_overheal_red" : "spell_overheal_blue";
  1120. TE_TFParticleEffect( filter, 0.0, pszEffectName, origin, vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1121. //pPlayer->EmitSound( "BaseExplosionEffect.Sound" );
  1122. // Collect players and cause knockback to enemies
  1123. // Treat this trace exactly like radius damage
  1124. CTraceFilterIgnorePlayers traceFilter( pPlayer, COLLISION_GROUP_PROJECTILE );
  1125. // Splash pee on everyone nearby.
  1126. CBaseEntity *pListOfEntities[32];
  1127. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, origin, 250.0f, FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  1128. for ( int i = 0; i < iEntities; ++i )
  1129. {
  1130. CBaseCombatCharacter *pBaseTarget = NULL;
  1131. CTFPlayer *pTarget = ToTFPlayer( pListOfEntities[i] );
  1132. if ( !pTarget )
  1133. {
  1134. pBaseTarget = dynamic_cast<CBaseCombatCharacter*>( pListOfEntities[i] );
  1135. }
  1136. else
  1137. {
  1138. pBaseTarget = pTarget;
  1139. }
  1140. if ( !pBaseTarget || !pTarget || !pTarget->IsAlive() )
  1141. continue;
  1142. // Do a quick trace to see if there's any geometry in the way.
  1143. trace_t trace;
  1144. UTIL_TraceLine( origin, pBaseTarget->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  1145. if ( trace.DidHitWorld() )
  1146. continue;
  1147. Vector vecDir = pBaseTarget->WorldSpaceCenter() - origin;
  1148. VectorNormalize( vecDir );
  1149. // help allies
  1150. if ( pBaseTarget->GetTeamNumber() == pPlayer->GetTeamNumber() )
  1151. {
  1152. pBaseTarget->TakeHealth( 50, DMG_GENERIC );
  1153. if ( pTarget )
  1154. {
  1155. pTarget->m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, 1, pPlayer );
  1156. pTarget->m_Shared.AddCond( TF_COND_HALLOWEEN_QUICK_HEAL, 3, pPlayer );
  1157. }
  1158. }
  1159. else // knockback enemies
  1160. {
  1161. if ( pTarget )
  1162. {
  1163. pTarget->ApplyAirBlastImpulse( vecDir * 300.0f );
  1164. }
  1165. else
  1166. {
  1167. pBaseTarget->ApplyAbsVelocityImpulse( vecDir * 300.0f );
  1168. }
  1169. }
  1170. }
  1171. #endif
  1172. return true;
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. bool CTFSpellBook::CastRocketJump( CTFPlayer *pPlayer )
  1176. {
  1177. #ifdef GAME_DLL
  1178. const float flBlastRadius = 100.f;
  1179. // Set z to zero then add impulse
  1180. // make this proper jumping later
  1181. Vector vel = pPlayer->GetAbsVelocity();
  1182. if ( vel.z < 0 )
  1183. {
  1184. vel.z = 0;
  1185. }
  1186. pPlayer->SetAbsVelocity( vel );
  1187. Vector vForward( 0, 0, 800 );
  1188. pPlayer->ApplyAbsVelocityImpulse( vForward );
  1189. const Vector& origin = pPlayer->GetAbsOrigin();
  1190. CPVSFilter filter( origin );
  1191. TE_TFParticleEffect( filter, 0.0, "bombinomicon_burningdebris", origin, vec3_angle );
  1192. TE_TFParticleEffect( filter, 0.0, "heavy_ring_of_fire", origin, vec3_angle );
  1193. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pPlayer, "foot_L" );
  1194. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pPlayer, "foot_R" );
  1195. // Give a little health to compensate for fall damage
  1196. pPlayer->TakeHealth( 25, DMG_GENERIC );
  1197. // Collect players and cause knockback to enemies
  1198. // Treat this trace exactly like radius damage
  1199. CTraceFilterIgnorePlayers traceFilter( pPlayer, COLLISION_GROUP_PROJECTILE );
  1200. // Splash pee on everyone nearby.
  1201. CBaseEntity *pListOfEntities[32];
  1202. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, origin, flBlastRadius, FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  1203. for ( int i = 0; i < iEntities; ++i )
  1204. {
  1205. CBaseCombatCharacter *pBaseTarget = NULL;
  1206. CTFPlayer *pTarget = ToTFPlayer( pListOfEntities[i] );
  1207. if ( !pTarget )
  1208. {
  1209. pBaseTarget = dynamic_cast<CBaseCombatCharacter*>( pListOfEntities[i] );
  1210. }
  1211. else
  1212. {
  1213. pBaseTarget = pTarget;
  1214. }
  1215. if ( !pBaseTarget || !pTarget || !pTarget->IsAlive() || pBaseTarget->GetTeamNumber() == pPlayer->GetTeamNumber() )
  1216. continue;
  1217. // Do a quick trace to see if there's any geometry in the way.
  1218. trace_t trace;
  1219. UTIL_TraceLine( origin, pBaseTarget->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  1220. if ( trace.DidHitWorld() )
  1221. continue;
  1222. Vector vecDir = pBaseTarget->WorldSpaceCenter() - origin;
  1223. VectorNormalize( vecDir );
  1224. pBaseTarget->RemoveFlag( FL_ONGROUND );
  1225. if ( pTarget )
  1226. {
  1227. pTarget->ApplyAirBlastImpulse( vecDir * 800.0f );
  1228. }
  1229. else
  1230. {
  1231. pBaseTarget->ApplyAbsVelocityImpulse( vecDir * 800.0f );
  1232. }
  1233. }
  1234. CTakeDamageInfo info;
  1235. info.SetAttacker( pPlayer );
  1236. info.SetInflictor( pPlayer );
  1237. info.SetDamage( 20.f );
  1238. info.SetDamageCustom( TF_DMG_CUSTOM_SPELL_BLASTJUMP );
  1239. info.SetDamagePosition( origin );
  1240. info.SetDamageType( DMG_BLAST );
  1241. CTFRadiusDamageInfo radiusinfo( &info, origin, flBlastRadius, pPlayer );
  1242. TFGameRules()->RadiusDamage( radiusinfo );
  1243. #endif
  1244. return true;
  1245. }
  1246. //-----------------------------------------------------------------------------
  1247. bool CTFSpellBook::CastSelfSpeedBoost( CTFPlayer *pPlayer )
  1248. {
  1249. #ifdef GAME_DLL
  1250. // Give a little health
  1251. pPlayer->TakeHealth( 100, DMG_GENERIC );
  1252. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_TINY, 20, pPlayer );
  1253. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_SPEED_BOOST, 20, pPlayer );
  1254. #endif
  1255. return true;
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. bool CTFSpellBook::CastSelfStealth( CTFPlayer *pPlayer )
  1259. {
  1260. #ifdef GAME_DLL
  1261. // Grant a small amount of health
  1262. pPlayer->TakeHealth( 40, DMG_GENERIC );
  1263. pPlayer->m_Shared.AddCond( TF_COND_STEALTHED_USER_BUFF, 8, pPlayer );
  1264. #endif
  1265. return true;
  1266. }
  1267. //********************************************************************************************************************************
  1268. //-----------------------------------------------------------------------------
  1269. // Kart Self Spells
  1270. //-----------------------------------------------------------------------------
  1271. bool CTFSpellBook::CastKartRocketJump( CTFPlayer *pPlayer )
  1272. {
  1273. #ifdef GAME_DLL
  1274. const float flBlastRadius = 250.f;
  1275. // Set z to zero then add impulse
  1276. // make this proper jumping later
  1277. Vector vel = pPlayer->GetAbsVelocity();
  1278. if ( vel.z < 0 )
  1279. {
  1280. vel.z = 0;
  1281. }
  1282. pPlayer->SetAbsVelocity( vel );
  1283. Vector vForward( 0, 0, 1200 );
  1284. pPlayer->ApplyAbsVelocityImpulse( vForward );
  1285. const Vector& origin = pPlayer->GetAbsOrigin();
  1286. CPVSFilter filter( origin );
  1287. TE_TFParticleEffect( filter, 0.0, "bombinomicon_burningdebris", origin, vec3_angle );
  1288. TE_TFParticleEffect( filter, 0.0, "heavy_ring_of_fire", origin, vec3_angle );
  1289. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pPlayer, "foot_L" );
  1290. DispatchParticleEffect( "rocketjump_smoke", PATTACH_POINT_FOLLOW, pPlayer, "foot_R" );
  1291. // Give a little health to compensate for fall damage
  1292. //pPlayer->TakeHealth( 25, DMG_GENERIC );
  1293. pPlayer->RemoveFlag( FL_ONGROUND );
  1294. pPlayer->m_Shared.AddCond( TF_COND_PARACHUTE_DEPLOYED );
  1295. // Collect players and cause knockback to enemies
  1296. // Treat this trace exactly like radius damage
  1297. CTraceFilterIgnorePlayers traceFilter( pPlayer, COLLISION_GROUP_PROJECTILE );
  1298. // Trace entity radius
  1299. CBaseEntity *pListOfEntities[32];
  1300. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, origin, flBlastRadius, FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  1301. for ( int i = 0; i < iEntities; ++i )
  1302. {
  1303. CTFPlayer *pTarget = ToTFPlayer( pListOfEntities[i] );
  1304. if ( !pTarget || !pTarget->IsAlive() || pTarget->GetTeamNumber() == pPlayer->GetTeamNumber() )
  1305. continue;
  1306. // Do a quick trace to see if there's any geometry in the way.
  1307. trace_t trace;
  1308. UTIL_TraceLine( origin, pTarget->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  1309. if ( trace.DidHitWorld() )
  1310. continue;
  1311. Vector vecDir = pTarget->WorldSpaceCenter() - origin;
  1312. vecDir.NormalizeInPlace();
  1313. vecDir.z += 0.5f;
  1314. pTarget->AddHalloweenKartPushEvent( pPlayer, pPlayer->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ), pPlayer->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ), vecDir * tf_halloween_kart_normal_speed.GetFloat(), 30.0f );
  1315. }
  1316. #endif
  1317. return true;
  1318. }
  1319. bool CTFSpellBook::CastKartUber( CTFPlayer *pPlayer )
  1320. {
  1321. #ifdef GAME_DLL
  1322. Vector origin = pPlayer->GetAbsOrigin();
  1323. CPVSFilter filter( origin );
  1324. const char* pszEffectName = pPlayer->GetTeamNumber() == TF_TEAM_RED ? "spell_overheal_red" : "spell_overheal_blue";
  1325. TE_TFParticleEffect( filter, 0.0, pszEffectName, origin, vec3_angle, pPlayer, PATTACH_ABSORIGIN_FOLLOW );
  1326. //pPlayer->EmitSound( "BaseExplosionEffect.Sound" );
  1327. // Collect players and cause knockback to enemies
  1328. // Treat this trace exactly like radius damage
  1329. CTraceFilterIgnorePlayers traceFilter( pPlayer, COLLISION_GROUP_PROJECTILE );
  1330. pPlayer->m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, 7, pPlayer );
  1331. pPlayer->AddKartDamage( -50 ); //Heal
  1332. #endif
  1333. return true;
  1334. }
  1335. bool CTFSpellBook::CastKartBombHead( CTFPlayer *pPlayer )
  1336. {
  1337. #ifdef GAME_DLL
  1338. pPlayer->m_Shared.AddCond( TF_COND_HALLOWEEN_BOMB_HEAD, 10, pPlayer );
  1339. #endif
  1340. return true;
  1341. }
  1342. //************************************************************************************************************************
  1343. // Spell Projectiles
  1344. //************************************************************************************************************************
  1345. class CTFProjectile_SpellFireball : public CTFProjectile_Rocket
  1346. {
  1347. public:
  1348. DECLARE_CLASS( CTFProjectile_SpellFireball, CTFProjectile_Rocket );
  1349. DECLARE_NETWORKCLASS();
  1350. virtual int GetWeaponID( void ) const { return TF_WEAPON_SPELLBOOK_PROJECTILE; }
  1351. virtual float GetDamageRadius() const { return 200.0f; }
  1352. virtual int GetCustomDamageType() const OVERRIDE { return m_bIsMeteor ? TF_DMG_CUSTOM_SPELL_METEOR : TF_DMG_CUSTOM_SPELL_FIREBALL; }
  1353. virtual bool IsDeflectable() OVERRIDE { return false; }
  1354. void SetMeteor( bool bIsMeteor ) { m_bIsMeteor = bIsMeteor; }
  1355. CTFProjectile_SpellFireball()
  1356. {
  1357. m_bIsMeteor = false;
  1358. #ifdef GAME_DLL
  1359. //m_pszExplodeParticleName = "ExplosionCore_buildings";
  1360. m_pszExplodeParticleName = "bombinomicon_burningdebris";
  1361. #endif // GAME_DLL
  1362. }
  1363. #ifdef GAME_DLL
  1364. virtual void Spawn() OVERRIDE
  1365. {
  1366. SetModelScale( 0.01f );
  1367. BaseClass::Spawn();
  1368. }
  1369. virtual int UpdateTransmitState() OVERRIDE { return SetTransmitState( FL_EDICT_PVSCHECK ); }
  1370. virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE
  1371. {
  1372. Assert( pOther );
  1373. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  1374. return;
  1375. if ( pOther->GetParent() == GetOwnerEntity() )
  1376. return;
  1377. // Handle hitting skybox (disappear).
  1378. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  1379. if( pTrace->surface.flags & SURF_SKY )
  1380. {
  1381. UTIL_Remove( this );
  1382. return;
  1383. }
  1384. // pass through ladders
  1385. if( pTrace->surface.flags & CONTENTS_LADDER )
  1386. return;
  1387. Explode( pTrace );
  1388. UTIL_Remove( this );
  1389. }
  1390. virtual void Explode( const trace_t *pTrace )
  1391. {
  1392. SetModelName( NULL_STRING );//invisible
  1393. AddSolidFlags( FSOLID_NOT_SOLID );
  1394. m_takedamage = DAMAGE_NO;
  1395. // Pull out of the wall a bit.
  1396. if ( pTrace->fraction != 1.0 )
  1397. {
  1398. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  1399. }
  1400. CTFPlayer *pThrower = ToTFPlayer( GetOwnerEntity() );
  1401. if ( pThrower )
  1402. {
  1403. const Vector &vecOrigin = GetAbsOrigin();
  1404. // Any effects from the initial explosion
  1405. if ( InitialExplodeEffects( pThrower, pTrace ) )
  1406. {
  1407. // Particle
  1408. if ( GetExplodeEffectParticle() )
  1409. {
  1410. CPVSFilter filter( vecOrigin );
  1411. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  1412. }
  1413. // Sounds
  1414. if ( GetExplodeEffectSound() )
  1415. {
  1416. EmitSound( GetExplodeEffectSound() );
  1417. }
  1418. // Treat this trace exactly like radius damage
  1419. CTraceFilterIgnorePlayers traceFilter( pThrower, COLLISION_GROUP_PROJECTILE );
  1420. // Splash pee on everyone nearby.
  1421. CBaseEntity *pListOfEntities[32];
  1422. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, vecOrigin, GetDamageRadius(), FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  1423. for ( int i = 0; i < iEntities; ++i )
  1424. {
  1425. CBaseCombatCharacter *pBasePlayer = NULL;
  1426. CTFPlayer *pPlayer = ToTFPlayer( pListOfEntities[i] );
  1427. if ( !pPlayer )
  1428. {
  1429. pBasePlayer = dynamic_cast<CBaseCombatCharacter*>( pListOfEntities[i] );
  1430. }
  1431. else
  1432. {
  1433. pBasePlayer = pPlayer;
  1434. }
  1435. if ( !pBasePlayer || !pPlayer || !pPlayer->IsAlive() )
  1436. continue;
  1437. // Do a quick trace to see if there's any geometry in the way.
  1438. trace_t trace;
  1439. UTIL_TraceLine( vecOrigin, pPlayer->WorldSpaceCenter(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  1440. //debugoverlay->AddLineOverlay( vecOrigin, pPlayer->WorldSpaceCenter(), 255, 0, 0, false, 10 );
  1441. if ( trace.DidHitWorld() )
  1442. continue;
  1443. // Effects on the individual players
  1444. ExplodeEffectOnTarget( pThrower, pPlayer, pBasePlayer );
  1445. }
  1446. if ( TFGameRules() )
  1447. {
  1448. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_FIREBALL, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  1449. }
  1450. CTakeDamageInfo info;
  1451. info.SetAttacker( pThrower );
  1452. info.SetInflictor( this );
  1453. info.SetWeapon( GetLauncher() );
  1454. info.SetDamage( 10.f );
  1455. info.SetDamageCustom( GetCustomDamageType() );
  1456. info.SetDamagePosition( vecOrigin );
  1457. info.SetDamageType( DMG_BLAST );
  1458. CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, GetDamageRadius(), pThrower );
  1459. TFGameRules()->RadiusDamage( radiusinfo );
  1460. }
  1461. else
  1462. {
  1463. pThrower->EmitSound( "Player.DenyWeaponSelection" );
  1464. }
  1465. }
  1466. // Grenade remove
  1467. //SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  1468. // Remove the rocket.
  1469. UTIL_Remove( this );
  1470. SetTouch( NULL );
  1471. AddEffects( EF_NODRAW );
  1472. SetAbsVelocity( vec3_origin );
  1473. }
  1474. virtual const char *GetProjectileModelName( void ) { return ""; } // We dont have a model by default, and that's OK
  1475. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) { return true; }
  1476. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget )
  1477. {
  1478. if ( pBaseTarget->GetTeamNumber() == GetTeamNumber() )
  1479. return;
  1480. if ( pTarget )
  1481. {
  1482. if ( pTarget->m_Shared.IsInvulnerable() )
  1483. return;
  1484. if ( pTarget->m_Shared.InCond( TF_COND_PHASE ) || pTarget->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  1485. return;
  1486. pTarget->m_Shared.SelfBurn( 5.0f );
  1487. }
  1488. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  1489. trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
  1490. CBaseEntity *pInflictor = GetLauncher();
  1491. CTakeDamageInfo info;
  1492. info.SetAttacker( pThrower );
  1493. info.SetInflictor( this );
  1494. info.SetWeapon( pInflictor );
  1495. info.SetDamage( 100.f );
  1496. info.SetDamageCustom( GetCustomDamageType() );
  1497. info.SetDamagePosition( GetAbsOrigin() );
  1498. info.SetDamageType( DMG_BURN );
  1499. // Hurt 'em.
  1500. Vector dir;
  1501. AngleVectors( GetAbsAngles(), &dir );
  1502. pBaseTarget->DispatchTraceAttack( info, dir, pNewTrace );
  1503. ApplyMultiDamage();
  1504. Vector vecDir = pBaseTarget->WorldSpaceCenter() - GetAbsOrigin();
  1505. VectorNormalize( vecDir );
  1506. vecDir.z = 0.1f;
  1507. if ( pTarget )
  1508. {
  1509. pTarget->ApplyAirBlastImpulse( vecDir * 5 );
  1510. }
  1511. }
  1512. virtual const char *GetExplodeEffectParticle() const { return m_pszExplodeParticleName; }
  1513. void SetExplodeParticleName( const char *pszName ) { m_pszExplodeParticleName = pszName; }
  1514. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_fireball_impact"; }
  1515. #endif
  1516. #ifdef CLIENT_DLL
  1517. virtual const char *GetTrailParticleName( void )
  1518. {
  1519. return GetTeamNumber() == TF_TEAM_BLUE ? "spell_fireball_small_blue" : "spell_fireball_small_red";
  1520. }
  1521. #endif
  1522. private:
  1523. bool m_bIsMeteor;
  1524. #ifdef GAME_DLL
  1525. const char *m_pszExplodeParticleName;
  1526. #endif // GAME_DLL
  1527. };
  1528. // Fireball
  1529. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellFireball, DT_TFProjectile_SpellFireball )
  1530. BEGIN_NETWORK_TABLE( CTFProjectile_SpellFireball, DT_TFProjectile_SpellFireball )
  1531. END_NETWORK_TABLE()
  1532. LINK_ENTITY_TO_CLASS( tf_projectile_spellfireball, CTFProjectile_SpellFireball );
  1533. PRECACHE_WEAPON_REGISTER( tf_projectile_spellfireball);
  1534. // *************************************************************************************************************************
  1535. class CTFProjectile_SpellBats : public CTFProjectile_Jar
  1536. {
  1537. public:
  1538. DECLARE_CLASS( CTFProjectile_SpellBats, CTFProjectile_Jar );
  1539. DECLARE_NETWORKCLASS();
  1540. virtual int GetWeaponID( void ) const { return TF_WEAPON_SPELLBOOK_PROJECTILE; }
  1541. virtual float GetDamageRadius() const { return 250.0f; }
  1542. virtual float GetModelScale() const { return 0.01f; }
  1543. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_BATS; }
  1544. virtual bool IsDeflectable() OVERRIDE { return false; }
  1545. #ifdef GAME_DLL
  1546. virtual void Spawn( void )
  1547. {
  1548. SetModelScale( GetModelScale() );
  1549. BaseClass::Spawn();
  1550. }
  1551. //-----------------------------------------------------------------------------
  1552. // Lightning Ball / Base
  1553. //-----------------------------------------------------------------------------
  1554. virtual void Explode( trace_t *pTrace, int bitsDamageType ) OVERRIDE
  1555. {
  1556. SetModelName( NULL_STRING );//invisible
  1557. AddSolidFlags( FSOLID_NOT_SOLID );
  1558. m_takedamage = DAMAGE_NO;
  1559. // Pull out of the wall a bit.
  1560. if ( pTrace->fraction != 1.0 )
  1561. {
  1562. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  1563. }
  1564. CTFPlayer *pThrower = ToTFPlayer( GetThrower() );
  1565. if ( pThrower )
  1566. {
  1567. const Vector& vecOrigin = GetAbsOrigin();
  1568. // Any effects from the initial explosion
  1569. if ( InitialExplodeEffects( pThrower, pTrace ) )
  1570. {
  1571. // Particle
  1572. if ( GetExplodeEffectParticle() )
  1573. {
  1574. CPVSFilter filter( vecOrigin );
  1575. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  1576. }
  1577. // Sounds
  1578. if ( GetExplodeEffectSound() )
  1579. {
  1580. EmitSound( GetExplodeEffectSound() );
  1581. }
  1582. // Treat this trace exactly like radius damage
  1583. CTraceFilterIgnorePlayers traceFilter( pThrower, COLLISION_GROUP_PROJECTILE );
  1584. // Splash pee on everyone nearby.
  1585. CBaseEntity *pListOfEntities[32];
  1586. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, vecOrigin, GetDamageRadius(), FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  1587. for ( int i = 0; i < iEntities; ++i )
  1588. {
  1589. CBaseCombatCharacter *pBasePlayer = NULL;
  1590. CTFPlayer *pPlayer = ToTFPlayer( pListOfEntities[i] );
  1591. if ( !pPlayer )
  1592. {
  1593. pBasePlayer = dynamic_cast<CBaseCombatCharacter*>( pListOfEntities[i] );
  1594. }
  1595. else
  1596. {
  1597. pBasePlayer = pPlayer;
  1598. }
  1599. if ( !pBasePlayer || !pBasePlayer->IsAlive() )
  1600. continue;
  1601. // Do a quick trace to see if there's any geometry in the way.
  1602. trace_t trace;
  1603. UTIL_TraceLine( GetAbsOrigin(), pBasePlayer->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  1604. if ( trace.DidHitWorld() )
  1605. continue;
  1606. // Effects on the individual players
  1607. ExplodeEffectOnTarget( pThrower, pPlayer, pBasePlayer );
  1608. }
  1609. if ( TFGameRules() )
  1610. {
  1611. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MERASMUS_ZAP, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  1612. }
  1613. ApplyBlastDamage( pThrower, vecOrigin );
  1614. }
  1615. else
  1616. {
  1617. pThrower->EmitSound( "Player.DenyWeaponSelection" );
  1618. }
  1619. }
  1620. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  1621. SetTouch( NULL );
  1622. AddEffects( EF_NODRAW );
  1623. SetAbsVelocity( vec3_origin );
  1624. }
  1625. virtual void ApplyBlastDamage( CTFPlayer *pThrower, Vector vecOrigin )
  1626. {
  1627. CTakeDamageInfo info;
  1628. info.SetAttacker( pThrower );
  1629. info.SetInflictor( this );
  1630. info.SetWeapon( GetLauncher() );
  1631. info.SetDamage( 10.f );
  1632. info.SetDamageCustom( GetCustomDamageType() );
  1633. info.SetDamagePosition( vecOrigin );
  1634. info.SetDamageType( DMG_BLAST );
  1635. CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, GetDamageRadius(), pThrower );
  1636. TFGameRules()->RadiusDamage( radiusinfo );
  1637. }
  1638. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace )
  1639. {
  1640. // Added Particle
  1641. Vector vecOrigin = GetAbsOrigin();
  1642. // Particle
  1643. CPVSFilter filter( vecOrigin );
  1644. TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, TF_WEAPON_GRENADE_PIPEBOMB, kInvalidEHandleExplosion, -1, SPECIAL1, INVALID_STRING_INDEX );
  1645. return true;
  1646. }
  1647. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget )
  1648. {
  1649. if ( pBaseTarget->GetTeamNumber() == GetTeamNumber() )
  1650. return;
  1651. if ( pTarget )
  1652. {
  1653. if ( pTarget->m_Shared.IsInvulnerable() )
  1654. return;
  1655. if ( pTarget->m_Shared.InCond( TF_COND_PHASE ) || pTarget->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  1656. return;
  1657. // Stun the target
  1658. pTarget->m_Shared.StunPlayer( 0.5, 0.5, TF_STUN_MOVEMENT, pThrower );
  1659. }
  1660. Vector vecDir = pBaseTarget->WorldSpaceCenter() - GetAbsOrigin();
  1661. VectorNormalize( vecDir );
  1662. if ( pTarget )
  1663. {
  1664. pTarget->ApplyAirBlastImpulse( vecDir * 200.0f + Vector(0, 0, 800 ) );
  1665. const char* pszEffectName = GetTeamNumber() == TF_TEAM_RED ? "spell_batball_red" : "spell_batball_blue";
  1666. DispatchParticleEffect( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, pTarget );
  1667. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  1668. if ( pSpellBook )
  1669. {
  1670. pTarget->m_Shared.MakeBleed( pThrower, pSpellBook, 3.0f );
  1671. }
  1672. }
  1673. else
  1674. {
  1675. pBaseTarget->ApplyAbsVelocityImpulse( vecDir * 1000.0f );
  1676. }
  1677. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  1678. trace_t *pNewTrace = const_cast<trace_t*>( pTrace );
  1679. CBaseEntity *pInflictor = GetLauncher();
  1680. CTakeDamageInfo info;
  1681. info.SetAttacker( pThrower );
  1682. info.SetInflictor( this );
  1683. info.SetWeapon( pInflictor );
  1684. info.SetDamage( 40 );
  1685. info.SetDamageCustom( GetCustomDamageType() );
  1686. info.SetDamagePosition( GetAbsOrigin() );
  1687. info.SetDamageType( DMG_BURN );
  1688. // Hurt 'em.
  1689. Vector dir;
  1690. AngleVectors( GetAbsAngles(), &dir );
  1691. pBaseTarget->DispatchTraceAttack( info, dir, pNewTrace );
  1692. ApplyMultiDamage();
  1693. }
  1694. virtual const char *GetExplodeEffectParticle() const { return GetTeamNumber() == TF_TEAM_RED ? "spell_batball_impact_red" : "spell_batball_impact_blue"; }
  1695. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_bat_impact"; }
  1696. #endif
  1697. #ifdef CLIENT_DLL
  1698. virtual const char* GetTrailParticleName( void ) { return GetTeamNumber() == TF_TEAM_RED ? "spell_batball_throw_red" : "spell_batball_throw_blue"; }
  1699. #endif
  1700. };
  1701. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellBats, DT_TFProjectile_SpellBats )
  1702. BEGIN_NETWORK_TABLE( CTFProjectile_SpellBats, DT_TFProjectile_SpellBats )
  1703. END_NETWORK_TABLE()
  1704. LINK_ENTITY_TO_CLASS( tf_projectile_spellbats, CTFProjectile_SpellBats );
  1705. PRECACHE_WEAPON_REGISTER( tf_projectile_spellbats );
  1706. // *************************************************************************************************************************
  1707. class CTFProjectile_SpellSpawnZombie : public CTFProjectile_SpellBats
  1708. {
  1709. public:
  1710. DECLARE_CLASS( CTFProjectile_SpellSpawnZombie, CTFProjectile_SpellBats );
  1711. DECLARE_NETWORKCLASS();
  1712. CTFProjectile_SpellSpawnZombie()
  1713. {
  1714. #ifdef GAME_DLL
  1715. m_skeletonType = 0;
  1716. #endif // GAME_DLL
  1717. }
  1718. virtual float GetDamageRadius() const { return 1.0f; }
  1719. virtual void SetCustomPipebombModel() { SetModel( "models/props_mvm/mvm_human_skull_collide.mdl" ); }
  1720. virtual float GetModelScale() const { return 1.0f; }
  1721. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_SKELETON; }
  1722. #ifdef GAME_DLL
  1723. virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { }
  1724. virtual void PipebombTouch( CBaseEntity *pOther ) { }
  1725. virtual void Explode( trace_t *pTrace, int bitsDamageType ) OVERRIDE
  1726. {
  1727. // no owner? spawn skeletons anyways
  1728. if ( !GetThrower() )
  1729. {
  1730. InitialExplodeEffects( NULL, pTrace );
  1731. // Particle
  1732. if ( GetExplodeEffectParticle() )
  1733. {
  1734. CPVSFilter filter( GetAbsOrigin() );
  1735. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), GetAbsOrigin(), vec3_angle );
  1736. }
  1737. // Sounds
  1738. if ( GetExplodeEffectSound() )
  1739. {
  1740. EmitSound( GetExplodeEffectSound() );
  1741. }
  1742. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  1743. SetTouch( NULL );
  1744. AddEffects( EF_NODRAW );
  1745. SetAbsVelocity( vec3_origin );
  1746. return;
  1747. }
  1748. BaseClass::Explode( pTrace, bitsDamageType );
  1749. }
  1750. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  1751. {
  1752. // Pull in a little
  1753. Vector vSpawnPoint = ( pTrace->endpos + ( pTrace->plane.normal * 2.0f ) );
  1754. CZombie::SpawnAtPos( vSpawnPoint, 30.0f, GetTeamNumber(), pThrower, (CZombie::SkeletonType_t)m_skeletonType );
  1755. return true;
  1756. }
  1757. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) { }
  1758. virtual const char *GetExplodeEffectParticle() const
  1759. {
  1760. if ( GetTeamNumber() == TF_TEAM_HALLOWEEN )
  1761. {
  1762. return "spell_skeleton_goop_green";
  1763. }
  1764. return GetTeamNumber() == TF_TEAM_RED ? "spell_pumpkin_mirv_goop_red" : "spell_pumpkin_mirv_goop_blue";
  1765. }
  1766. virtual const char *GetExplodeEffectSound() const { return "Cleaver.ImpactFlesh"; }
  1767. void SetSkeletonType ( int iType ) { m_skeletonType = iType; }
  1768. int m_skeletonType;
  1769. #endif
  1770. #ifdef CLIENT_DLL
  1771. virtual const char* GetTrailParticleName( void ) { return "unusual_bubbles_green_fumes"; }
  1772. #endif
  1773. };
  1774. // Spawn Zombie
  1775. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellSpawnZombie, DT_TFProjectile_SpellSpawnZombie )
  1776. BEGIN_NETWORK_TABLE( CTFProjectile_SpellSpawnZombie, DT_TFProjectile_SpellSpawnZombie )
  1777. END_NETWORK_TABLE()
  1778. LINK_ENTITY_TO_CLASS( tf_projectile_spellspawnzombie, CTFProjectile_SpellSpawnZombie );
  1779. PRECACHE_WEAPON_REGISTER( tf_projectile_spellspawnzombie );
  1780. #ifdef GAME_DLL
  1781. CBaseEntity* CreateSpellSpawnZombie( CBaseCombatCharacter *pCaster, const Vector& vSpawnPosition, int nSkeletonType )
  1782. {
  1783. Vector offset = RandomVector( -32, 32 );
  1784. offset.z = 16;
  1785. CTFProjectile_SpellSpawnZombie *pGrenade = static_cast<CTFProjectile_SpellSpawnZombie*>( CBaseEntity::CreateNoSpawn( "tf_projectile_spellspawnzombie", vSpawnPosition + offset, RandomAngle( 0, 360 ), pCaster ) );
  1786. if ( pGrenade )
  1787. {
  1788. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  1789. pGrenade->SetPipebombMode();
  1790. DispatchSpawn( pGrenade );
  1791. Vector vecImpulse = RandomVector( -1, 1 );
  1792. VectorNormalize( vecImpulse );
  1793. vecImpulse.z = RandomFloat( 1.0f, 1.6f );
  1794. Vector vecVelocity = vecImpulse * RandomFloat( 250.0f, 300.0f );
  1795. AngularImpulse angVelocity = AngularImpulse( 300, 300, 100 );
  1796. pGrenade->InitGrenade( vecVelocity, angVelocity, pCaster, 0, 0 );
  1797. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  1798. pGrenade->SetDetonateTimerLength( RandomFloat( 2.f, 2.5f ) );
  1799. pGrenade->SetSkeletonType( nSkeletonType );
  1800. }
  1801. return pGrenade;
  1802. }
  1803. #endif
  1804. // *************************************************************************************************************************
  1805. class CTFProjectile_SpellSpawnHorde : public CTFProjectile_SpellBats
  1806. {
  1807. public:
  1808. DECLARE_CLASS( CTFProjectile_SpellSpawnHorde, CTFProjectile_SpellBats );
  1809. DECLARE_NETWORKCLASS();
  1810. virtual float GetDamageRadius() const { return 1.0f; }
  1811. virtual void SetCustomPipebombModel() { SetModel( "models/props_mvm/mvm_human_skull_collide.mdl" ); }
  1812. virtual float GetModelScale() const { return 1.0f; }
  1813. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_SKELETON; }
  1814. #ifdef GAME_DLL
  1815. //virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { }
  1816. //virtual void PipebombTouch( CBaseEntity *pOther ) { }
  1817. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  1818. {
  1819. // Spawn a tonne of extra skelatone grenades (mirv style)
  1820. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  1821. if ( !pSpellBook )
  1822. return false;
  1823. for ( int i = 0; i < 3; i++ )
  1824. {
  1825. Vector offset = RandomVector( -32, 32 );
  1826. offset.z = 16;
  1827. CreateSpellSpawnZombie( pThrower, GetAbsOrigin(), 0 );
  1828. }
  1829. if ( TFGameRules() )
  1830. {
  1831. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_SKELETON_HORDE, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  1832. }
  1833. return true;
  1834. }
  1835. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) { }
  1836. virtual const char *GetExplodeEffectParticle() const { return GetTeamNumber() == TF_TEAM_RED ? "spell_pumpkin_mirv_goop_red" : "spell_pumpkin_mirv_goop_blue"; }
  1837. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_skeleton_horde_rise"; }
  1838. #endif
  1839. #ifdef CLIENT_DLL
  1840. virtual const char* GetTrailParticleName( void ) { return "unusual_bubbles_green_fumes"; }
  1841. #endif
  1842. };
  1843. // Spawn Horde of Skels
  1844. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellSpawnHorde, DT_TFProjectile_SpellSpawnHorde )
  1845. BEGIN_NETWORK_TABLE( CTFProjectile_SpellSpawnHorde, DT_TFProjectile_SpellSpawnHorde )
  1846. END_NETWORK_TABLE()
  1847. LINK_ENTITY_TO_CLASS( tf_projectile_spellspawnhorde, CTFProjectile_SpellSpawnHorde );
  1848. PRECACHE_WEAPON_REGISTER( tf_projectile_spellspawnhorde);
  1849. // *************************************************************************************************************************
  1850. class CTFProjectile_SpellPumpkin : public CTFProjectile_SpellBats
  1851. {
  1852. public:
  1853. DECLARE_CLASS( CTFProjectile_SpellPumpkin, CTFProjectile_SpellBats );
  1854. DECLARE_NETWORKCLASS();
  1855. CTFProjectile_SpellPumpkin ()
  1856. {
  1857. #ifdef GAME_DLL
  1858. m_flImpactTime = gpGlobals->curtime + 1.0f;
  1859. #endif
  1860. }
  1861. virtual float GetDamageRadius() const { return 1.0f; }
  1862. virtual void SetCustomPipebombModel() { SetModel( "models/weapons/w_models/w_cannonball.mdl" ); }
  1863. virtual float GetModelScale() const { return 0.75f; }
  1864. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_MIRV; }
  1865. #ifdef GAME_DLL
  1866. // ignore collisions early in its lifetime
  1867. virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  1868. {
  1869. if ( gpGlobals->curtime < m_flImpactTime )
  1870. return;
  1871. BaseClass::VPhysicsCollision( index, pEvent );
  1872. }
  1873. virtual void PipebombTouch( CBaseEntity *pOther )
  1874. {
  1875. if ( gpGlobals->curtime < m_flImpactTime )
  1876. return;
  1877. BaseClass::PipebombTouch( pOther );
  1878. }
  1879. virtual void ApplyBlastDamage ( CTFPlayer *pThrower, Vector vecOrigin ) { }
  1880. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  1881. {
  1882. // Spawn a pumkin bomb here
  1883. // Set the angles to what I want
  1884. QAngle angle(0, RandomFloat( 0, 360 ) ,0);
  1885. CTFPumpkinBomb *pGrenade = static_cast<CTFPumpkinBomb*>( CBaseEntity::CreateNoSpawn( "tf_pumpkin_bomb", GetAbsOrigin(), angle, NULL ) );
  1886. if ( pGrenade )
  1887. {
  1888. pGrenade->SetInitParams( 0.60, 80.0f, 200.0f, GetTeamNumber(), 40.0f + RandomFloat(0 , 1.0f) );
  1889. DispatchSpawn( pGrenade );
  1890. pGrenade->SetSpell( true );
  1891. }
  1892. if ( TFGameRules() )
  1893. {
  1894. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MIRV, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  1895. }
  1896. return true;
  1897. }
  1898. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) { }
  1899. virtual const char *GetExplodeEffectParticle() const { return GetTeamNumber() == TF_TEAM_RED ? "spell_pumpkin_mirv_goop_red" : "spell_pumpkin_mirv_goop_blue"; }
  1900. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_mirv_explode_secondary"; }
  1901. float m_flImpactTime;
  1902. #endif
  1903. #ifdef CLIENT_DLL
  1904. virtual const char* GetTrailParticleName( void ) { return "unusual_bubbles_green_fumes"; }
  1905. #endif
  1906. };
  1907. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellPumpkin, DT_TFProjectile_SpellPumpkin )
  1908. BEGIN_NETWORK_TABLE( CTFProjectile_SpellPumpkin, DT_TFProjectile_SpellPumpkin)
  1909. END_NETWORK_TABLE()
  1910. LINK_ENTITY_TO_CLASS( tf_projectile_spellpumpkin, CTFProjectile_SpellPumpkin );
  1911. PRECACHE_WEAPON_REGISTER( tf_projectile_spellpumpkin);
  1912. // *************************************************************************************************************************
  1913. class CTFProjectile_SpellMirv : public CTFProjectile_SpellBats
  1914. {
  1915. public:
  1916. DECLARE_CLASS( CTFProjectile_SpellMirv, CTFProjectile_SpellBats );
  1917. DECLARE_NETWORKCLASS();
  1918. virtual float GetDamageRadius() const { return 1.0f; }
  1919. virtual void SetCustomPipebombModel() { SetModel( "models/weapons/w_models/w_cannonball.mdl" ); }
  1920. virtual float GetModelScale() const { return 0.9f; }
  1921. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_MIRV; }
  1922. #ifdef GAME_DLL
  1923. //virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { }
  1924. //virtual void PipebombTouch( CBaseEntity *pOther ) { }
  1925. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  1926. {
  1927. // Spawn a tonne of extra grenades (mirv style)
  1928. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  1929. if ( !pSpellBook )
  1930. return false;
  1931. // Create bomblets
  1932. Vector offset = Vector( 0, -100, 400 );
  1933. for ( int i = 0; i < 6; i++ )
  1934. {
  1935. AngularImpulse angVelocity = AngularImpulse( 0, 0, RandomFloat( 100, 300) );
  1936. switch ( i )
  1937. {
  1938. case 0: offset = Vector( 75, 110, 400 ); break;
  1939. case 1: offset = Vector( 75, -110, 400 ); break;
  1940. case 2: offset = Vector( -75, 110, 400 ); break;
  1941. case 3: offset = Vector( -75, -110, 400 ); break;
  1942. case 4: offset = Vector( 135, 0, 400 ); break;
  1943. case 5: offset = Vector( -135, 0, 400 ); break;
  1944. }
  1945. CTFProjectile_SpellPumpkin *pGrenade = static_cast<CTFProjectile_SpellPumpkin*>( CBaseEntity::CreateNoSpawn( "tf_projectile_spellpumpkin", GetAbsOrigin(), pThrower->EyeAngles(), pThrower ) );
  1946. if ( pGrenade )
  1947. {
  1948. // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  1949. pGrenade->SetPipebombMode();
  1950. DispatchSpawn( pGrenade );
  1951. pGrenade->InitGrenade( offset, angVelocity, pThrower, pSpellBook->GetTFWpnData() );
  1952. pGrenade->m_flFullDamage = 0;
  1953. pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  1954. pGrenade->SetDetonateTimerLength( 2.0f + 0.05f * i );
  1955. }
  1956. }
  1957. if ( TFGameRules() )
  1958. {
  1959. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MIRV, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  1960. }
  1961. return true;
  1962. }
  1963. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) { }
  1964. virtual const char *GetExplodeEffectParticle() const { return GetTeamNumber() == TF_TEAM_RED ? "spell_pumpkin_mirv_goop_red" : "spell_pumpkin_mirv_goop_blue"; }
  1965. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_mirv_explode_primary"; }
  1966. #endif
  1967. #ifdef CLIENT_DLL
  1968. virtual const char* GetTrailParticleName( void ) { return "unusual_bubbles_green_fumes"; }
  1969. #endif
  1970. };
  1971. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellMirv, DT_TFProjectile_SpellMirv )
  1972. BEGIN_NETWORK_TABLE( CTFProjectile_SpellMirv, DT_TFProjectile_SpellMirv)
  1973. END_NETWORK_TABLE()
  1974. LINK_ENTITY_TO_CLASS( tf_projectile_spellmirv, CTFProjectile_SpellMirv );
  1975. PRECACHE_WEAPON_REGISTER( tf_projectile_spellmirv);
  1976. // *************************************************************************************************************************
  1977. class CTFProjectile_SpellSpawnBoss : public CTFProjectile_SpellBats
  1978. {
  1979. public:
  1980. DECLARE_CLASS( CTFProjectile_SpellSpawnBoss, CTFProjectile_SpellBats );
  1981. DECLARE_NETWORKCLASS();
  1982. virtual float GetDamageRadius() const { return 1.0f; }
  1983. virtual void SetCustomPipebombModel() { SetModel( "models/props_mvm/mvm_human_skull_collide.mdl" ); }
  1984. virtual float GetModelScale() const { return 1.5f; }
  1985. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_MONOCULUS; }
  1986. #ifdef GAME_DLL
  1987. //virtual void Explode( trace_t *pTrace, int bitsDamageType );
  1988. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  1989. {
  1990. const Vector &vContactPoint = pTrace->endpos;
  1991. CHalloweenBaseBoss *pBoss = CHalloweenBaseBoss::SpawnBossAtPos( HALLOWEEN_BOSS_MONOCULUS, vContactPoint, pThrower->GetTeamNumber(), pThrower );
  1992. if ( pBoss )
  1993. {
  1994. float flDesiredHeight = tf_eyeball_boss_hover_height.GetFloat();
  1995. const Vector &vMins = pBoss->WorldAlignMins();
  1996. const Vector &vMaxs = pBoss->WorldAlignMaxs();
  1997. Vector vSize = vMaxs - vMins;
  1998. float flBossHeight = vSize.z;
  1999. float flBossHalfX = 0.5f * vSize.x;
  2000. float flBossHalfY = 0.5f * vSize.y;
  2001. static Vector vTest[] =
  2002. {
  2003. Vector( 0, 0, flBossHeight ),
  2004. Vector( flBossHalfX, flBossHalfY, flBossHeight ),
  2005. Vector( -flBossHalfX, -flBossHalfY, flBossHeight ),
  2006. Vector( flBossHalfX, -flBossHalfY, flBossHeight ),
  2007. Vector( -flBossHalfX, flBossHalfY, flBossHeight )
  2008. };
  2009. bool bFoundValidSpawnPos = false;
  2010. for ( int i=0; i<ARRAYSIZE( vTest ); ++i )
  2011. {
  2012. trace_t result;
  2013. float bloat = 5.0f;
  2014. Vector vStart = vContactPoint + vTest[i] + 30.f * pTrace->plane.normal;
  2015. Vector vEnd = vStart + Vector( 0, 0, flDesiredHeight );
  2016. CTraceFilterNoNPCsOrPlayer filter( pBoss, COLLISION_GROUP_NONE );
  2017. UTIL_TraceHull( vStart, vEnd, vMins - Vector( bloat, bloat, 0 ), vMaxs + Vector( bloat, bloat, bloat ), MASK_SOLID | CONTENTS_PLAYERCLIP, &filter, &result );
  2018. if ( !result.startsolid )
  2019. {
  2020. pBoss->SetAbsOrigin( result.endpos );
  2021. bFoundValidSpawnPos = true;
  2022. //NDebugOverlay::SweptBox( vStart, vEnd, vMins - Vector( bloat, bloat, 0 ), vMaxs + Vector( bloat, bloat, bloat ), vec3_angle, 0, 255, 0, 0, 5.f );
  2023. //NDebugOverlay::Sphere( result.endpos, 10.f, 0, 255, 0, true, 5.f );
  2024. break;
  2025. }
  2026. else
  2027. {
  2028. // Maybe we should play fail sound here?
  2029. //NDebugOverlay::SweptBox( vStart, vEnd, vMins - Vector( bloat, bloat, 0 ), vMaxs + Vector( bloat, bloat, bloat ), vec3_angle, 255, 0, 0, 0, 5.f );
  2030. //NDebugOverlay::Sphere( result.endpos, 10.f, 255, 0, 0, true, 5.f );
  2031. }
  2032. }
  2033. // couldn't find any valid position
  2034. if ( !bFoundValidSpawnPos )
  2035. {
  2036. UTIL_Remove( pBoss );
  2037. pBoss = NULL;
  2038. }
  2039. }
  2040. // refund the player the spell
  2041. if ( !pBoss )
  2042. {
  2043. CTFSpellBook *pSpellBook = dynamic_cast< CTFSpellBook* >( pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  2044. if ( pSpellBook )
  2045. {
  2046. pSpellBook->SetSelectedSpell( GetSpellIndexFromContext( MP_CONCEPT_PLAYER_SPELL_MONOCULOUS ) );
  2047. }
  2048. return false;
  2049. }
  2050. if ( TFGameRules() )
  2051. {
  2052. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MONOCULOUS, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2053. }
  2054. return true;
  2055. }
  2056. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) { }
  2057. virtual const char *GetExplodeEffectParticle() const { return "eyeboss_death"; }
  2058. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_spawn_boss"; }
  2059. #endif
  2060. #ifdef CLIENT_DLL
  2061. virtual const char* GetTrailParticleName( void ) { return "unusual_bubbles_green_fumes"; }
  2062. #endif
  2063. };
  2064. // Spawn Boss
  2065. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellSpawnBoss, DT_TFProjectile_SpellSpawnBoss )
  2066. BEGIN_NETWORK_TABLE( CTFProjectile_SpellSpawnBoss, DT_TFProjectile_SpellSpawnBoss )
  2067. END_NETWORK_TABLE()
  2068. LINK_ENTITY_TO_CLASS( tf_projectile_spellspawnboss, CTFProjectile_SpellSpawnBoss );
  2069. PRECACHE_WEAPON_REGISTER( tf_projectile_spellspawnboss );
  2070. // *************************************************************************************************************************
  2071. #ifdef GAME_DLL
  2072. class CTFSpell_MeteorShowerSpawner : public CBaseEntity
  2073. {
  2074. public:
  2075. DECLARE_DATADESC();
  2076. DECLARE_CLASS( CTFSpell_MeteorShowerSpawner, CBaseEntity );
  2077. virtual void Spawn() OVERRIDE
  2078. {
  2079. m_flFinishTime = gpGlobals->curtime + 4.f;
  2080. SetContextThink( &CTFSpell_MeteorShowerSpawner::MeteorShowerThink, gpGlobals->curtime, "MeteorShowerThink" );
  2081. }
  2082. void MeteorShowerThink( void )
  2083. {
  2084. CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
  2085. if ( pPlayer && m_flFinishTime > gpGlobals->curtime )
  2086. {
  2087. // the owner changed team? remove this
  2088. if ( pPlayer->GetTeamNumber() != GetTeamNumber() )
  2089. {
  2090. UTIL_Remove( this );
  2091. return;
  2092. }
  2093. // Determine our "height" offset range based on surface normal
  2094. Vector vecDir = Vector( 0.f, 0.f, 1.f );
  2095. float flOffsetMin = 400.f;
  2096. float flOffsetMax = 500.f;
  2097. if ( m_vecImpactNormal.z <= -0.6f ) // Ceiling?
  2098. {
  2099. flOffsetMin = 45.f;
  2100. flOffsetMax = 60.f;
  2101. vecDir.z = -1.f;
  2102. }
  2103. const float flRange = 200.f;
  2104. const float flRandomAngleOffset = 75.f;
  2105. const int nNumToSpawn = random->RandomInt( 1, 2 );
  2106. for ( int i = 0; i < nNumToSpawn; ++i )
  2107. {
  2108. // Vary start point away from surface center
  2109. Vector vecOnPlane = Vector( RandomFloat( -flRange, flRange ), RandomFloat( -flRange, flRange ), 0.f ).Normalized();
  2110. Vector vecPointOnPlane = GetAbsOrigin() + random->RandomFloat( -flRange, flRange ) * vecOnPlane;
  2111. const float flOffsetFromPlane = random->RandomFloat( flOffsetMin, flOffsetMax );
  2112. Vector vecEmit = vecPointOnPlane + flOffsetFromPlane * vecDir;
  2113. // debugoverlay->AddLineOverlay( GetAbsOrigin(), vecEmit, 255, 0, 0, false, 10 );
  2114. Vector vecVelocity = Vector( RandomFloat( -flRandomAngleOffset, flRandomAngleOffset ), RandomFloat( -flRandomAngleOffset, flRandomAngleOffset ), -700.f );
  2115. // Check for a spot
  2116. trace_t trace;
  2117. UTIL_TraceLine( GetAbsOrigin(), vecEmit, ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), NULL, COLLISION_GROUP_NONE, &trace );
  2118. if ( !trace.DidHit() )
  2119. {
  2120. SpawnMeteor( pPlayer, trace.endpos, vec3_angle, vecVelocity );
  2121. }
  2122. else
  2123. {
  2124. // Pull back and try again
  2125. vecEmit = trace.endpos + ( trace.plane.normal * 1.0f );
  2126. SpawnMeteor( pPlayer, vecEmit, vec3_angle, vecVelocity );
  2127. }
  2128. }
  2129. SetContextThink( &CTFSpell_MeteorShowerSpawner::MeteorShowerThink, gpGlobals->curtime + 0.2f, "MeteorShowerThink" );
  2130. return;
  2131. }
  2132. UTIL_Remove( this );
  2133. }
  2134. void SpawnMeteor( CTFPlayer *pOwner, const Vector &origin, const QAngle &angles, const Vector &velocity )
  2135. {
  2136. CTFProjectile_SpellFireball *pRocket = static_cast< CTFProjectile_SpellFireball* >( CBaseEntity::CreateNoSpawn( "tf_projectile_spellfireball", origin, angles, pOwner ) );
  2137. if ( pRocket )
  2138. {
  2139. pRocket->SetOwnerEntity( pOwner );
  2140. pRocket->SetLauncher( pOwner );
  2141. pRocket->SetAbsVelocity( velocity );
  2142. pRocket->SetDamage( 50.f );
  2143. pRocket->SetMeteor( true );
  2144. pRocket->ChangeTeam( GetTeamNumber() );
  2145. const char *pszParticle = GetTeamNumber() == TF_TEAM_BLUE ? "spell_fireball_tendril_parent_blue" : "spell_fireball_tendril_parent_red";
  2146. pRocket->SetExplodeParticleName( pszParticle );
  2147. IPhysicsObject *pPhysicsObject = pRocket->VPhysicsGetObject();
  2148. if ( pPhysicsObject )
  2149. {
  2150. pPhysicsObject->AddVelocity( &velocity, NULL );
  2151. }
  2152. DispatchSpawn( pRocket );
  2153. }
  2154. }
  2155. void SetImpaceNormal( Vector &vecNormal ) { m_vecImpactNormal = vecNormal; }
  2156. private:
  2157. float m_flFinishTime;
  2158. Vector m_vecImpactNormal;
  2159. };
  2160. // Meteor Shower
  2161. LINK_ENTITY_TO_CLASS( tf_spell_meteorshowerspawner, CTFSpell_MeteorShowerSpawner );
  2162. BEGIN_DATADESC( CTFSpell_MeteorShowerSpawner )
  2163. END_DATADESC()
  2164. #endif // GAME_DLL
  2165. // *************************************************************************************************************************
  2166. class CTFProjectile_SpellMeteorShower : public CTFProjectile_SpellBats
  2167. {
  2168. public:
  2169. DECLARE_CLASS( CTFProjectile_SpellMeteorShower, CTFProjectile_SpellBats );
  2170. DECLARE_NETWORKCLASS();
  2171. virtual float GetModelScale() const { return 0.01f; }
  2172. #ifdef GAME_DLL
  2173. virtual void Explode( trace_t *pTrace, int bitsDamageType ) OVERRIDE
  2174. {
  2175. m_takedamage = DAMAGE_NO;
  2176. // Pull out of the wall a bit.
  2177. if ( pTrace->fraction != 1.0 )
  2178. {
  2179. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  2180. }
  2181. Vector vecOrigin = GetAbsOrigin();
  2182. // Particle
  2183. if ( GetExplodeEffectParticle() )
  2184. {
  2185. CPVSFilter filter( vecOrigin );
  2186. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  2187. }
  2188. // Sounds
  2189. if ( GetExplodeEffectSound() )
  2190. {
  2191. EmitSound( GetExplodeEffectSound() );
  2192. }
  2193. CTFSpell_MeteorShowerSpawner *pSpawner = static_cast< CTFSpell_MeteorShowerSpawner* >( CBaseEntity::CreateNoSpawn( "tf_spell_meteorshowerspawner", vecOrigin, vec3_angle, GetThrower() ) );
  2194. if ( pSpawner )
  2195. {
  2196. pSpawner->SetImpaceNormal( pTrace->plane.normal );
  2197. pSpawner->ChangeTeam( GetTeamNumber() );
  2198. DispatchSpawn( pSpawner );
  2199. }
  2200. if ( TFGameRules() )
  2201. {
  2202. CTFPlayer *pThrower = ToTFPlayer( GetOwnerEntity() );
  2203. if ( pThrower )
  2204. {
  2205. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_METEOR_SWARM, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2206. }
  2207. }
  2208. SetModelName( NULL_STRING );
  2209. AddSolidFlags( FSOLID_NOT_SOLID );
  2210. SetTouch( NULL );
  2211. AddEffects( EF_NODRAW );
  2212. SetAbsVelocity( vec3_origin );
  2213. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  2214. }
  2215. virtual const char *GetExplodeEffectParticle() const { return "bomibomicon_ring"; }
  2216. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_meteor_impact"; }
  2217. #endif // GAME_DLL
  2218. #ifdef CLIENT_DLL
  2219. virtual const char *GetTrailParticleName( void )
  2220. {
  2221. return GetTeamNumber() == TF_TEAM_BLUE ? "spell_fireball_small_blue" : "spell_fireball_small_red";
  2222. }
  2223. #endif
  2224. };
  2225. // Meteor Shower
  2226. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellMeteorShower, DT_TFProjectile_SpellMeteorShower )
  2227. BEGIN_NETWORK_TABLE( CTFProjectile_SpellMeteorShower, DT_TFProjectile_SpellMeteorShower)
  2228. END_NETWORK_TABLE()
  2229. LINK_ENTITY_TO_CLASS( tf_projectile_spellmeteorshower, CTFProjectile_SpellMeteorShower );
  2230. PRECACHE_WEAPON_REGISTER( tf_projectile_spellmeteorshower );
  2231. // *************************************************************************************************************************
  2232. class CTFProjectile_SpellTransposeTeleport : public CTFProjectile_SpellBats
  2233. {
  2234. public:
  2235. DECLARE_CLASS( CTFProjectile_SpellTransposeTeleport, CTFProjectile_SpellBats );
  2236. DECLARE_NETWORKCLASS();
  2237. virtual void Spawn( void )
  2238. {
  2239. SetModelScale( 0.01f );
  2240. BaseClass::Spawn();
  2241. SetCollisionGroup( COLLISION_GROUP_PLAYER_MOVEMENT );
  2242. #ifdef GAME_DLL
  2243. SetContextThink( &CTFProjectile_SpellTransposeTeleport::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" );
  2244. #endif
  2245. }
  2246. // FIX
  2247. virtual int GetWeaponID( void ) const { return TF_PROJECTILE_SPELL; }
  2248. virtual float GetDamageRadius() const { return 5.0f; }
  2249. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_TELEPORT; }
  2250. virtual unsigned int PhysicsSolidMaskForEntity( void ) const
  2251. {
  2252. return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
  2253. }
  2254. #ifdef GAME_DLL
  2255. void RecordPosThink( void )
  2256. {
  2257. m_vecTrailingPos.AddToTail( GetAbsOrigin() );
  2258. // Only retain 5 positions
  2259. if ( m_vecTrailingPos.Count() > 5 )
  2260. {
  2261. m_vecTrailingPos.Remove( 0 );
  2262. }
  2263. SetContextThink( &CTFProjectile_SpellTransposeTeleport::RecordPosThink, gpGlobals->curtime + 0.05f, "RecordThink" );
  2264. }
  2265. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  2266. {
  2267. if ( !pThrower->IsAlive() )
  2268. return false;
  2269. // Grant a small amount of health
  2270. pThrower->TakeHealth( 30, DMG_GENERIC );
  2271. trace_t result;
  2272. CTraceFilterIgnoreTeammates traceFilter( this, COLLISION_GROUP_PLAYER_MOVEMENT, GetTeamNumber() );
  2273. unsigned int nMask = pThrower->GetTeamNumber() == TF_TEAM_RED ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM;
  2274. nMask |= MASK_PLAYERSOLID;
  2275. m_vecTrailingPos.AddToTail( pTrace->endpos + ( pTrace->plane.normal * 50.f ) );
  2276. // Try a few spots
  2277. FOR_EACH_VEC_BACK( m_vecTrailingPos, i )
  2278. {
  2279. // Try positions starting with the current, and moving back in time a bit
  2280. Vector vecStart = m_vecTrailingPos[i];
  2281. UTIL_TraceHull( vecStart, vecStart, VEC_HULL_MIN, VEC_HULL_MAX, nMask, &traceFilter, &result );
  2282. if( !result.DidHit() )
  2283. {
  2284. // Place a teleport effect where they came from
  2285. const Vector& vecOrigin = pThrower->GetAbsOrigin();
  2286. CPVSFilter pvsFilter( vecOrigin );
  2287. TE_TFParticleEffect( pvsFilter, 0.0, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  2288. // Move 'em!
  2289. pThrower->Teleport( &vecStart, &pThrower->GetAbsAngles(), NULL );
  2290. // Do a zoom effect
  2291. pThrower->SetFOV( pThrower, 0, 0.3f, 120 );
  2292. // Screen flash
  2293. color32 fadeColor = {255,255,255,100};
  2294. UTIL_ScreenFade( pThrower, fadeColor, 0.25, 0.4, FFADE_IN );
  2295. if ( TFGameRules() )
  2296. {
  2297. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_TELEPORT, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2298. }
  2299. // Success!
  2300. return true;
  2301. }
  2302. }
  2303. return false;
  2304. }
  2305. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget )
  2306. {
  2307. // ...
  2308. }
  2309. virtual const char *GetExplodeEffectParticle() const { return "eyeboss_tp_player"; }
  2310. virtual const char *GetExplodeEffectSound() const { return "Building_Teleporter.Ready"; }
  2311. #endif
  2312. #ifdef CLIENT_DLL
  2313. virtual const char* GetTrailParticleName( void ) { return GetTeamNumber() == TF_TEAM_RED ? "spell_teleport_red" : "spell_teleport_blue"; }
  2314. #endif
  2315. private:
  2316. #ifdef GAME_DLL
  2317. CUtlVector< Vector > m_vecTrailingPos;
  2318. #endif
  2319. };
  2320. // Spawn Boss
  2321. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellTransposeTeleport, SpellTransposeTeleport )
  2322. BEGIN_NETWORK_TABLE( CTFProjectile_SpellTransposeTeleport, SpellTransposeTeleport )
  2323. END_NETWORK_TABLE()
  2324. LINK_ENTITY_TO_CLASS( tf_projectile_spelltransposeteleport, CTFProjectile_SpellTransposeTeleport );
  2325. PRECACHE_WEAPON_REGISTER( tf_projectile_spelltransposeteleport );
  2326. #ifdef GAME_DLL
  2327. void RemoveAll2013HalloweenTeleportSpellsInMidFlight( void )
  2328. {
  2329. CBaseEntity *pTeleport = NULL;
  2330. while ( ( pTeleport = gEntList.FindEntityByClassname( pTeleport, "tf_projectile_spelltransposeteleport" ) ) != NULL )
  2331. {
  2332. UTIL_Remove( pTeleport );
  2333. }
  2334. }
  2335. #endif
  2336. // *************************************************************************************************************************
  2337. class CTFProjectile_SpellLightningOrb : public CTFProjectile_SpellFireball
  2338. {
  2339. public:
  2340. DECLARE_CLASS( CTFProjectile_SpellLightningOrb, CTFProjectile_SpellFireball );
  2341. DECLARE_NETWORKCLASS();
  2342. ~CTFProjectile_SpellLightningOrb()
  2343. {
  2344. #ifdef CLIENT_DLL
  2345. if ( m_pTrailParticle )
  2346. {
  2347. ParticleProp()->StopEmissionAndDestroyImmediately( m_pTrailParticle );
  2348. m_pTrailParticle = NULL;
  2349. }
  2350. #endif // CLIENT_DLL
  2351. }
  2352. #ifdef GAME_DLL
  2353. virtual void Spawn() OVERRIDE
  2354. {
  2355. BaseClass::Spawn();
  2356. // We dont want to collide with anything but the world
  2357. SetSolid( SOLID_NONE );
  2358. SetExplodeParticleName( GetTeamNumber() == TF_TEAM_BLUE ? "drg_cow_explosioncore_charged_blue" : "drg_cow_explosioncore_charged" );
  2359. SetContextThink( &CTFProjectile_SpellLightningOrb::ZapThink, gpGlobals->curtime + 0.25f, "ZapThink" );
  2360. SetContextThink( &CTFProjectile_SpellLightningOrb::VortexThink, gpGlobals->curtime + 0.2f, "VortexThink" );
  2361. SetContextThink( &CTFProjectile_SpellLightningOrb::ExplodeAndRemove, gpGlobals->curtime + 5.f, "ExplodeAndRemoveThink" );
  2362. SetDamage( 20.f );
  2363. }
  2364. virtual const char *GetProjectileModelName( void ) { return ""; } // We dont have a model by default, and that's OK
  2365. virtual float GetDamageRadius() const { return 200.f; }
  2366. virtual int GetCustomDamageType() const OVERRIDE { return TF_DMG_CUSTOM_SPELL_LIGHTNING; }
  2367. virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE
  2368. {
  2369. Assert( pOther );
  2370. if ( !pOther )
  2371. return;
  2372. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  2373. return;
  2374. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  2375. // Bounce off the world
  2376. if ( pOther->IsWorld() )
  2377. {
  2378. Vector vIntoSurface = pTrace->plane.normal * pTrace->plane.normal.Dot( GetAbsVelocity() );
  2379. SetAbsVelocity( GetAbsVelocity() + ( -1.5f * vIntoSurface ) );
  2380. return;
  2381. }
  2382. // Handle hitting skybox (disappear).
  2383. if ( pTrace->surface.flags & SURF_SKY )
  2384. {
  2385. UTIL_Remove( this );
  2386. return;
  2387. }
  2388. // pass through ladders
  2389. if( pTrace->surface.flags & CONTENTS_LADDER )
  2390. return;
  2391. if ( pOther->IsPlayer() )
  2392. return;
  2393. // Spell ends when we run into something
  2394. ExplodeAndRemove();
  2395. return;
  2396. }
  2397. virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  2398. {
  2399. Zap( 16 );
  2400. return true;
  2401. }
  2402. virtual const char *GetExplodeEffectSound() const { return "Halloween.spell_lightning_impact"; }
  2403. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget ) OVERRIDE
  2404. {}
  2405. void ExplodeAndRemove()
  2406. {
  2407. // Particle
  2408. if ( GetExplodeEffectParticle() )
  2409. {
  2410. CPVSFilter filter( GetAbsOrigin() );
  2411. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), GetAbsOrigin(), vec3_angle );
  2412. EmitSound( filter, entindex(), GetExplodeEffectSound() );
  2413. }
  2414. // Go out with a bang
  2415. Zap( 16 );
  2416. SetContextThink( &CBaseGrenade::SUB_Remove, gpGlobals->curtime, "RemoveThink" );
  2417. return;
  2418. }
  2419. void ZapThink()
  2420. {
  2421. Zap( 2 );
  2422. SetContextThink( &CTFProjectile_SpellLightningOrb::ZapThink, gpGlobals->curtime + RandomFloat( 0.25f, 0.35f ), "ZapThink" );
  2423. }
  2424. void Zap( int nNumToZap )
  2425. {
  2426. CBaseEntity *pOwner = GetOwnerEntity();
  2427. if ( !pOwner )
  2428. return;
  2429. CTakeDamageInfo info;
  2430. info.SetAttacker( pOwner );
  2431. info.SetInflictor( this );
  2432. info.SetWeapon( GetLauncher() );
  2433. info.SetDamage( GetDamage() );
  2434. info.SetDamageCustom( GetCustomDamageType() );
  2435. info.SetDamagePosition( GetAbsOrigin() );
  2436. info.SetDamageType( DMG_BURN );
  2437. CBaseEntity *pListOfEntities[5];
  2438. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 5, GetAbsOrigin(), GetDamageRadius(), FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  2439. // Shuffle the list
  2440. for( int i = iEntities - 1; i > 0; --i )
  2441. {
  2442. V_swap( pListOfEntities[i], pListOfEntities[ RandomInt( 0, i ) ] );
  2443. }
  2444. // Zap as many targets as we're told to, if we can
  2445. int nHits = 0;
  2446. for ( int i = 0; i < iEntities && nHits < nNumToZap; ++i )
  2447. {
  2448. CBaseEntity* pTarget = pListOfEntities[i];
  2449. if ( !pTarget )
  2450. continue;
  2451. if ( !pTarget->IsAlive() )
  2452. continue;
  2453. if ( pOwner->InSameTeam( pTarget ) )
  2454. continue;
  2455. if ( !FVisible( pTarget, MASK_OPAQUE ) )
  2456. continue;
  2457. CTFPlayer *pTFPlayer = ToTFPlayer( pTarget );
  2458. if ( pTFPlayer )
  2459. {
  2460. if ( pTFPlayer->m_Shared.InCond( TF_COND_PHASE ) || pTFPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  2461. continue;
  2462. if ( pTFPlayer->m_Shared.IsInvulnerable() )
  2463. continue;
  2464. }
  2465. CTraceFilterIgnoreTeammates tracefilter( this, COLLISION_GROUP_NONE, GetTeamNumber() );
  2466. trace_t trace;
  2467. UTIL_TraceLine( GetAbsOrigin(), pTarget->GetAbsOrigin(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &tracefilter, &trace );
  2468. if ( trace.DidHitWorld() )
  2469. continue;
  2470. // Shoot a beam at them
  2471. CPVSFilter filter( pTarget->WorldSpaceCenter() );
  2472. Vector vStart = WorldSpaceCenter();
  2473. Vector vEnd = pTarget->EyePosition();
  2474. const char *pszHitEffect = GetTeamNumber() == TF_TEAM_BLUE ? "spell_lightningball_hit_blue" : "spell_lightningball_hit_red";
  2475. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  2476. TE_TFParticleEffectComplex( filter, 0.0f, pszHitEffect, vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, pTFPlayer, PATTACH_CUSTOMORIGIN );
  2477. // Hurt 'em.
  2478. Vector dir;
  2479. AngleVectors( GetAbsAngles(), &dir );
  2480. pTarget->DispatchTraceAttack( info, dir, &trace );
  2481. ApplyMultiDamage();
  2482. ++nHits;
  2483. }
  2484. // We zapped someone. Play a sound
  2485. if ( nHits > 0 )
  2486. {
  2487. pOwner->EmitSound( "TFPlayer.MedicChargedDeath" );
  2488. if ( TFGameRules() )
  2489. {
  2490. CTFPlayer *pThrower = ToTFPlayer( GetOwnerEntity() );
  2491. if ( pThrower )
  2492. {
  2493. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_LIGHTNING_BALL, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2494. }
  2495. }
  2496. }
  2497. }
  2498. void VortexThink( void )
  2499. {
  2500. const int nMaxEnts = 32;
  2501. Vector vecPos = GetAbsOrigin();
  2502. CBaseEntity *pObjects[ nMaxEnts ];
  2503. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecPos, GetDamageRadius(), FL_CLIENT | FL_NPC );
  2504. // NDebugOverlay::Sphere( vecPos, GetDamageRadius(), 0, 255, 0, false, 0.35f );
  2505. // Iterate through sphere's contents
  2506. for ( int i = 0; i < nCount; i++ )
  2507. {
  2508. CBaseCombatCharacter *pEntity = pObjects[i]->MyCombatCharacterPointer();
  2509. if ( !pEntity )
  2510. continue;
  2511. if ( InSameTeam( pEntity ) )
  2512. continue;
  2513. if ( !FVisible( pEntity, MASK_OPAQUE ) )
  2514. continue;
  2515. // Draw player toward us
  2516. Vector vecSourcePos = pEntity->GetAbsOrigin();
  2517. Vector vecTargetPos = GetAbsOrigin();
  2518. Vector vecVelocity = ( vecTargetPos - vecSourcePos ) * 2.f;
  2519. vecVelocity.z += 50.f;
  2520. if ( pEntity->GetFlags() & FL_ONGROUND )
  2521. {
  2522. vecVelocity.z += 150.f;
  2523. pEntity->SetGroundEntity( NULL );
  2524. pEntity->SetGroundChangeTime( gpGlobals->curtime + 0.5f );
  2525. }
  2526. pEntity->Teleport( NULL, NULL, &vecVelocity );
  2527. }
  2528. SetContextThink( &CTFProjectile_SpellLightningOrb::VortexThink, gpGlobals->curtime + 0.2f, "VortexThink" );
  2529. return;
  2530. }
  2531. #endif
  2532. #ifdef CLIENT_DLL
  2533. virtual const char *GetTrailParticleName( void ) { return NULL; } // CRUTGUN!
  2534. virtual void CreateTrails( void )
  2535. {
  2536. BaseClass::CreateTrails();
  2537. if ( !m_pTrailParticle )
  2538. {
  2539. m_pTrailParticle = ParticleProp()->Create( ( GetTeamNumber() == TF_TEAM_BLUE ? "spell_lightningball_parent_blue" : "spell_lightningball_parent_red" ), PATTACH_ABSORIGIN_FOLLOW );
  2540. }
  2541. }
  2542. private:
  2543. CNewParticleEffect *m_pTrailParticle;
  2544. #endif // CLIENT_DLL
  2545. };
  2546. // Lightning ball
  2547. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellLightningOrb, DT_TFProjectile_SpellLightningOrb )
  2548. BEGIN_NETWORK_TABLE( CTFProjectile_SpellLightningOrb, DT_TFProjectile_SpellLightningOrb )
  2549. END_NETWORK_TABLE()
  2550. LINK_ENTITY_TO_CLASS( tf_projectile_lightningorb, CTFProjectile_SpellLightningOrb );
  2551. PRECACHE_WEAPON_REGISTER( tf_projectile_lightningorb);
  2552. #ifdef CLIENT_DLL
  2553. #define CTFHellZap C_TFHellZap
  2554. #endif
  2555. #ifdef GAME_DLL
  2556. #include "tf_obj_dispenser.h"
  2557. #include "particle_parse.h"
  2558. #include "tf_fx.h"
  2559. #endif
  2560. class CTFHellZap : public CBaseEntity
  2561. {
  2562. DECLARE_CLASS( CTFHellZap, CBaseEntity )
  2563. DECLARE_NETWORKCLASS();
  2564. DECLARE_DATADESC();
  2565. public:
  2566. CTFHellZap()
  2567. #ifdef GAME_DLL
  2568. : m_eType( ZAP_ON_TOUCH )
  2569. #endif
  2570. {}
  2571. #ifdef GAME_DLL
  2572. enum EZapperType
  2573. {
  2574. ZAP_ON_TOUCH,
  2575. ZAP_ON_TEST,
  2576. };
  2577. virtual void Spawn()
  2578. {
  2579. m_bEnabled = true;
  2580. if ( m_iszCustomTouchTrigger != NULL_STRING )
  2581. {
  2582. m_hTouchTrigger = dynamic_cast<CDispenserTouchTrigger *> ( gEntList.FindEntityByName( NULL, m_iszCustomTouchTrigger ) );
  2583. if ( m_hTouchTrigger.Get() != NULL )
  2584. {
  2585. Assert( m_hTouchTrigger->GetOwnerEntity() == NULL );
  2586. m_hTouchTrigger->SetOwnerEntity( this ); //owned
  2587. }
  2588. }
  2589. }
  2590. void ZapAllTouching()
  2591. {
  2592. FOR_EACH_VEC_BACK( m_vecZapTargets, i )
  2593. {
  2594. CBaseEntity* pZapTarget = m_vecZapTargets[i].Get();
  2595. // Remove targets that have disappeared
  2596. if ( !pZapTarget )
  2597. {
  2598. m_vecZapTargets.Remove( i );
  2599. continue;
  2600. }
  2601. // Shoot a beam at them
  2602. CPVSFilter filter( pZapTarget->WorldSpaceCenter() );
  2603. Vector vStart = WorldSpaceCenter();
  2604. Vector vEnd = pZapTarget->WorldSpaceCenter();
  2605. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_CUSTOMORIGIN, vEnd };
  2606. TE_TFParticleEffectComplex( filter, 0.0f, m_iszParticleName.ToCStr(), vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, this, PATTACH_CUSTOMORIGIN );
  2607. }
  2608. }
  2609. void ZapThink()
  2610. {
  2611. ZapAllTouching();
  2612. // Keep zapping if we have targets
  2613. if ( m_vecZapTargets.Count() )
  2614. {
  2615. SetContextThink( &CTFHellZap::ZapThink, gpGlobals->curtime + 0.25f, "ZapThink" );
  2616. }
  2617. }
  2618. virtual void StartTouch( CBaseEntity *pEntity )
  2619. {
  2620. m_vecZapTargets.AddToTail( pEntity );
  2621. if ( m_eType == ZAP_ON_TOUCH )
  2622. {
  2623. SetContextThink( &CTFHellZap::ZapThink, gpGlobals->curtime, "ZapThink" );
  2624. }
  2625. }
  2626. virtual void EndTouch( CBaseEntity *pEntity )
  2627. {
  2628. int nIndex = m_vecZapTargets.Find( pEntity );
  2629. if( nIndex != m_vecZapTargets.InvalidIndex() )
  2630. {
  2631. m_vecZapTargets.Remove( nIndex );
  2632. }
  2633. // No more targets. Stop thinking!
  2634. if ( m_vecZapTargets.Count() == 0 && m_eType == ZAP_ON_TOUCH )
  2635. {
  2636. SetContextThink( NULL, 0, "ZapThink" );
  2637. }
  2638. }
  2639. void InputEnable( inputdata_t &inputdata )
  2640. {
  2641. m_bEnabled = true;
  2642. if ( m_vecZapTargets.Count() > 0 && m_eType == ZAP_ON_TOUCH )
  2643. {
  2644. SetContextThink( &CTFHellZap::ZapThink, gpGlobals->curtime, "ZapThink" );
  2645. }
  2646. }
  2647. void InputDisable( inputdata_t &inputdata )
  2648. {
  2649. m_bEnabled = false;
  2650. SetContextThink( NULL, 0, "ZapThink" );
  2651. }
  2652. void InputZapAllTouching( inputdata_t &inputdata )
  2653. {
  2654. ZapAllTouching();
  2655. }
  2656. private:
  2657. EZapperType m_eType;
  2658. bool m_bEnabled;
  2659. EHANDLE m_hTouchTrigger;
  2660. string_t m_iszCustomTouchTrigger;
  2661. string_t m_iszParticleName;
  2662. CUtlVector< EHANDLE > m_vecZapTargets;
  2663. #endif
  2664. };
  2665. BEGIN_DATADESC( CTFHellZap )
  2666. #ifdef GAME_DLL
  2667. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  2668. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  2669. DEFINE_INPUTFUNC( FIELD_VOID, "ZapTouching", InputZapAllTouching ),
  2670. DEFINE_KEYFIELD( m_iszCustomTouchTrigger, FIELD_STRING, "touch_trigger" ),
  2671. DEFINE_KEYFIELD( m_iszParticleName, FIELD_STRING, "ParticleEffect" ),
  2672. DEFINE_KEYFIELD( m_eType, FIELD_INTEGER, "ZapperType" ),
  2673. #endif
  2674. END_DATADESC()
  2675. LINK_ENTITY_TO_CLASS( halloween_zapper, CTFHellZap );
  2676. IMPLEMENT_NETWORKCLASS_ALIASED( TFHellZap, DT_TFHellZap )
  2677. BEGIN_NETWORK_TABLE( CTFHellZap, DT_TFHellZap )
  2678. END_NETWORK_TABLE()
  2679. //*******************************************************************************************************************************************************
  2680. // Kart Spells
  2681. //*******************************************************************************************************************************************************
  2682. class CTFProjectile_SpellKartOrb: public CTFProjectile_SpellFireball
  2683. {
  2684. public:
  2685. DECLARE_CLASS( CTFProjectile_SpellKartOrb, CTFProjectile_SpellFireball );
  2686. DECLARE_NETWORKCLASS();
  2687. #ifdef GAME_DLL
  2688. virtual void Spawn() OVERRIDE
  2689. {
  2690. BaseClass::Spawn();
  2691. SetContextThink( &CTFProjectile_SpellKartOrb::ExplodeAndRemove, gpGlobals->curtime + tf_halloween_kart_rocketspell_lifetime.GetFloat(), "ExplodeAndRemoveThink" );
  2692. SetContextThink( &CTFProjectile_SpellKartOrb::MoveChecking, gpGlobals->curtime + 0.05f, "MoveCheckingThink" );
  2693. SetModel( SPELL_BOXING_GLOVE );
  2694. SetModelScale( 2.5f );
  2695. Vector mins( -20, -20, 0 );
  2696. Vector maxs( 20, 20, 20 );
  2697. UTIL_SetSize( this, mins, maxs );
  2698. }
  2699. virtual void RocketTouch( CBaseEntity *pOther ) OVERRIDE
  2700. {
  2701. Assert( pOther );
  2702. if ( !pOther )
  2703. return;
  2704. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  2705. return;
  2706. // Handle hitting skybox (disappear).
  2707. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  2708. if ( pTrace->surface.flags & SURF_SKY )
  2709. {
  2710. UTIL_Remove( this );
  2711. return;
  2712. }
  2713. // Bounce off the world
  2714. if ( pOther->IsWorld() )
  2715. {
  2716. Vector vOld = GetAbsVelocity();
  2717. //float flSpeed = vOld.Length();
  2718. Vector vNew = ( -2.0f * pTrace->plane.normal.Dot( vOld ) * pTrace->plane.normal + vOld );
  2719. vNew.NormalizeInPlace();
  2720. vNew *= tf_halloween_kart_rocketspell_speed.GetFloat();
  2721. SetAbsVelocity( vNew );
  2722. return;
  2723. }
  2724. // pass through ladders
  2725. if ( pTrace->surface.flags & CONTENTS_LADDER )
  2726. return;
  2727. if ( pOther->IsPlayer() )
  2728. ExplodeAndRemove();
  2729. // Spell ends when we run into something
  2730. //ExplodeAndRemove();
  2731. return;
  2732. }
  2733. void MoveChecking ()
  2734. {
  2735. // do a short trace down, if nothing is there, add a bit of downward velocity
  2736. trace_t pTrace;
  2737. Vector vecSpot = GetAbsOrigin() ;
  2738. UTIL_TraceLine( vecSpot, vecSpot - Vector(0, 0, 32), MASK_SOLID, this, COLLISION_GROUP_NONE, &pTrace );
  2739. if ( pTrace.fraction >= 1.0 )
  2740. {
  2741. // Start moving down
  2742. SetAbsVelocity( GetAbsVelocity() - Vector( 0, 0, 128 ) );
  2743. }
  2744. SetContextThink( &CTFProjectile_SpellKartOrb::MoveChecking, gpGlobals->curtime + 0.05f, "MoveCheckingThink" );
  2745. }
  2746. void ExplodeAndRemove()
  2747. {
  2748. // Handle hitting skybox (disappear).
  2749. const trace_t *pTrace = &CBaseEntity::GetTouchTrace();
  2750. if ( pTrace->surface.flags & SURF_SKY )
  2751. {
  2752. UTIL_Remove( this );
  2753. return;
  2754. }
  2755. // pass through ladders
  2756. if ( pTrace->surface.flags & CONTENTS_LADDER )
  2757. return;
  2758. Explode( pTrace );
  2759. UTIL_Remove( this );
  2760. }
  2761. // We dont deal actual damage, just Car damage
  2762. virtual void Explode( const trace_t *pTrace )
  2763. {
  2764. SetModelName( NULL_STRING );//invisible
  2765. AddSolidFlags( FSOLID_NOT_SOLID );
  2766. m_takedamage = DAMAGE_NO;
  2767. // Pull out of the wall a bit.
  2768. if ( pTrace->fraction != 1.0 )
  2769. {
  2770. SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) );
  2771. }
  2772. CTFPlayer *pThrower = ToTFPlayer( GetOwnerEntity() );
  2773. if ( pThrower )
  2774. {
  2775. const Vector &vecOrigin = GetAbsOrigin();
  2776. // Any effects from the initial explosion
  2777. if ( InitialExplodeEffects( pThrower, pTrace ) )
  2778. {
  2779. // Particle
  2780. if ( GetExplodeEffectParticle() )
  2781. {
  2782. CPVSFilter filter( vecOrigin );
  2783. TE_TFParticleEffect( filter, 0.0, GetExplodeEffectParticle(), vecOrigin, vec3_angle );
  2784. }
  2785. // Sounds
  2786. if ( GetExplodeEffectSound() )
  2787. {
  2788. EmitSound( GetExplodeEffectSound() );
  2789. }
  2790. // Treat this trace exactly like radius damage
  2791. CTraceFilterIgnorePlayers traceFilter( pThrower, COLLISION_GROUP_PROJECTILE );
  2792. // Splash pee on everyone nearby.
  2793. CBaseEntity *pListOfEntities[32];
  2794. int iEntities = UTIL_EntitiesInSphere( pListOfEntities, 32, vecOrigin, GetDamageRadius(), FL_CLIENT | FL_FAKECLIENT | FL_NPC );
  2795. for ( int i = 0; i < iEntities; ++i )
  2796. {
  2797. CBaseCombatCharacter *pBasePlayer = NULL;
  2798. CTFPlayer *pPlayer = ToTFPlayer( pListOfEntities[i] );
  2799. if ( !pPlayer )
  2800. {
  2801. pBasePlayer = dynamic_cast<CBaseCombatCharacter*>( pListOfEntities[i] );
  2802. }
  2803. else
  2804. {
  2805. pBasePlayer = pPlayer;
  2806. }
  2807. if ( !pBasePlayer || !pPlayer || !pPlayer->IsAlive() || InSameTeam(pPlayer) )
  2808. continue;
  2809. // Do a quick trace to see if there's any geometry in the way.
  2810. trace_t trace;
  2811. UTIL_TraceLine( vecOrigin, pPlayer->WorldSpaceCenter(), ( MASK_SHOT & ~( CONTENTS_HITBOX ) ), &traceFilter, &trace );
  2812. //debugoverlay->AddLineOverlay( vecOrigin, pPlayer->WorldSpaceCenter(), 255, 0, 0, false, 10 );
  2813. if ( trace.DidHitWorld() )
  2814. continue;
  2815. // Effects on the individual players
  2816. //ExplodeEffectOnTarget( pThrower, pPlayer, pBasePlayer );
  2817. // Apply Car Damage and a force
  2818. Vector vecDir = pPlayer->WorldSpaceCenter() - GetAbsOrigin();
  2819. vecDir.NormalizeInPlace();
  2820. Vector vecForward, vecRight, vecUp;
  2821. AngleVectors( pPlayer->GetAnimRenderAngles(), &vecForward, &vecRight, &vecUp );
  2822. vecDir += ( vecUp * 0.5f );
  2823. pPlayer->AddHalloweenKartPushEvent( pThrower, this, pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ), vecDir * tf_halloween_kart_rocketspell_force.GetFloat(), 50.0f );
  2824. }
  2825. }
  2826. else
  2827. {
  2828. pThrower->EmitSound( "Player.DenyWeaponSelection" );
  2829. }
  2830. }
  2831. }
  2832. virtual const char *GetExplodeEffectParticle() const { return "ExplosionCore_MidAir"; }
  2833. #endif
  2834. #ifdef CLIENT_DLL
  2835. virtual const char *GetTrailParticleName( void )
  2836. {
  2837. return "halloween_rockettrail";
  2838. }
  2839. #endif
  2840. };
  2841. // Kart Spell Orbs
  2842. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellKartOrb, DT_TFProjectile_SpellKartOrb )
  2843. BEGIN_NETWORK_TABLE( CTFProjectile_SpellKartOrb, DT_TFProjectile_SpellKartOrb )
  2844. END_NETWORK_TABLE()
  2845. LINK_ENTITY_TO_CLASS( tf_projectile_spellkartorb, CTFProjectile_SpellKartOrb );
  2846. PRECACHE_WEAPON_REGISTER( tf_projectile_spellkartorb );
  2847. class CTFProjectile_SpellKartBats : public CTFProjectile_SpellBats
  2848. {
  2849. public:
  2850. DECLARE_CLASS( CTFProjectile_SpellKartBats, CTFProjectile_SpellBats );
  2851. DECLARE_NETWORKCLASS();
  2852. #ifdef GAME_DLL
  2853. virtual void ApplyBlastDamage( CTFPlayer *pThrower, Vector vecOrigin )
  2854. {
  2855. }
  2856. virtual void ExplodeEffectOnTarget( CTFPlayer *pThrower, CTFPlayer *pTarget, CBaseCombatCharacter *pBaseTarget )
  2857. {
  2858. if ( pBaseTarget->GetTeamNumber() == GetTeamNumber() )
  2859. return;
  2860. if ( !pTarget )
  2861. return;
  2862. if ( pTarget->m_Shared.IsInvulnerable() )
  2863. return;
  2864. if ( pTarget->m_Shared.InCond( TF_COND_PHASE ) || pTarget->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
  2865. return;
  2866. // Stun the target
  2867. pTarget->m_Shared.StunPlayer( 0.5, 0.5, TF_STUN_MOVEMENT, pThrower );
  2868. Vector vecDir = pBaseTarget->WorldSpaceCenter() - GetAbsOrigin();
  2869. VectorNormalize( vecDir );
  2870. Vector vecForward, vecRight, vecUp;
  2871. AngleVectors( pTarget->GetAnimRenderAngles(), &vecForward, &vecRight, &vecUp );
  2872. vecDir += ( vecUp * 0.5f );
  2873. if ( pTarget )
  2874. {
  2875. pTarget->AddHalloweenKartPushEvent( pThrower, this, pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ), vecDir * tf_halloween_kart_normal_speed.GetFloat() * 1.10f, 45.0f );
  2876. const char* pszEffectName = GetTeamNumber() == TF_TEAM_RED ? "spell_batball_red" : "spell_batball_blue";
  2877. DispatchParticleEffect( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, pTarget );
  2878. }
  2879. }
  2880. #endif
  2881. };
  2882. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellKartBats, DT_TFProjectile_SpellKartBats )
  2883. BEGIN_NETWORK_TABLE( CTFProjectile_SpellKartBats, DT_TFProjectile_SpellKartBats )
  2884. END_NETWORK_TABLE()
  2885. LINK_ENTITY_TO_CLASS( tf_projectile_spellkartbats, CTFProjectile_SpellKartBats );
  2886. PRECACHE_WEAPON_REGISTER( tf_projectile_spellkartbats );
  2887. //// *************************************************************************************************************************
  2888. //class CTFProjectile_SpellKartPumpkin : public CTFProjectile_SpellPumpkin
  2889. //{
  2890. //public:
  2891. // DECLARE_CLASS( CTFProjectile_SpellKartPumpkin, CTFProjectile_SpellPumpkin );
  2892. // DECLARE_NETWORKCLASS();
  2893. //
  2894. //#ifdef GAME_DLL
  2895. // virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  2896. // {
  2897. // // Spawn a pumkin bomb here
  2898. // // Set the angles to what I want
  2899. // QAngle angle( 0, RandomFloat( 0, 360 ), 0 );
  2900. // CTFPumpkinBomb *pGrenade = static_cast<CTFPumpkinBomb*>( CBaseEntity::CreateNoSpawn( "tf_pumpkin_bomb", GetAbsOrigin(), angle, NULL ) );
  2901. // if ( pGrenade )
  2902. // {
  2903. // pGrenade->SetInitParams( 0.60, 80.0f, 200.0f, GetTeamNumber(), 40.0f + RandomFloat( 0, 1.0f ) );
  2904. // DispatchSpawn( pGrenade );
  2905. // pGrenade->SetSpell( true );
  2906. // pGrenade->TakeDamage( CTakeDamageInfo( pThrower, pThrower, 10.f, DMG_CRUSH ) );
  2907. // }
  2908. //
  2909. // if ( TFGameRules() )
  2910. // {
  2911. // TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MIRV, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2912. // }
  2913. //
  2914. // return true;
  2915. // }
  2916. //#endif
  2917. //};
  2918. //
  2919. //IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellKartPumpkin, DT_TFProjectile_SpellKartPumpkin )
  2920. //BEGIN_NETWORK_TABLE( CTFProjectile_SpellKartPumpkin, DT_TFProjectile_SpellKartPumpkin )
  2921. //END_NETWORK_TABLE()
  2922. //
  2923. //LINK_ENTITY_TO_CLASS( tf_projectile_spellkartpumpkin, CTFProjectile_SpellKartPumpkin );
  2924. //PRECACHE_WEAPON_REGISTER( tf_projectile_spellkartpumpkin );
  2925. //
  2926. ////*************
  2927. //// *************************************************************************************************************************
  2928. //class CTFProjectile_SpellKartMirv : public CTFProjectile_SpellMirv
  2929. //{
  2930. //public:
  2931. // DECLARE_CLASS( CTFProjectile_SpellKartMirv, CTFProjectile_SpellMirv );
  2932. // DECLARE_NETWORKCLASS();
  2933. //
  2934. //#ifdef GAME_DLL
  2935. //
  2936. // virtual bool InitialExplodeEffects( CTFPlayer *pThrower, const trace_t *pTrace ) OVERRIDE
  2937. // {
  2938. // // Spawn a tonne of extra grenades (mirv style)
  2939. // CTFSpellBook *pSpellBook = dynamic_cast<CTFSpellBook*>( pThrower->GetEntityForLoadoutSlot( LOADOUT_POSITION_ACTION ) );
  2940. // if ( !pSpellBook )
  2941. // return false;
  2942. //
  2943. // // Create bomblets
  2944. // Vector offset = Vector( 0, -100, 400 );
  2945. //
  2946. // for ( int i = 0; i < 6; i++ )
  2947. // {
  2948. // AngularImpulse angVelocity = AngularImpulse( 0, 0, RandomFloat( 100, 300 ) );
  2949. //
  2950. // switch ( i )
  2951. // {
  2952. // case 0: offset = Vector( 75, 110, 400 ); break;
  2953. // case 1: offset = Vector( 75, -110, 400 ); break;
  2954. // case 2: offset = Vector( -75, 110, 400 ); break;
  2955. // case 3: offset = Vector( -75, -110, 400 ); break;
  2956. // case 4: offset = Vector( 135, 0, 400 ); break;
  2957. // case 5: offset = Vector( -135, 0, 400 ); break;
  2958. // }
  2959. //
  2960. // CTFProjectile_SpellPumpkin *pGrenade = static_cast<CTFProjectile_SpellPumpkin*>( CBaseEntity::CreateNoSpawn( "tf_projectile_spellkartpumpkin", GetAbsOrigin(), pThrower->EyeAngles(), pThrower ) );
  2961. // if ( pGrenade )
  2962. // {
  2963. // // Set the pipebomb mode before calling spawn, so the model & associated vphysics get setup properly.
  2964. // pGrenade->SetPipebombMode();
  2965. // DispatchSpawn( pGrenade );
  2966. // pGrenade->InitGrenade( offset, angVelocity, pThrower, pSpellBook->GetTFWpnData() );
  2967. // pGrenade->m_flFullDamage = 0;
  2968. // pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
  2969. // pGrenade->SetDetonateTimerLength( 2.0f + 0.05f * i );
  2970. // }
  2971. // }
  2972. //
  2973. // if ( TFGameRules() )
  2974. // {
  2975. // TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_PLAYER_SPELL_MIRV, ( pThrower->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED );
  2976. // }
  2977. //
  2978. // return true;
  2979. // }
  2980. //#endif
  2981. //};
  2982. //
  2983. //IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_SpellKartMirv, DT_TFProjectile_SpellKartMirv )
  2984. //BEGIN_NETWORK_TABLE( CTFProjectile_SpellKartMirv, DT_TFProjectile_SpellKartMirv )
  2985. //END_NETWORK_TABLE()
  2986. //
  2987. //LINK_ENTITY_TO_CLASS( tf_projectile_spellkartmirv, CTFProjectile_SpellKartMirv );
  2988. //PRECACHE_WEAPON_REGISTER( tf_projectile_spellkartmirv );