diff options
Diffstat (limited to 'game/shared/econ/econ_holidays.cpp')
| -rw-r--r-- | game/shared/econ/econ_holidays.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/game/shared/econ/econ_holidays.cpp b/game/shared/econ/econ_holidays.cpp new file mode 100644 index 0000000..afe1278 --- /dev/null +++ b/game/shared/econ/econ_holidays.cpp @@ -0,0 +1,415 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// + +#include "cbase.h" + +#include "rtime.h" +#include "econ_holidays.h" + +//----------------------------------------------------------------------------- +// Purpose: Interface that answers the simple question "on the passed-in time, +// would this holiday be active?". Any caching of calculations is left +// up to subclasses. +//----------------------------------------------------------------------------- +class IIsHolidayActive +{ +public: + IIsHolidayActive( const char *pszHolidayName ) : m_pszHolidayName( pszHolidayName ) { } + virtual ~IIsHolidayActive ( ) { } + virtual bool IsActive( const CRTime& timeCurrent ) = 0; + + const char *GetHolidayName() const { return m_pszHolidayName; } + +private: + const char *m_pszHolidayName; +}; + +//----------------------------------------------------------------------------- +// Purpose: Always-disabled. Dummy event needed to map to slot zero for "disabled +// holiday". +//----------------------------------------------------------------------------- +class CNoHoliday : public IIsHolidayActive +{ +public: + CNoHoliday() : IIsHolidayActive( "none" ) { } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + return false; + } +}; + +//----------------------------------------------------------------------------- +// Purpose: A holiday that lasts exactly one and only one day. +//----------------------------------------------------------------------------- +class CSingleDayHoliday : public IIsHolidayActive +{ +public: + CSingleDayHoliday( const char *pszName, int iMonth, int iDay ) + : IIsHolidayActive( pszName ) + , m_iMonth( iMonth ) + , m_iDay( iDay ) + { + // + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + return m_iMonth == timeCurrent.GetMonth() + && m_iDay == timeCurrent.GetDayOfMonth(); + } + +private: + int m_iMonth; + int m_iDay; +}; + +//----------------------------------------------------------------------------- +// Purpose: We want "week long" holidays to encompass at least two weekends, +// so that players get plenty of time interacting with the holiday +// features. +//----------------------------------------------------------------------------- +class CWeeksBasedHoliday : public IIsHolidayActive +{ +public: + CWeeksBasedHoliday( const char *pszName, int iMonth, int iDay, int iExtraWeeks ) + : IIsHolidayActive( pszName ) + , m_iMonth( iMonth ) + , m_iDay( iDay ) + , m_iExtraWeeks( iExtraWeeks ) + , m_iCachedCalculatedYear( 0 ) + { + // We'll calculate the interval the first time we call IsActive(). + } + + void RecalculateTimeActiveInterval( int iYear ) + { + // Get the date of the holiday. + tm holiday_tm = { }; + holiday_tm.tm_mday = m_iDay; + holiday_tm.tm_mon = m_iMonth - 1; + holiday_tm.tm_year = iYear - 1900; // convert to years since 1900 + mktime( &holiday_tm ); + + // The event starts on the first Friday at least four days prior to the holiday. + tm start_time_tm( holiday_tm ); + start_time_tm.tm_mday -= 4; // Move back four days. + mktime( &start_time_tm ); + int days_offset = start_time_tm.tm_wday - kFriday; // Find the nearest prior Friday. + if ( days_offset < 0 ) + days_offset += 7; + start_time_tm.tm_mday -= days_offset; + time_t start_time = mktime( &start_time_tm ); + + // The event ends on the first Monday after the holiday, maybe plus some additional fudge + // time. + tm end_time_tm( holiday_tm ); + days_offset = 7 - (end_time_tm.tm_wday - kMonday); + if ( days_offset >= 7 ) + days_offset -= 7; + end_time_tm.tm_mday += days_offset + 7 * m_iExtraWeeks; + time_t end_time = mktime( &end_time_tm ); + +#ifdef GC_DLL + char rgchDateStartBuf[ 128 ]; + BGetLocalFormattedDate( start_time, rgchDateStartBuf, sizeof( rgchDateStartBuf) ); + + char rgchDateEndBuf[ 128 ]; + BGetLocalFormattedDate( end_time, rgchDateEndBuf, sizeof( rgchDateEndBuf ) ); + + EmitInfo( GCSDK::SPEW_GC, 4, LOG_ALWAYS, "Holiday - '%s' event starts on '%s' and ends on '%s'.\n", GetHolidayName(), rgchDateStartBuf, rgchDateEndBuf ); +#endif // GC_DLL + + m_timeStart = start_time; + m_timeEnd = end_time; + + // We're done and our interval data is cached. + m_iCachedCalculatedYear = iYear; + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + const int iCurrentYear = timeCurrent.GetYear(); + if ( m_iCachedCalculatedYear != iCurrentYear ) + RecalculateTimeActiveInterval( iCurrentYear ); + + return timeCurrent.GetRTime32() > m_timeStart + && timeCurrent.GetRTime32() < m_timeEnd; + } + +private: + static const int kMonday = 1; + static const int kFriday = 5; + + int m_iMonth; + int m_iDay; + int m_iExtraWeeks; + + // Filled out from RecalculateTimeActiveInterval(). + int m_iCachedCalculatedYear; + + RTime32 m_timeStart; + RTime32 m_timeEnd; +}; + +//----------------------------------------------------------------------------- +// Purpose: A holiday that repeats on a certain time interval, like "every N days" +// or "once every two months" or, uh, "any time there's a full moon". +//----------------------------------------------------------------------------- +class CCyclicalHoliday : public IIsHolidayActive +{ +public: + CCyclicalHoliday( const char *pszName, int iMonth, int iDay, int iYear, float fCycleLengthInDays, float fBonusTimeInDays ) + : IIsHolidayActive( pszName ) + , m_fCycleLengthInDays( fCycleLengthInDays ) + , m_fBonusTimeInDays( fBonusTimeInDays ) + { + // When is our initial interval? + tm holiday_tm = { }; + holiday_tm.tm_mday = iDay; + holiday_tm.tm_mon = iMonth - 1; + holiday_tm.tm_year = iYear - 1900; // convert to years since 1900 + m_timeInitial = mktime( &holiday_tm ); + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + // Days-to-seconds conversion. + const int iSecondsPerDay = 24 * 60 * 60; + + // Convert our cycle/buffer times to seconds. + const int iCycleLengthInSeconds = (int)(m_fCycleLengthInDays * iSecondsPerDay); + const int iBufferTimeInSeconds = (int)(m_fBonusTimeInDays * iSecondsPerDay); + + // How long has it been since we started this cycle? + int iSecondsIntoCycle = (timeCurrent.GetRTime32() - m_timeInitial) % iCycleLengthInSeconds; + + // If we're within the buffer period right after the start of a cycle, we're active. + if ( iSecondsIntoCycle < iBufferTimeInSeconds ) + return true; + + // If we're within the buffer period towards the end of a cycle, we're active. + if ( iSecondsIntoCycle > iCycleLengthInSeconds - iBufferTimeInSeconds ) + return true; + + // Alas, normal mode for us. + return false; + } + +private: + time_t m_timeInitial ; + + float m_fCycleLengthInDays; + float m_fBonusTimeInDays; +}; + +//----------------------------------------------------------------------------- +// Purpose: A pseudo-holiday that is active when either of its child holidays +// is active. Works through pointers but does not manage memory. +//----------------------------------------------------------------------------- +class COrHoliday : public IIsHolidayActive +{ +public: + COrHoliday( const char *pszName, IIsHolidayActive *pA, IIsHolidayActive *pB ) + : IIsHolidayActive( pszName ) + , m_pA( pA ) + , m_pB( pB ) + { + Assert( pA ); + Assert( pB ); + Assert( pA != pB ); + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + return m_pA->IsActive( timeCurrent ) + || m_pB->IsActive( timeCurrent ); + } + +private: + IIsHolidayActive *m_pA; + IIsHolidayActive *m_pB; +}; + +//----------------------------------------------------------------------------- +// Purpose: Holiday that is defined by a start and end date +//----------------------------------------------------------------------------- +class CDateBasedHoliday : public IIsHolidayActive +{ +public: + CDateBasedHoliday( const char *pszName, const char *pszStartTime, const char *pszEndTime ) + : IIsHolidayActive( pszName ) + { + m_rtStartTime = CRTime::RTime32FromString( pszStartTime ); + m_rtEndTime = CRTime::RTime32FromString( pszEndTime ); + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + return ( ( timeCurrent >= m_rtStartTime ) && ( timeCurrent <= m_rtEndTime ) ); + } + + RTime32 GetEndRTime() const + { + return m_rtEndTime.GetRTime32(); + } + + +private: + CRTime m_rtStartTime; + CRTime m_rtEndTime; +}; + +//----------------------------------------------------------------------------- +// Purpose: Holiday that is defined by a start and end date with no year specified +//----------------------------------------------------------------------------- +class CDateBasedHolidayNoSpecificYear : public IIsHolidayActive +{ +public: + CDateBasedHolidayNoSpecificYear( const char *pszName, const char *pszStartTime, const char *pszEndTime ) + : IIsHolidayActive( pszName ) + , m_pszStartTime( pszStartTime ) + , m_pszEndTime( pszEndTime ) + , m_iCachedYear( -1 ) + { + } + + virtual bool IsActive( const CRTime& timeCurrent ) + { + const int iYear = timeCurrent.GetYear(); + + if ( iYear != m_iCachedYear ) + { + char m_szStartTime[k_RTimeRenderBufferSize]; + char m_szEndTime[k_RTimeRenderBufferSize]; + + V_sprintf_safe( m_szStartTime, "%d-%s", iYear, m_pszStartTime ); + V_sprintf_safe( m_szEndTime, "%d-%s", iYear, m_pszEndTime ); + + m_iCachedYear = iYear; + m_rtCachedStartTime = CRTime::RTime32FromString( m_szStartTime ); + m_rtCachedEndTime = CRTime::RTime32FromString( m_szEndTime ); + } + + return ( ( timeCurrent >= m_rtCachedStartTime ) && ( timeCurrent <= m_rtCachedEndTime ) ); + } + +private: + const char *m_pszStartTime; + const char *m_pszEndTime; + + int m_iCachedYear; + CRTime m_rtCachedStartTime; + CRTime m_rtCachedEndTime; +}; + +//----------------------------------------------------------------------------- +// Purpose: Actual holiday implementation objects. +//----------------------------------------------------------------------------- + +static CNoHoliday g_Holiday_NoHoliday; + +static CDateBasedHolidayNoSpecificYear g_Holiday_TF2Birthday ( "birthday", "08-23", "08-25" ); + +static CDateBasedHoliday g_Holiday_Halloween ( "halloween", "2016-10-19", "2016-11-18" ); + +static CDateBasedHoliday g_Holiday_Christmas ( "christmas", "2016-11-28", "2017-01-12" ); + +static CDateBasedHolidayNoSpecificYear g_Holiday_ValentinesDay ( "valentines", "02-13", "02-15" ); + +static CDateBasedHoliday g_Holiday_MeetThePyro ( "meet_the_pyro", "2012-06-26", "2012-07-05" ); + /* starting date cycle length in days bonus time in days on both sides */ +static CCyclicalHoliday g_Holiday_FullMoon ( "fullmoon", 5, 21, 2016, 29.53f, 1.0f ); + // 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 +static COrHoliday g_Holiday_HalloweenOrFullMoon ( "halloween_or_fullmoon", &g_Holiday_Halloween, &g_Holiday_FullMoon ); + +static COrHoliday g_Holiday_HalloweenOrFullMoonOrValentines ( "halloween_or_fullmoon_or_valentines", &g_Holiday_HalloweenOrFullMoon, &g_Holiday_ValentinesDay ); + +static CDateBasedHolidayNoSpecificYear g_Holiday_AprilFools ( "april_fools", "03-31", "04-02" ); + +static CDateBasedHoliday g_Holiday_EndOfTheLine ( "eotl_launch", "2014-12-03", "2015-01-05" ); + +static CDateBasedHoliday g_Holiday_CommunityUpdate ( "community_update", "2015-09-01", "2015-11-05" ); + +// ORDER NEEDS TO MATCH enum EHoliday +static IIsHolidayActive *s_HolidayChecks[] = +{ + &g_Holiday_NoHoliday, // kHoliday_None + &g_Holiday_TF2Birthday, // kHoliday_TFBirthday + &g_Holiday_Halloween, // kHoliday_Halloween + &g_Holiday_Christmas, // kHoliday_Christmas + &g_Holiday_CommunityUpdate, // kHoliday_CommunityUpdate + &g_Holiday_EndOfTheLine, // kHoliday_EOTL + &g_Holiday_ValentinesDay, // kHoliday_Valentines + &g_Holiday_MeetThePyro, // kHoliday_MeetThePyro + &g_Holiday_FullMoon, // kHoliday_FullMoon + &g_Holiday_HalloweenOrFullMoon, // kHoliday_HalloweenOrFullMoon + &g_Holiday_HalloweenOrFullMoonOrValentines, // kHoliday_HalloweenOrFullMoonOrValentines + &g_Holiday_AprilFools, // kHoliday_AprilFools +}; + +COMPILE_TIME_ASSERT( ARRAYSIZE( s_HolidayChecks ) == kHolidayCount ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool EconHolidays_IsHolidayActive( int iHolidayIndex, const CRTime& timeCurrent ) +{ + if ( iHolidayIndex < 0 || iHolidayIndex >= kHolidayCount ) + return false; + + Assert( s_HolidayChecks[iHolidayIndex] ); + if ( !s_HolidayChecks[iHolidayIndex] ) + return false; + + return s_HolidayChecks[iHolidayIndex]->IsActive( timeCurrent ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int EconHolidays_GetHolidayForString( const char* pszHolidayName ) +{ + for ( int iHoliday = 0; iHoliday < kHolidayCount; ++iHoliday ) + { + Assert( s_HolidayChecks[iHoliday] ); + if ( s_HolidayChecks[iHoliday] && + 0 == Q_stricmp( pszHolidayName, s_HolidayChecks[iHoliday]->GetHolidayName() ) ) + { + return iHoliday; + } + } + + return kHoliday_None; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *EconHolidays_GetActiveHolidayString() +{ + CRTime timeNow; + timeNow.SetToCurrentTime(); + timeNow.SetToGMT( true ); + + for ( int iHoliday = 0; iHoliday < kHolidayCount; iHoliday++ ) + { + if ( EconHolidays_IsHolidayActive( iHoliday, timeNow ) ) + { + Assert( s_HolidayChecks[iHoliday] ); + return s_HolidayChecks[iHoliday]->GetHolidayName(); + } + } + + // No holidays currently active. + return NULL; +} + +#if defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +RTime32 EconHolidays_TerribleHack_GetHalloweenEndData() +{ + return g_Holiday_Halloween.GetEndRTime(); +} +#endif // defined(TF_CLIENT_DLL) || defined(TF_DLL) || defined(TF_GC_DLL) |