416 lines
14 KiB
C++
416 lines
14 KiB
C++
//========= 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)
|