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.

416 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "cbase.h"
  8. #include "c_tf_player.h"
  9. #include "collisionutils.h"
  10. #include "econ_item_inventory.h"
  11. #include "iclientmode.h"
  12. #include "tf_gcmessages.h"
  13. #include "tf_gamerules.h"
  14. #include "econ_notifications.h"
  15. #include "rtime.h"
  16. #include "achievementmgr.h"
  17. #include "baseachievement.h"
  18. #include "achievements_tf.h"
  19. #include "gc_clientsystem.h"
  20. //-----------------------------------------------------------------------------
  21. // Purpose:
  22. //-----------------------------------------------------------------------------
  23. EHalloweenMap GetHalloweenMap()
  24. {
  25. if ( FStrEq( engine->GetLevelName(), "maps/cp_manor_event.bsp" ) )
  26. return kHalloweenMap_MannManor;
  27. if ( FStrEq( engine->GetLevelName(), "maps/koth_viaduct_event.bsp" ) )
  28. return kHalloweenMap_Viaduct;
  29. if ( FStrEq( engine->GetLevelName(), "maps/koth_lakeside_event.bsp" ) )
  30. return kHalloweenMap_Lakeside;
  31. if ( FStrEq( engine->GetLevelName(), "maps/plr_hightower_event.bsp" ) )
  32. return kHalloweenMap_Hightower;
  33. return kHalloweenMapCount;
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Created when the GC decides to give out an item.
  37. // A player must intersect the item to claim it.
  38. //-----------------------------------------------------------------------------
  39. #define HALLOWEEN_ITEM_TIME_TO_READY 10.0f
  40. class C_HalloweenItemPickup : public CBaseAnimating
  41. {
  42. DECLARE_CLASS( C_HalloweenItemPickup, CBaseAnimating );
  43. public:
  44. C_HalloweenItemPickup()
  45. : m_bReadyForPickup( false )
  46. , m_bClaimed( false )
  47. , m_flTimeToReady( 0.0f )
  48. {
  49. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  50. }
  51. virtual ~C_HalloweenItemPickup()
  52. {
  53. }
  54. bool Initialize()
  55. {
  56. const char *pszModelName = "models/props_halloween/halloween_gift.mdl";
  57. SetModelName( AllocPooledString( pszModelName ) );
  58. if ( InitializeAsClientEntity( STRING(GetModelName()), RENDER_GROUP_OPAQUE_ENTITY ) == false )
  59. return false;
  60. const model_t *mod = GetModel();
  61. if ( mod )
  62. {
  63. Vector mins, maxs;
  64. modelinfo->GetModelBounds( mod, mins, maxs );
  65. SetCollisionBounds( mins, maxs );
  66. }
  67. Spawn();
  68. // initialize as translucent
  69. float alpha = 0.0f;
  70. SetRenderMode( kRenderTransTexture );
  71. SetRenderColorA( alpha * 256 );
  72. m_flTimeToReady = gpGlobals->realtime + HALLOWEEN_ITEM_TIME_TO_READY;
  73. UpdatePartitionListEntry();
  74. SetBlocksLOS( false ); // this should be a small object
  75. // Set up shadows; do it here so that objects can change shadowcasting state
  76. CreateShadow();
  77. UpdateVisibility();
  78. SetNextClientThink( CLIENT_THINK_ALWAYS );
  79. return true;
  80. }
  81. virtual void Spawn()
  82. {
  83. Precache();
  84. BaseClass::Spawn();
  85. SetSolid( SOLID_NONE );
  86. AddSolidFlags( FSOLID_NOT_SOLID );
  87. SetMoveType( MOVETYPE_NONE );
  88. }
  89. virtual void ClientThink()
  90. {
  91. if ( m_bReadyForPickup )
  92. {
  93. ClientThink_Active();
  94. return;
  95. }
  96. float flTimeDelta = m_flTimeToReady - gpGlobals->realtime;
  97. if ( flTimeDelta < 0 )
  98. {
  99. m_bReadyForPickup = true;
  100. ParticleProp()->Create( "halloween_pickup_active", PATTACH_ABSORIGIN_FOLLOW );
  101. SetRenderMode( kRenderNormal );
  102. }
  103. else
  104. {
  105. float alpha = 0.75f * ( ( HALLOWEEN_ITEM_TIME_TO_READY - flTimeDelta ) / HALLOWEEN_ITEM_TIME_TO_READY );
  106. SetRenderColorA( alpha * 256 );
  107. }
  108. }
  109. void ClientThink_Active( void )
  110. {
  111. Vector vWorldMins = WorldAlignMins();
  112. Vector vWorldMaxs = WorldAlignMaxs();
  113. Vector vBoxMin1 = GetAbsOrigin() + vWorldMins;
  114. Vector vBoxMax1 = GetAbsOrigin() + vWorldMaxs;
  115. float flBestDistance2 = 0.0f;
  116. CSteamID bestSteamID;
  117. bool bBestHasNoclip = false;
  118. #define CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY 1
  119. #if !CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY
  120. for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
  121. {
  122. C_TFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayerIndex ) );
  123. #else
  124. do
  125. {
  126. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  127. #endif
  128. CSteamID steamID;
  129. if ( pPlayer == NULL || pPlayer->IsBot() == true || pPlayer->GetSteamID( &steamID ) == false ||
  130. ( pPlayer->GetTeamNumber() != TF_TEAM_RED && pPlayer->GetTeamNumber() != TF_TEAM_BLUE ) ||
  131. pPlayer->IsAlive() == false ||
  132. pPlayer->GetObserverMode() != OBS_MODE_NONE )
  133. {
  134. continue;
  135. }
  136. Vector vPlayerMins = pPlayer->GetAbsOrigin() + pPlayer->WorldAlignMins();
  137. Vector vPlayerMaxs = pPlayer->GetAbsOrigin() + pPlayer->WorldAlignMaxs();
  138. bool bIntersecting = IsBoxIntersectingBox( vBoxMin1, vBoxMax1, vPlayerMins, vPlayerMaxs );
  139. float flDistance2 = ( pPlayer->GetAbsOrigin(), GetAbsOrigin() ).LengthSqr();
  140. if ( bIntersecting && ( bestSteamID.GetAccountID() == 0 || flDistance2 < flBestDistance2 ) )
  141. {
  142. bestSteamID = steamID;
  143. bBestHasNoclip = pPlayer->GetMoveType() == MOVETYPE_NOCLIP;
  144. flBestDistance2 = flDistance2;
  145. }
  146. }
  147. #if CLIENT_HALLOWEEN_LOGIC_ENABLE_LOCAL_PLAYER_ONLY
  148. while ( false );
  149. #endif
  150. if ( bestSteamID.GetAccountID() != 0 )
  151. {
  152. GCSDK::CProtoBufMsg<CMsgGC_Halloween_GrantItem> msg( k_EMsgGC_Halloween_GrantItem );
  153. msg.Body().set_recipient_account_id( bestSteamID.GetAccountID() );
  154. msg.Body().set_level_id( GetHalloweenMap() );
  155. msg.Body().set_flagged( bBestHasNoclip );
  156. GCClientSystem()->BSendMessage( msg );
  157. OnClaimed( true );
  158. return;
  159. }
  160. SetNextClientThink( gpGlobals->curtime + 0.33f );
  161. }
  162. void OnClaimed( bool bPlayAudio )
  163. {
  164. if ( m_bClaimed )
  165. return;
  166. m_bClaimed = true;
  167. // stop thinking and remove sparkle...
  168. ParticleProp()->StopParticlesNamed( "halloween_pickup_active", true );
  169. SetNextClientThink( CLIENT_THINK_NEVER );
  170. // throw up bday confetti
  171. DispatchParticleEffect( "halloween_gift_pickup", GetAbsOrigin(), vec3_angle );
  172. if ( bPlayAudio )
  173. {
  174. C_BaseEntity::EmitSound( "Game.HappyBirthday" );
  175. }
  176. SetRenderMode( kRenderNone );
  177. UpdateVisibility();
  178. }
  179. bool m_bReadyForPickup;
  180. bool m_bClaimed;
  181. float m_flTimeToReady;
  182. };
  183. LINK_ENTITY_TO_CLASS( tf_halloween_item_pickup, C_HalloweenItemPickup );
  184. PRECACHE_REGISTER( tf_halloween_item_pickup );
  185. static EHANDLE gHalloweenPickup;
  186. #ifdef _DEBUG
  187. CON_COMMAND( cl_halloween_test_cheating, "Test cheating the halloween pickup" )
  188. {
  189. GCSDK::CProtoBufMsg< CMsgGC_Halloween_GrantItem > msg( k_EMsgGC_Halloween_GrantItem );
  190. msg.Body().set_recipient_account_id( steamapicontext->SteamUser()->GetSteamID().GetAccountID() );
  191. GCClientSystem()->BSendMessage( msg );
  192. }
  193. CON_COMMAND( cl_halloween_test_spawn_pickup, "Test spawning the pickup item" )
  194. {
  195. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  196. if ( pLocalPlayer == NULL )
  197. return;
  198. // Now create the pickup item
  199. C_HalloweenItemPickup *pEntity = new C_HalloweenItemPickup();
  200. if ( !pEntity )
  201. return;
  202. Vector vecTargetPoint;
  203. trace_t tr;
  204. Vector forward;
  205. pLocalPlayer->EyeVectors( &forward );
  206. UTIL_TraceLine( pLocalPlayer->EyePosition(),
  207. pLocalPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_NPCSOLID,
  208. pLocalPlayer, COLLISION_GROUP_NONE, &tr );
  209. if ( tr.fraction != 1.0 )
  210. {
  211. vecTargetPoint = tr.endpos;
  212. }
  213. pEntity->SetAbsOrigin( vecTargetPoint );
  214. if ( !pEntity->Initialize() )
  215. {
  216. pEntity->Release();
  217. }
  218. else
  219. {
  220. if ( gHalloweenPickup.Get() )
  221. gHalloweenPickup->Release();
  222. gHalloweenPickup = pEntity;
  223. }
  224. }
  225. #endif
  226. //-----------------------------------------------------------------------------
  227. // GC has decided to drop a Halloween item
  228. //-----------------------------------------------------------------------------
  229. //class CGCHalloween_ReservedItem : public GCSDK::CGCClientJob
  230. //{
  231. //public:
  232. // CGCHalloween_ReservedItem( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  233. //
  234. // virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  235. // {
  236. // GCSDK::CProtoBufMsg<CMsgGC_Halloween_ReservedItem> msg( pNetPacket );
  237. //
  238. // // Figure out which level we're on so we know how to handle notifications, etc.
  239. // EHalloweenMap eMap = GetHalloweenMap();
  240. // if ( eMap == kHalloweenMapCount )
  241. // return true;
  242. //
  243. // // Sanity-check the message contents from the GC.
  244. // if ( msg.Body().x_size() != msg.Body().y_size() || msg.Body().y_size() != msg.Body().z_size() )
  245. // return true;
  246. //
  247. // if ( msg.Body().x_size() <= eMap )
  248. // return true;
  249. //
  250. // // Don't spawn gifts during startup.
  251. // if ( TFGameRules() == NULL
  252. // || TFGameRules()->State_Get() != GR_STATE_RND_RUNNING
  253. // || TFGameRules()->InSetup()
  254. // || TFGameRules()->IsInWaitingForPlayers()
  255. // || TFGameRules()->ArePlayersInHell() ) // Dont spawn gifts if players are in 2013 Hell
  256. // return true;
  257. //
  258. // C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  259. // if ( pLocalPlayer == NULL )
  260. // return true;
  261. //
  262. // // If we don't already know about this gift pickup, create one.
  263. // if ( gHalloweenPickup.Get() == NULL )
  264. // {
  265. // // Now create the pickup item
  266. // C_HalloweenItemPickup *pEntity = new C_HalloweenItemPickup();
  267. // if ( !pEntity )
  268. // return true;
  269. //
  270. // Vector position( msg.Body().x( eMap ), msg.Body().y( eMap ), msg.Body().z( eMap ) );
  271. // pEntity->SetAbsOrigin( position );
  272. // if ( !pEntity->Initialize() )
  273. // {
  274. // pEntity->Release();
  275. // }
  276. // else
  277. // {
  278. // gHalloweenPickup = pEntity;
  279. // }
  280. // }
  281. //
  282. // // Regardless of whether we created a new gift or whether this was a new notification about an old gift,
  283. // // display a UI notification for the user.
  284. // CEconNotification *pNotification = new CEconNotification();
  285. // pNotification->SetText( "#TF_HalloweenItem_Reserved" );
  286. // pNotification->SetLifetime( 15.0f );
  287. // pNotification->SetSoundFilename( "ui/halloween_loot_spawn.wav" );
  288. // NotificationQueue_Add( pNotification );
  289. //
  290. // return true;
  291. // }
  292. //};
  293. //GC_REG_JOB( GCSDK::CGCClient, CGCHalloween_ReservedItem, "CGCHalloween_ReservedItem", k_EMsgGC_Halloween_ReservedItem, GCSDK::k_EServerTypeGCClient );
  294. //-----------------------------------------------------------------------------
  295. // GC gave out the Halloween item to a player
  296. //-----------------------------------------------------------------------------
  297. //class CGCHalloween_GrantedItemResponse : public GCSDK::CGCClientJob
  298. //{
  299. //public:
  300. // CGCHalloween_GrantedItemResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
  301. //
  302. // virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  303. // {
  304. // GCSDK::CProtoBufMsg<CMsgGC_Halloween_GrantItemResponse> msg( pNetPacket );
  305. //
  306. // // which Steam universe are we in?
  307. // EUniverse eUniverse = steamapicontext && steamapicontext->SteamUtils()
  308. // ? steamapicontext->SteamUtils()->GetConnectedUniverse()
  309. // : k_EUniverseInvalid;
  310. //
  311. // CSteamID steamIDRecipient( msg.Body().recipient_account_id(), eUniverse, k_EAccountTypeIndividual );
  312. // bool bIsValidRecipient = steamIDRecipient.IsValid();
  313. //
  314. // if ( gHalloweenPickup.Get() != NULL )
  315. // {
  316. // assert_cast<C_HalloweenItemPickup *>( gHalloweenPickup.Get() )->OnClaimed( bIsValidRecipient );
  317. // gHalloweenPickup->Release();
  318. // gHalloweenPickup = NULL;
  319. // }
  320. //
  321. // // don't do any work if we're not on a Halloween map
  322. // EHalloweenMap eMap = GetHalloweenMap();
  323. // if ( eMap == kHalloweenMapCount )
  324. // return true;
  325. //
  326. // // add alert
  327. // const char* pPlayerName = InventoryManager()->PersonaName_Get( steamIDRecipient.GetAccountID() );
  328. // wchar_t wszPlayerName[MAX_PLAYER_NAME_LENGTH] = L"";
  329. // if ( pPlayerName != NULL && FStrEq( pPlayerName, "" ) == false )
  330. // {
  331. // g_pVGuiLocalize->ConvertANSIToUnicode( pPlayerName, wszPlayerName, sizeof(wszPlayerName) );
  332. // }
  333. // CEconNotification *pNotification = new CEconNotification();
  334. // pNotification->SetLifetime( 15.0f );
  335. //
  336. // if ( bIsValidRecipient )
  337. // {
  338. // pNotification->SetText( "#TF_HalloweenItem_Granted" );
  339. // pNotification->AddStringToken( "recipient", wszPlayerName );
  340. // pNotification->SetSteamID( steamIDRecipient );
  341. // pNotification->SetSoundFilename( "ui/halloween_loot_found.wav" );
  342. // }
  343. // else
  344. // {
  345. // pNotification->SetText( "#TF_HalloweenItem_GrantPickupFail" );
  346. // // pNotification->SetSoundFilename( "coach/coach_student_died.wav" );
  347. // }
  348. //
  349. // NotificationQueue_Add( pNotification );
  350. //
  351. // // is this the local player? award the achievement...
  352. // if ( steamapicontext && steamapicontext->SteamUser() )
  353. // {
  354. // CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
  355. // if ( steamIDRecipient == localSteamID )
  356. // {
  357. // g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HALLOWEEN_COLLECT_GOODY_BAG );
  358. // }
  359. // }
  360. //
  361. // return true;
  362. // }
  363. //};
  364. //GC_REG_JOB( GCSDK::CGCClient, CGCHalloween_GrantedItemResponse, "CGCHalloween_GrantedItemResponse", k_EMsgGC_Halloween_GrantItemResponse, GCSDK::k_EServerTypeGCClient );
  365. //-----------------------------------------------------------------------------
  366. void CL_Halloween_LevelShutdown()
  367. {
  368. gHalloweenPickup = NULL;
  369. }