Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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
14 KiB

5 years ago
//========= 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)