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.

415 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #include "cbase.h"
  3. #include "rtime.h"
  4. #include "econ_holidays.h"
  5. //-----------------------------------------------------------------------------
  6. // Purpose: Interface that answers the simple question "on the passed-in time,
  7. // would this holiday be active?". Any caching of calculations is left
  8. // up to subclasses.
  9. //-----------------------------------------------------------------------------
  10. class IIsHolidayActive
  11. {
  12. public:
  13. IIsHolidayActive( const char *pszHolidayName ) : m_pszHolidayName( pszHolidayName ) { }
  14. virtual ~IIsHolidayActive ( ) { }
  15. virtual bool IsActive( const CRTime& timeCurrent ) = 0;
  16. const char *GetHolidayName() const { return m_pszHolidayName; }
  17. private:
  18. const char *m_pszHolidayName;
  19. };
  20. //-----------------------------------------------------------------------------
  21. // Purpose: Always-disabled. Dummy event needed to map to slot zero for "disabled
  22. // holiday".
  23. //-----------------------------------------------------------------------------
  24. class CNoHoliday : public IIsHolidayActive
  25. {
  26. public:
  27. CNoHoliday() : IIsHolidayActive( "none" ) { }
  28. virtual bool IsActive( const CRTime& timeCurrent )
  29. {
  30. return false;
  31. }
  32. };
  33. //-----------------------------------------------------------------------------
  34. // Purpose: A holiday that lasts exactly one and only one day.
  35. //-----------------------------------------------------------------------------
  36. class CSingleDayHoliday : public IIsHolidayActive
  37. {
  38. public:
  39. CSingleDayHoliday( const char *pszName, int iMonth, int iDay )
  40. : IIsHolidayActive( pszName )
  41. , m_iMonth( iMonth )
  42. , m_iDay( iDay )
  43. {
  44. //
  45. }
  46. virtual bool IsActive( const CRTime& timeCurrent )
  47. {
  48. return m_iMonth == timeCurrent.GetMonth()
  49. && m_iDay == timeCurrent.GetDayOfMonth();
  50. }
  51. private:
  52. int m_iMonth;
  53. int m_iDay;
  54. };
  55. //-----------------------------------------------------------------------------
  56. // Purpose: We want "week long" holidays to encompass at least two weekends,
  57. // so that players get plenty of time interacting with the holiday
  58. // features.
  59. //-----------------------------------------------------------------------------
  60. class CWeeksBasedHoliday : public IIsHolidayActive
  61. {
  62. public:
  63. CWeeksBasedHoliday( const char *pszName, int iMonth, int iDay, int iExtraWeeks )
  64. : IIsHolidayActive( pszName )
  65. , m_iMonth( iMonth )
  66. , m_iDay( iDay )
  67. , m_iExtraWeeks( iExtraWeeks )
  68. , m_iCachedCalculatedYear( 0 )
  69. {
  70. // We'll calculate the interval the first time we call IsActive().
  71. }
  72. void RecalculateTimeActiveInterval( int iYear )
  73. {
  74. // Get the date of the holiday.
  75. tm holiday_tm = { };
  76. holiday_tm.tm_mday = m_iDay;
  77. holiday_tm.tm_mon = m_iMonth - 1;
  78. holiday_tm.tm_year = iYear - 1900; // convert to years since 1900
  79. mktime( &holiday_tm );
  80. // The event starts on the first Friday at least four days prior to the holiday.
  81. tm start_time_tm( holiday_tm );
  82. start_time_tm.tm_mday -= 4; // Move back four days.
  83. mktime( &start_time_tm );
  84. int days_offset = start_time_tm.tm_wday - kFriday; // Find the nearest prior Friday.
  85. if ( days_offset < 0 )
  86. days_offset += 7;
  87. start_time_tm.tm_mday -= days_offset;
  88. time_t start_time = mktime( &start_time_tm );
  89. // The event ends on the first Monday after the holiday, maybe plus some additional fudge
  90. // time.
  91. tm end_time_tm( holiday_tm );
  92. days_offset = 7 - (end_time_tm.tm_wday - kMonday);
  93. if ( days_offset >= 7 )
  94. days_offset -= 7;
  95. end_time_tm.tm_mday += days_offset + 7 * m_iExtraWeeks;
  96. time_t end_time = mktime( &end_time_tm );
  97. #ifdef GC_DLL
  98. char rgchDateStartBuf[ 128 ];
  99. BGetLocalFormattedDate( start_time, rgchDateStartBuf, sizeof( rgchDateStartBuf) );
  100. char rgchDateEndBuf[ 128 ];
  101. BGetLocalFormattedDate( end_time, rgchDateEndBuf, sizeof( rgchDateEndBuf ) );
  102. EmitInfo( GCSDK::SPEW_GC, 4, LOG_ALWAYS, "Holiday - '%s' event starts on '%s' and ends on '%s'.\n", GetHolidayName(), rgchDateStartBuf, rgchDateEndBuf );
  103. #endif // GC_DLL
  104. m_timeStart = start_time;
  105. m_timeEnd = end_time;
  106. // We're done and our interval data is cached.
  107. m_iCachedCalculatedYear = iYear;
  108. }
  109. virtual bool IsActive( const CRTime& timeCurrent )
  110. {
  111. const int iCurrentYear = timeCurrent.GetYear();
  112. if ( m_iCachedCalculatedYear != iCurrentYear )
  113. RecalculateTimeActiveInterval( iCurrentYear );
  114. return timeCurrent.GetRTime32() > m_timeStart
  115. && timeCurrent.GetRTime32() < m_timeEnd;
  116. }
  117. private:
  118. static const int kMonday = 1;
  119. static const int kFriday = 5;
  120. int m_iMonth;
  121. int m_iDay;
  122. int m_iExtraWeeks;
  123. // Filled out from RecalculateTimeActiveInterval().
  124. int m_iCachedCalculatedYear;
  125. RTime32 m_timeStart;
  126. RTime32 m_timeEnd;
  127. };
  128. //-----------------------------------------------------------------------------
  129. // Purpose: A holiday that repeats on a certain time interval, like "every N days"
  130. // or "once every two months" or, uh, "any time there's a full moon".
  131. //-----------------------------------------------------------------------------
  132. class CCyclicalHoliday : public IIsHolidayActive
  133. {
  134. public:
  135. CCyclicalHoliday( const char *pszName, int iMonth, int iDay, int iYear, float fCycleLengthInDays, float fBonusTimeInDays )
  136. : IIsHolidayActive( pszName )
  137. , m_fCycleLengthInDays( fCycleLengthInDays )
  138. , m_fBonusTimeInDays( fBonusTimeInDays )
  139. {
  140. // When is our initial interval?
  141. tm holiday_tm = { };
  142. holiday_tm.tm_mday = iDay;
  143. holiday_tm.tm_mon = iMonth - 1;
  144. holiday_tm.tm_year = iYear - 1900; // convert to years since 1900
  145. m_timeInitial = mktime( &holiday_tm );
  146. }
  147. virtual bool IsActive( const CRTime& timeCurrent )
  148. {
  149. // Days-to-seconds conversion.
  150. const int iSecondsPerDay = 24 * 60 * 60;
  151. // Convert our cycle/buffer times to seconds.
  152. const int iCycleLengthInSeconds = (int)(m_fCycleLengthInDays * iSecondsPerDay);
  153. const int iBufferTimeInSeconds = (int)(m_fBonusTimeInDays * iSecondsPerDay);
  154. // How long has it been since we started this cycle?
  155. int iSecondsIntoCycle = (timeCurrent.GetRTime32() - m_timeInitial) % iCycleLengthInSeconds;
  156. // If we're within the buffer period right after the start of a cycle, we're active.
  157. if ( iSecondsIntoCycle < iBufferTimeInSeconds )
  158. return true;
  159. // If we're within the buffer period towards the end of a cycle, we're active.
  160. if ( iSecondsIntoCycle > iCycleLengthInSeconds - iBufferTimeInSeconds )
  161. return true;
  162. // Alas, normal mode for us.
  163. return false;
  164. }
  165. private:
  166. time_t m_timeInitial ;
  167. float m_fCycleLengthInDays;
  168. float m_fBonusTimeInDays;
  169. };
  170. //-----------------------------------------------------------------------------
  171. // Purpose: A pseudo-holiday that is active when either of its child holidays
  172. // is active. Works through pointers but does not manage memory.
  173. //-----------------------------------------------------------------------------
  174. class COrHoliday : public IIsHolidayActive
  175. {
  176. public:
  177. COrHoliday( const char *pszName, IIsHolidayActive *pA, IIsHolidayActive *pB )
  178. : IIsHolidayActive( pszName )
  179. , m_pA( pA )
  180. , m_pB( pB )
  181. {
  182. Assert( pA );
  183. Assert( pB );
  184. Assert( pA != pB );
  185. }
  186. virtual bool IsActive( const CRTime& timeCurrent )
  187. {
  188. return m_pA->IsActive( timeCurrent )
  189. || m_pB->IsActive( timeCurrent );
  190. }
  191. private:
  192. IIsHolidayActive *m_pA;
  193. IIsHolidayActive *m_pB;
  194. };
  195. //-----------------------------------------------------------------------------
  196. // Purpose: Holiday that is defined by a start and end date
  197. //-----------------------------------------------------------------------------
  198. class CDateBasedHoliday : public IIsHolidayActive
  199. {
  200. public:
  201. CDateBasedHoliday( const char *pszName, const char *pszStartTime, const char *pszEndTime )
  202. : IIsHolidayActive( pszName )
  203. {
  204. m_rtStartTime = CRTime::RTime32FromString( pszStartTime );
  205. m_rtEndTime = CRTime::RTime32FromString( pszEndTime );
  206. }
  207. virtual bool IsActive( const CRTime& timeCurrent )
  208. {
  209. return ( ( timeCurrent >= m_rtStartTime ) && ( timeCurrent <= m_rtEndTime ) );
  210. }
  211. RTime32 GetEndRTime() const
  212. {
  213. return m_rtEndTime.GetRTime32();
  214. }
  215. private:
  216. CRTime m_rtStartTime;
  217. CRTime m_rtEndTime;
  218. };
  219. //-----------------------------------------------------------------------------
  220. // Purpose: Holiday that is defined by a start and end date with no year specified
  221. //-----------------------------------------------------------------------------
  222. class CDateBasedHolidayNoSpecificYear : public IIsHolidayActive
  223. {
  224. public:
  225. CDateBasedHolidayNoSpecificYear( const char *pszName, const char *pszStartTime, const char *pszEndTime )
  226. : IIsHolidayActive( pszName )
  227. , m_pszStartTime( pszStartTime )
  228. , m_pszEndTime( pszEndTime )
  229. , m_iCachedYear( -1 )
  230. {
  231. }
  232. virtual bool IsActive( const CRTime& timeCurrent )
  233. {
  234. const int iYear = timeCurrent.GetYear();
  235. if ( iYear != m_iCachedYear )
  236. {
  237. char m_szStartTime[k_RTimeRenderBufferSize];
  238. char m_szEndTime[k_RTimeRenderBufferSize];
  239. V_sprintf_safe( m_szStartTime, "%d-%s", iYear, m_pszStartTime );
  240. V_sprintf_safe( m_szEndTime, "%d-%s", iYear, m_pszEndTime );
  241. m_iCachedYear = iYear;
  242. m_rtCachedStartTime = CRTime::RTime32FromString( m_szStartTime );
  243. m_rtCachedEndTime = CRTime::RTime32FromString( m_szEndTime );
  244. }
  245. return ( ( timeCurrent >= m_rtCachedStartTime ) && ( timeCurrent <= m_rtCachedEndTime ) );
  246. }
  247. private:
  248. const char *m_pszStartTime;
  249. const char *m_pszEndTime;
  250. int m_iCachedYear;
  251. CRTime m_rtCachedStartTime;
  252. CRTime m_rtCachedEndTime;
  253. };
  254. //-----------------------------------------------------------------------------
  255. // Purpose: Actual holiday implementation objects.
  256. //-----------------------------------------------------------------------------
  257. static CNoHoliday g_Holiday_NoHoliday;
  258. static CDateBasedHolidayNoSpecificYear g_Holiday_TF2Birthday ( "birthday", "08-23", "08-25" );
  259. static CDateBasedHoliday g_Holiday_Halloween ( "halloween", "2016-10-19", "2016-11-18" );
  260. static CDateBasedHoliday g_Holiday_Christmas ( "christmas", "2016-11-28", "2017-01-12" );
  261. static CDateBasedHolidayNoSpecificYear g_Holiday_ValentinesDay ( "valentines", "02-13", "02-15" );
  262. static CDateBasedHoliday g_Holiday_MeetThePyro ( "meet_the_pyro", "2012-06-26", "2012-07-05" );
  263. /* starting date cycle length in days bonus time in days on both sides */
  264. static CCyclicalHoliday g_Holiday_FullMoon ( "fullmoon", 5, 21, 2016, 29.53f, 1.0f );
  265. // note: the cycle length is 29.5 instead of 29.53 so that the time calculations always start at noon based on the way CCyclicalHoliday works
  266. static COrHoliday g_Holiday_HalloweenOrFullMoon ( "halloween_or_fullmoon", &g_Holiday_Halloween, &g_Holiday_FullMoon );
  267. static COrHoliday g_Holiday_HalloweenOrFullMoonOrValentines ( "halloween_or_fullmoon_or_valentines", &g_Holiday_HalloweenOrFullMoon, &g_Holiday_ValentinesDay );
  268. static CDateBasedHolidayNoSpecificYear g_Holiday_AprilFools ( "april_fools", "03-31", "04-02" );
  269. static CDateBasedHoliday g_Holiday_EndOfTheLine ( "eotl_launch", "2014-12-03", "2015-01-05" );
  270. static CDateBasedHoliday g_Holiday_CommunityUpdate ( "community_update", "2015-09-01", "2015-11-05" );
  271. // ORDER NEEDS TO MATCH enum EHoliday
  272. static IIsHolidayActive *s_HolidayChecks[] =
  273. {
  274. &g_Holiday_NoHoliday, // kHoliday_None
  275. &g_Holiday_TF2Birthday, // kHoliday_TFBirthday
  276. &g_Holiday_Halloween, // kHoliday_Halloween
  277. &g_Holiday_Christmas, // kHoliday_Christmas
  278. &g_Holiday_CommunityUpdate, // kHoliday_CommunityUpdate
  279. &g_Holiday_EndOfTheLine, // kHoliday_EOTL
  280. &g_Holiday_ValentinesDay, // kHoliday_Valentines
  281. &g_Holiday_MeetThePyro, // kHoliday_MeetThePyro
  282. &g_Holiday_FullMoon, // kHoliday_FullMoon
  283. &g_Holiday_HalloweenOrFullMoon, // kHoliday_HalloweenOrFullMoon
  284. &g_Holiday_HalloweenOrFullMoonOrValentines, // kHoliday_HalloweenOrFullMoonOrValentines
  285. &g_Holiday_AprilFools, // kHoliday_AprilFools
  286. };
  287. COMPILE_TIME_ASSERT( ARRAYSIZE( s_HolidayChecks ) == kHolidayCount );
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. //-----------------------------------------------------------------------------
  291. bool EconHolidays_IsHolidayActive( int iHolidayIndex, const CRTime& timeCurrent )
  292. {
  293. if ( iHolidayIndex < 0 || iHolidayIndex >= kHolidayCount )
  294. return false;
  295. Assert( s_HolidayChecks[iHolidayIndex] );
  296. if ( !s_HolidayChecks[iHolidayIndex] )
  297. return false;
  298. return s_HolidayChecks[iHolidayIndex]->IsActive( timeCurrent );
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose:
  302. //-----------------------------------------------------------------------------
  303. int EconHolidays_GetHolidayForString( const char* pszHolidayName )
  304. {
  305. for ( int iHoliday = 0; iHoliday < kHolidayCount; ++iHoliday )
  306. {
  307. Assert( s_HolidayChecks[iHoliday] );
  308. if ( s_HolidayChecks[iHoliday] &&
  309. 0 == Q_stricmp( pszHolidayName, s_HolidayChecks[iHoliday]->GetHolidayName() ) )
  310. {
  311. return iHoliday;
  312. }
  313. }
  314. return kHoliday_None;
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose:
  318. //-----------------------------------------------------------------------------
  319. const char *EconHolidays_GetActiveHolidayString()
  320. {
  321. CRTime timeNow;
  322. timeNow.SetToCurrentTime();
  323. timeNow.SetToGMT( true );
  324. for ( int iHoliday = 0; iHoliday < kHolidayCount; iHoliday++ )
  325. {
  326. if ( EconHolidays_IsHolidayActive( iHoliday, timeNow ) )
  327. {
  328. Assert( s_HolidayChecks[iHoliday] );
  329. return s_HolidayChecks[iHoliday]->GetHolidayName();
  330. }
  331. }
  332. // No holidays currently active.
  333. return NULL;
  334. }
  335. #if defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)
  336. //-----------------------------------------------------------------------------
  337. // Purpose:
  338. //-----------------------------------------------------------------------------
  339. RTime32 EconHolidays_TerribleHack_GetHalloweenEndData()
  340. {
  341. return g_Holiday_Halloween.GetEndRTime();
  342. }
  343. #endif // defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL)