mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-12 03:39:10 +00:00
1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
bool CheckWinNoEnemyCaps( IGameEvent *event, int iRole );
|
||||
bool IsLocalTFPlayerClass( int iClass );
|
||||
bool GameRulesAllowsAchievements( void );
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// All achievements should derive from this. It takes care of ensuring that MVM mode isn't active for
|
||||
// non MVM achievements and that MVM is active for MVM achievements
|
||||
class CBaseTFAchievementSimple : public CBaseAchievement
|
||||
{
|
||||
DECLARE_CLASS( CBaseTFAchievementSimple, CBaseAchievement );
|
||||
public:
|
||||
virtual bool LocalPlayerCanEarn( void );
|
||||
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// All class specific achievements should derive from this. It takes care of ensuring that the class
|
||||
// check is performed, and saves needless event handling for other class's achievements.
|
||||
class CBaseTFAchievement : public CBaseTFAchievementSimple
|
||||
{
|
||||
DECLARE_CLASS( CBaseTFAchievement, CBaseTFAchievementSimple );
|
||||
public:
|
||||
virtual bool LocalPlayerCanEarn( void );
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Helper class for achievements that check that the player was playing on a game team for the full round
|
||||
class CTFAchievementFullRound : public CBaseTFAchievement
|
||||
{
|
||||
DECLARE_CLASS( CTFAchievementFullRound, CBaseTFAchievement );
|
||||
public:
|
||||
void Init() ;
|
||||
virtual void ListenForEvents();
|
||||
void FireGameEvent_Internal( IGameEvent *event );
|
||||
bool PlayerWasInEntireRound( float flRoundTime );
|
||||
|
||||
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event ) = 0 ;
|
||||
};
|
||||
|
||||
class CAchievementTopScoreboard : public CTFAchievementFullRound
|
||||
{
|
||||
DECLARE_CLASS( CAchievementTopScoreboard, CTFAchievementFullRound );
|
||||
|
||||
public:
|
||||
void Init();
|
||||
virtual void ListenForEvents();
|
||||
virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event );
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
// Helper class for achievements that involve killing players after walking through a teleporter
|
||||
template < class tBaseClass >
|
||||
class CTFAchievementTeleporterTimingKills : public tBaseClass
|
||||
{
|
||||
DECLARE_CLASS( CTFAchievementTeleporterTimingKills, CBaseTFAchievement );
|
||||
|
||||
void Init()
|
||||
{
|
||||
this->SetFlags( ACH_SAVE_GLOBAL | ACH_LISTEN_KILL_ENEMY_EVENTS );
|
||||
this->SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
if ( !pVictim || !pVictim->IsPlayer() )
|
||||
return;
|
||||
|
||||
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
|
||||
if ( pAttacker == pLocalPlayer && pVictim != pLocalPlayer )
|
||||
{
|
||||
C_TFPlayer *pTFAttacker = ToTFPlayer( pAttacker );
|
||||
if ( pTFAttacker && pTFAttacker->m_Shared.InCond( TF_COND_TELEPORTED ) && ( gpGlobals->curtime - pTFAttacker->m_Shared.GetTimeTeleEffectAdded() <= 5.0f ) )
|
||||
{
|
||||
this->IncrementCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern CAchievementMgr g_AchievementMgrTF; // global achievement mgr for TF
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,593 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#include "achievementmgr.h"
|
||||
#include "baseachievement.h"
|
||||
#include "tf_hud_statpanel.h"
|
||||
#include "c_tf_team.h"
|
||||
#include "c_tf_player.h"
|
||||
#include "c_tf_playerresource.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "econ_wearable.h"
|
||||
#include "achievements_tf.h"
|
||||
|
||||
// NVNT include for tf2 damage
|
||||
#include "haptics/haptic_utils.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Halloween Achievements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CAchievementTFHalloweenCollectPumpkins : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 20 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
virtual void ListenForEvents()
|
||||
{
|
||||
ListenForGameEvent( "halloween_pumpkin_grab" );
|
||||
}
|
||||
|
||||
void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( !TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
|
||||
return;
|
||||
|
||||
if ( Q_strcmp( event->GetName(), "halloween_pumpkin_grab" ) == 0 )
|
||||
{
|
||||
int iPlayer = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
|
||||
CBaseEntity *pPlayer = UTIL_PlayerByIndex( iPlayer );
|
||||
|
||||
if ( pPlayer && pPlayer == C_TFPlayer::GetLocalTFPlayer() )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenCollectPumpkins, ACHIEVEMENT_TF_HALLOWEEN_COLLECT_PUMPKINS, "TF_HALLOWEEN_COLLECT_PUMPKINS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDominateForHat : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
|
||||
bool bDomination = event->GetInt( "death_flags" ) & TF_DEATH_DOMINATION;
|
||||
|
||||
if ( pTFVictim && pAttacker == C_TFPlayer::GetLocalTFPlayer() && bDomination == true )
|
||||
{
|
||||
// Are they wearing the HAT?
|
||||
for ( int i=0; i<pTFVictim->GetNumWearables(); ++i )
|
||||
{
|
||||
C_EconWearable *pWearable = pTFVictim->GetWearable( i );
|
||||
if ( pWearable && pWearable->GetAttributeContainer() )
|
||||
{
|
||||
CEconItemView *pItem = pWearable->GetAttributeContainer()->GetItem();
|
||||
if ( pItem && pItem->IsValid() )
|
||||
{
|
||||
if ( ( pItem->GetItemDefIndex() == 116 ) || // Ghastly Gibus
|
||||
( pItem->GetItemDefIndex() == 279 ) || // Ghastly Gibus 2010
|
||||
( pItem->GetItemDefIndex() == 584 ) || // Ghastly Gibus 2011
|
||||
( pItem->GetItemDefIndex() == 940 ) ) // Ghostly Gibus
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDominateForHat, ACHIEVEMENT_TF_HALLOWEEN_DOMINATE_FOR_HAT, "TF_HALLOWEEN_DOMINATE_FOR_HAT", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenKillScaredPlayer : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
|
||||
if ( !pTFVictim )
|
||||
return;
|
||||
|
||||
CTFPlayer *pLocalPlayer = ToTFPlayer( C_TFPlayer::GetLocalPlayer() );
|
||||
if ( !pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( !TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
|
||||
return;
|
||||
|
||||
if ( pVictim == pLocalPlayer )
|
||||
return;
|
||||
|
||||
int iStunFlags = event->GetInt( "stun_flags" );
|
||||
bool bStunByTrigger = iStunFlags & TF_STUN_BY_TRIGGER;
|
||||
if ( bStunByTrigger )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenKillScaredPlayer, ACHIEVEMENT_TF_HALLOWEEN_KILL_SCARED_PLAYER, "TF_HALLOWEEN_KILL_SCARED_PLAYER", 1 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenPumpkinKill : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
|
||||
SetGoal( 5 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
|
||||
if ( !pTFVictim )
|
||||
return;
|
||||
|
||||
CTFPlayer *pLocalPlayer = ToTFPlayer( C_TFPlayer::GetLocalPlayer() );
|
||||
if ( !pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( pLocalPlayer != pAttacker )
|
||||
return;
|
||||
|
||||
if ( !TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
|
||||
return;
|
||||
|
||||
if ( pVictim == pLocalPlayer )
|
||||
return;
|
||||
|
||||
int customdmg = event->GetInt( "customkill" );
|
||||
if ( customdmg == TF_DMG_CUSTOM_PUMPKIN_BOMB )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenPumpkinKill, ACHIEVEMENT_TF_HALLOWEEN_PUMPKIN_KILL, "TF_HALLOWEEN_PUMPKIN_KILL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDisguisedSpyKill : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
|
||||
if ( !pTFVictim )
|
||||
return;
|
||||
|
||||
CTFPlayer *pTFAttacker = ToTFPlayer( pAttacker );
|
||||
if ( !pTFAttacker )
|
||||
return;
|
||||
|
||||
CTFPlayer *pLocalPlayer = ToTFPlayer( C_TFPlayer::GetLocalPlayer() );
|
||||
if ( !pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( !TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
|
||||
return;
|
||||
|
||||
if ( pVictim == pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( pTFVictim->m_Shared.GetDisguiseClass() == pTFAttacker->GetPlayerClass()->GetClassIndex() )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDisguisedSpyKill, ACHIEVEMENT_TF_HALLOWEEN_DISGUISED_SPY_KILL, "TF_HALLOWEEN_DISGUISED_SPY_KILL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenBossKill : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenBossKill, ACHIEVEMENT_TF_HALLOWEEN_BOSS_KILL, "TF_HALLOWEEN_BOSS_KILL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenBossKillMelee : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenBossKillMelee, ACHIEVEMENT_TF_HALLOWEEN_BOSS_KILL_MELEE, "TF_HALLOWEEN_BOSS_KILL_MELEE", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenCollectGoodyBag : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// client awards this achievement in CGCHalloween_GrantedItemResponse, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenCollectGoodyBag, ACHIEVEMENT_TF_HALLOWEEN_COLLECT_GOODY_BAG, "TF_HALLOWEEN_COLLECT_GOODY_BAG", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenCraftSaxtonMask : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// client awards this achievement in CTFPlayerInventory::SOCreated(), no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenCraftSaxtonMask, ACHIEVEMENT_TF_HALLOWEEN_CRAFT_SAXTON_MASK, "TF_HALLOWEEN_CRAFT_SAXTON_MASK", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenEyeBossKill : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenEyeBossKill, ACHIEVEMENT_TF_HALLOWEEN_EYEBOSS_KILL, "TF_HALLOWEEN_EYEBOSS_KILL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenLootIsland : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenLootIsland, ACHIEVEMENT_TF_HALLOWEEN_LOOT_ISLAND, "TF_HALLOWEEN_LOOT_ISLAND", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenMerasmusKill : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenMerasmusKill, ACHIEVEMENT_TF_HALLOWEEN_MERASMUS_KILL, "TF_HALLOWEEN_MERASMUS_KILL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenMerasmusCollectLoot : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenMerasmusCollectLoot, ACHIEVEMENT_TF_HALLOWEEN_MERASMUS_COLLECT_LOOT, "TF_HALLOWEEN_MERASMUS_COLLECT_LOOT", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerRareSpell : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerRareSpell, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_RARE_SPELL, "TF_HALLOWEEN_HELLTOWER_RARE_SPELL", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerWinRounds : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 142 );
|
||||
SetStoreProgressInSteam( true );
|
||||
SetMapNameFilter( "plr_hightower_event" );
|
||||
}
|
||||
|
||||
virtual void ListenForEvents()
|
||||
{
|
||||
ListenForGameEvent( "teamplay_round_win" );
|
||||
}
|
||||
|
||||
void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
|
||||
{
|
||||
// Were we on the winning team?
|
||||
int iTeam = event->GetInt( "team" );
|
||||
if ( ( iTeam >= FIRST_GAME_TEAM ) && ( iTeam == GetLocalPlayerTeam() ) )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerWinRounds, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_WIN_ROUNDS, "TF_HALLOWEEN_HELLTOWER_WIN_ROUNDS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerEnvironmentalKills : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 17 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerEnvironmentalKills, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_ENVIRONMENTAL_KILLS, "TF_HALLOWEEN_HELLTOWER_ENVIRONMENTAL_KILLS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerSkeletonGrind : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 99 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerSkeletonGrind, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_SKELETON_GRIND, "TF_HALLOWEEN_HELLTOWER_SKELETON_GRIND", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerKillGrind : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_LISTEN_PLAYER_KILL_ENEMY_EVENTS | ACH_SAVE_GLOBAL );
|
||||
SetGoal( 25 );
|
||||
SetStoreProgressInSteam( true );
|
||||
SetMapNameFilter( "plr_hightower_event" );
|
||||
}
|
||||
|
||||
virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event )
|
||||
{
|
||||
if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_HIGHTOWER ) )
|
||||
{
|
||||
CTFPlayer *pTFVictim = ToTFPlayer( pVictim );
|
||||
if ( !pTFVictim )
|
||||
return;
|
||||
|
||||
CTFPlayer *pLocalPlayer = ToTFPlayer( C_TFPlayer::GetLocalPlayer() );
|
||||
if ( !pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( pLocalPlayer != pAttacker )
|
||||
return;
|
||||
|
||||
if ( pLocalPlayer == pVictim )
|
||||
return;
|
||||
|
||||
switch( event->GetInt( "customkill" ) )
|
||||
{
|
||||
case TF_DMG_CUSTOM_SPELL_TELEPORT:
|
||||
case TF_DMG_CUSTOM_SPELL_SKELETON:
|
||||
case TF_DMG_CUSTOM_SPELL_MIRV:
|
||||
case TF_DMG_CUSTOM_SPELL_METEOR:
|
||||
case TF_DMG_CUSTOM_SPELL_LIGHTNING:
|
||||
case TF_DMG_CUSTOM_SPELL_FIREBALL:
|
||||
case TF_DMG_CUSTOM_SPELL_MONOCULUS:
|
||||
case TF_DMG_CUSTOM_SPELL_BLASTJUMP:
|
||||
case TF_DMG_CUSTOM_SPELL_BATS:
|
||||
case TF_DMG_CUSTOM_SPELL_TINY:
|
||||
IncrementCount();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerKillGrind, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_KILL_GRIND, "TF_HALLOWEEN_HELLTOWER_KILL_GRIND", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerKillBrothers : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 10 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerKillBrothers, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_KILL_BROTHERS, "TF_HALLOWEEN_HELLTOWER_KILL_BROTHERS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerMilestone : public CAchievement_AchievedCount
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CAchievementTFHalloweenHelltowerMilestone, CAchievement_AchievedCount );
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
SetAchievementsRequired( 4, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_RARE_SPELL, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_KILL_BROTHERS );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerMilestone, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_MILESTONE, "TF_HALLOWEEN_HELLTOWER_MILESTONE", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenHelltowerSkullIslandReward : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenHelltowerSkullIslandReward, ACHIEVEMENT_TF_HALLOWEEN_HELLTOWER_SKULL_ISLAND_REWARD, "TF_HALLOWEEN_HELLTOWER_SKULL_ISLAND_REWARD", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayKillKarts : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 30 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayKillKarts, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_KILL_KARTS, "TF_HALLOWEEN_DOOMSDAY_KILL_KARTS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayCollectDucks : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 250 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayCollectDucks, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_COLLECT_DUCKS, "TF_HALLOWEEN_DOOMSDAY_COLLECT_DUCKS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayScoreGoals : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 3 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayScoreGoals, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_SCORE_GOALS, "TF_HALLOWEEN_DOOMSDAY_SCORE_GOALS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayRespawnTeammates : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 30 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayRespawnTeammates, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_RESPAWN_TEAMMATES, "TF_HALLOWEEN_DOOMSDAY_RESPAWN_TEAMMATES", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayTinySmasher : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 15 );
|
||||
SetStoreProgressInSteam( true );
|
||||
}
|
||||
|
||||
// server awards this achievement, no other code within achievement necessary
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayTinySmasher, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_TINY_SMASHER, "TF_HALLOWEEN_DOOMSDAY_TINY_SMASHER", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayWinMinigames : public CBaseTFAchievementSimple
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL | ACH_HAS_COMPONENTS );
|
||||
SetGoal( 3 );
|
||||
SetMapNameFilter( "sd_doomsday_event" );
|
||||
}
|
||||
|
||||
|
||||
virtual void ListenForEvents()
|
||||
{
|
||||
ListenForGameEvent( "minigame_win" );
|
||||
}
|
||||
|
||||
void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "minigame_win" ) )
|
||||
{
|
||||
// Are we on the winning team?
|
||||
int iTeam = event->GetInt( "team" );
|
||||
if ( ( iTeam >= FIRST_GAME_TEAM ) && ( iTeam == GetLocalPlayerTeam() ) )
|
||||
{
|
||||
EnsureComponentBitSetAndEvaluate( event->GetInt( "type" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayWinMinigames, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_WIN_MINIGAMES, "TF_HALLOWEEN_DOOMSDAY_WIN_MINIGAMES", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFHalloweenDoomsdayMilestone : public CAchievement_AchievedCount
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CAchievementTFHalloweenDoomsdayMilestone, CAchievement_AchievedCount );
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
SetAchievementsRequired( 4, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_KILL_KARTS, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_WIN_MINIGAMES );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFHalloweenDoomsdayMilestone, ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_MILESTONE, "TF_HALLOWEEN_DOOMSDAY_MILESTONE", 5 );
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#include "achievementmgr.h"
|
||||
#include "baseachievement.h"
|
||||
#include "c_tf_player.h"
|
||||
#include "c_tf_playerresource.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "achievements_tf.h"
|
||||
|
||||
//======================================================================================================================================
|
||||
// REPLAY ACHIEVEMENTS
|
||||
//======================================================================================================================================
|
||||
|
||||
class CReplayAchievement : public CBaseTFAchievement
|
||||
{
|
||||
public:
|
||||
virtual bool AlwaysListen() { return true; }
|
||||
virtual bool LocalPlayerCanEarn() { return true; }
|
||||
virtual bool AlwaysEnabled() { return true; }
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFReplay_SaveReplay : public CReplayAchievement
|
||||
{
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
ListenForGameEvent( "replay_saved" );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "replay_saved" ) )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_SaveReplay, ACHIEVEMENT_TF_REPLAY_SAVE_REPLAY, "TF_REPLAY_SAVE_REPLAY", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFReplay_PerformanceMode : public CReplayAchievement
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
ListenForGameEvent( "entered_performance_mode" );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "entered_performance_mode" ) )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_PerformanceMode, ACHIEVEMENT_TF_REPLAY_PERFORMANCE_MODE, "TF_REPLAY_PERFORMANCE_MODE", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFReplay_BrowseReplays : public CReplayAchievement
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
ListenForGameEvent( "browse_replays" );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
|
||||
virtual void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "browse_replays" ) )
|
||||
{
|
||||
IncrementCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_BrowseReplays, ACHIEVEMENT_TF_REPLAY_BROWSE_REPLAYS, "TF_REPLAY_BROWSE_REPLAYS", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
class CAchievementTFReplay_EditTime : public CReplayAchievement
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
SetGoal( 1 );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_EditTime, ACHIEVEMENT_TF_REPLAY_EDIT_TIME, "TF_REPLAY_EDIT_TIME", 5 );
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class CAchievementTFReplay_YouTube_Views_Tier : public CReplayAchievement
|
||||
{
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
SetFlags( ACH_SAVE_GLOBAL );
|
||||
ListenForGameEvent( "replay_youtube_stats" );
|
||||
SetStoreProgressInSteam( true );
|
||||
SetStat( "TF_REPLAY_YOUTUBE_VIEWS" );
|
||||
}
|
||||
|
||||
virtual void FireGameEvent_Internal( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "replay_youtube_stats" ) )
|
||||
{
|
||||
int iCurrentCount = GetCount();
|
||||
int iNewCount = event->GetInt( "views" );
|
||||
if ( iNewCount > iCurrentCount )
|
||||
{
|
||||
IncrementCount( iNewCount - iCurrentCount );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool ShouldShowProgressNotification() { return false; }
|
||||
};
|
||||
|
||||
class CAchievementTFReplay_YouTube_Views_Tier1 : public CAchievementTFReplay_YouTube_Views_Tier
|
||||
{
|
||||
DECLARE_CLASS( CAchievementTFReplay_YouTube_Views_Tier1, CAchievementTFReplay_YouTube_Views_Tier );
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
|
||||
SetGoal( 100 );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_YouTube_Views_Tier1, ACHIEVEMENT_TF_REPLAY_YOUTUBE_VIEWS_TIER1, "TF_REPLAY_YOUTUBE_VIEWS_TIER1", 5 );
|
||||
|
||||
class CAchievementTFReplay_YouTube_Views_Tier2 : public CAchievementTFReplay_YouTube_Views_Tier
|
||||
{
|
||||
DECLARE_CLASS( CAchievementTFReplay_YouTube_Views_Tier1, CAchievementTFReplay_YouTube_Views_Tier );
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
SetGoal( 1000 );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_YouTube_Views_Tier2, ACHIEVEMENT_TF_REPLAY_YOUTUBE_VIEWS_TIER2, "TF_REPLAY_YOUTUBE_VIEWS_TIER2", 5 );
|
||||
|
||||
class CAchievementTFReplay_YouTube_Views_Tier3 : public CAchievementTFReplay_YouTube_Views_Tier
|
||||
{
|
||||
DECLARE_CLASS( CAchievementTFReplay_YouTube_Views_Tier1, CAchievementTFReplay_YouTube_Views_Tier );
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
SetGoal( 10000 );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_YouTube_Views_Tier3, ACHIEVEMENT_TF_REPLAY_YOUTUBE_VIEWS_TIER3, "TF_REPLAY_YOUTUBE_VIEWS_TIER3", 5 );
|
||||
|
||||
class CAchievementTFReplay_YouTube_Views_Highest : public CAchievementTFReplay_YouTube_Views_Tier
|
||||
{
|
||||
DECLARE_CLASS( CAchievementTFReplay_YouTube_Views_Tier1, CAchievementTFReplay_YouTube_Views_Tier );
|
||||
public:
|
||||
void Init()
|
||||
{
|
||||
BaseClass::Init();
|
||||
SetGoal( 100000 );
|
||||
}
|
||||
};
|
||||
DECLARE_ACHIEVEMENT( CAchievementTFReplay_YouTube_Views_Highest, ACHIEVEMENT_TF_REPLAY_YOUTUBE_VIEWS_HIGHEST, "TF_REPLAY_YOUTUBE_VIEWS_HIGHEST", 5 );
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,723 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "baseobject_shared.h"
|
||||
#include <KeyValues.h>
|
||||
#include "tf_shareddefs.h"
|
||||
#include "engine/ivmodelinfo.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "func_no_build.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_team.h"
|
||||
#include "func_no_build.h"
|
||||
#include "func_respawnroom.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#include "c_tf_team.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
ConVar tf_obj_build_rotation_speed( "tf_obj_build_rotation_speed", "250", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Degrees per second to rotate building when player alt-fires during placement." );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Parse our model and create the buildpoints in it
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::CreateBuildPoints( void )
|
||||
{
|
||||
// Clear out any existing build points
|
||||
m_BuildPoints.RemoveAll();
|
||||
|
||||
KeyValues * modelKeyValues = new KeyValues("");
|
||||
if ( !modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we have a build point section?
|
||||
KeyValues *pkvAllBuildPoints = modelKeyValues->FindKey("build_points");
|
||||
if ( pkvAllBuildPoints )
|
||||
{
|
||||
KeyValues *pkvBuildPoint = pkvAllBuildPoints->GetFirstSubKey();
|
||||
while ( pkvBuildPoint )
|
||||
{
|
||||
// Find the attachment first
|
||||
const char *sAttachment = pkvBuildPoint->GetName();
|
||||
int iAttachmentNumber = LookupAttachment( sAttachment );
|
||||
if ( iAttachmentNumber > 0 )
|
||||
{
|
||||
AddAndParseBuildPoint( iAttachmentNumber, pkvBuildPoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "ERROR: Model %s specifies buildpoint %s, but has no attachment named %s.\n", STRING(GetModelName()), pkvBuildPoint->GetString(), pkvBuildPoint->GetString() );
|
||||
}
|
||||
|
||||
pkvBuildPoint = pkvBuildPoint->GetNextKey();
|
||||
}
|
||||
}
|
||||
|
||||
// Any virtual build points (build points that aren't on an attachment)?
|
||||
pkvAllBuildPoints = modelKeyValues->FindKey("virtual_build_points");
|
||||
if ( pkvAllBuildPoints )
|
||||
{
|
||||
KeyValues *pkvBuildPoint = pkvAllBuildPoints->GetFirstSubKey();
|
||||
while ( pkvBuildPoint )
|
||||
{
|
||||
AddAndParseBuildPoint( -1, pkvBuildPoint );
|
||||
pkvBuildPoint = pkvBuildPoint->GetNextKey();
|
||||
}
|
||||
}
|
||||
|
||||
modelKeyValues->deleteThis();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::AddAndParseBuildPoint( int iAttachmentNumber, KeyValues *pkvBuildPoint )
|
||||
{
|
||||
int iPoint = AddBuildPoint( iAttachmentNumber );
|
||||
|
||||
|
||||
m_BuildPoints[iPoint].m_bPutInAttachmentSpace = (pkvBuildPoint->GetInt( "PutInAttachmentSpace", 0 ) != 0);
|
||||
|
||||
// Now see if we've got a set of valid objects specified
|
||||
KeyValues *pkvValidObjects = pkvBuildPoint->FindKey( "valid_objects" );
|
||||
if ( pkvValidObjects )
|
||||
{
|
||||
KeyValues *pkvObject = pkvValidObjects->GetFirstSubKey();
|
||||
while ( pkvObject )
|
||||
{
|
||||
const char *pSpecifiedObject = pkvObject->GetName();
|
||||
int iLenObjName = Q_strlen( pSpecifiedObject );
|
||||
|
||||
// Find the object index for the name
|
||||
for ( int i = 0; i < OBJ_LAST; i++ )
|
||||
{
|
||||
if ( !Q_strncasecmp( GetObjectInfo( i )->m_pClassName, pSpecifiedObject, iLenObjName) )
|
||||
{
|
||||
AddValidObjectToBuildPoint( iPoint, i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pkvObject = pkvObject->GetNextKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Add a new buildpoint to my list of buildpoints
|
||||
//-----------------------------------------------------------------------------
|
||||
int CBaseObject::AddBuildPoint( int iAttachmentNum )
|
||||
{
|
||||
// Make a new buildpoint
|
||||
BuildPoint_t sNewPoint;
|
||||
sNewPoint.m_hObject = NULL;
|
||||
sNewPoint.m_iAttachmentNum = iAttachmentNum;
|
||||
sNewPoint.m_bPutInAttachmentSpace = false;
|
||||
Q_memset( sNewPoint.m_bValidObjects, 0, sizeof( sNewPoint.m_bValidObjects ) );
|
||||
|
||||
// Insert it into our list
|
||||
return m_BuildPoints.AddToTail( sNewPoint );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::AddValidObjectToBuildPoint( int iPoint, int iObjectType )
|
||||
{
|
||||
Assert( iPoint <= GetNumBuildPoints() );
|
||||
m_BuildPoints[iPoint].m_bValidObjects[ iObjectType ] = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CBaseObject::GetNumBuildPoints( void ) const
|
||||
{
|
||||
return m_BuildPoints.Size();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseObject* CBaseObject::GetBuildPointObject( int iPoint )
|
||||
{
|
||||
Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
|
||||
|
||||
return m_BuildPoints[iPoint].m_hObject;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return true if the specified object type can be built on this point
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::CanBuildObjectOnBuildPoint( int iPoint, int iObjectType )
|
||||
{
|
||||
Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
|
||||
|
||||
// Allowed to build here?
|
||||
if ( !m_BuildPoints[iPoint].m_bValidObjects[ iObjectType ] )
|
||||
return false;
|
||||
|
||||
// Buildpoint empty?
|
||||
return ( m_BuildPoints[iPoint].m_hObject == NULL );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles )
|
||||
{
|
||||
Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
|
||||
|
||||
int iAttachmentNum = m_BuildPoints[iPoint].m_iAttachmentNum;
|
||||
if ( iAttachmentNum == -1 )
|
||||
{
|
||||
vecOrigin = GetAbsOrigin();
|
||||
vecAngles = GetAbsAngles();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetAttachment( m_BuildPoints[iPoint].m_iAttachmentNum, vecOrigin, vecAngles );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CBaseObject::GetBuildPointAttachmentIndex( int iPoint ) const
|
||||
{
|
||||
Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
|
||||
|
||||
if ( m_BuildPoints[iPoint].m_bPutInAttachmentSpace )
|
||||
{
|
||||
return m_BuildPoints[iPoint].m_iAttachmentNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject )
|
||||
{
|
||||
Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
|
||||
m_BuildPoints[iPoint].m_hObject = pObject;
|
||||
}
|
||||
|
||||
ConVar tf_obj_max_attach_dist( "tf_obj_max_attach_dist", "160", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
float CBaseObject::GetMaxSnapDistance( int iPoint )
|
||||
{
|
||||
return tf_obj_max_attach_dist.GetFloat();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return the number of objects on my build points
|
||||
//-----------------------------------------------------------------------------
|
||||
int CBaseObject::GetNumObjectsOnMe( void )
|
||||
{
|
||||
int iObjects = 0;
|
||||
for ( int i = 0; i < GetNumBuildPoints(); i++ )
|
||||
{
|
||||
if ( m_BuildPoints[i].m_hObject )
|
||||
{
|
||||
iObjects++;
|
||||
}
|
||||
}
|
||||
|
||||
return iObjects;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// I've finished building the specified object on the specified build point
|
||||
//-----------------------------------------------------------------------------
|
||||
int CBaseObject::FindObjectOnBuildPoint( CBaseObject *pObject )
|
||||
{
|
||||
for (int i = m_BuildPoints.Count(); --i >= 0; )
|
||||
{
|
||||
if (m_BuildPoints[i].m_hObject == pObject)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseObject *CBaseObject::GetObjectOfTypeOnMe( int iObjectType )
|
||||
{
|
||||
for ( int iObject = 0; iObject < GetNumObjectsOnMe(); ++iObject )
|
||||
{
|
||||
CBaseObject *pObject = dynamic_cast<CBaseObject*>( m_BuildPoints[iObject].m_hObject.Get() );
|
||||
if ( pObject )
|
||||
{
|
||||
if ( pObject->GetType() == iObjectType )
|
||||
return pObject;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::RemoveAllObjects( void )
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
for ( int i = 0; i < GetNumBuildPoints(); i++ )
|
||||
{
|
||||
if ( m_BuildPoints[i].m_hObject )
|
||||
{
|
||||
|
||||
UTIL_Remove( m_BuildPoints[i].m_hObject );
|
||||
}
|
||||
}
|
||||
#endif // !CLIENT_DLL
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseObject *CBaseObject::GetParentObject( void )
|
||||
{
|
||||
if ( GetMoveParent() )
|
||||
return dynamic_cast<CBaseObject*>(GetMoveParent());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity *CBaseObject::GetParentEntity( void )
|
||||
{
|
||||
if ( GetMoveParent() )
|
||||
return GetMoveParent();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ConVar sv_ignore_hitboxes( "sv_ignore_hitboxes", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Disable hitboxes" );
|
||||
|
||||
bool CBaseObject::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
|
||||
{
|
||||
bool bReturn = BaseClass::TestHitboxes( ray, fContentsMask, tr );
|
||||
|
||||
if( !sv_ignore_hitboxes.GetBool() )
|
||||
return bReturn;
|
||||
|
||||
|
||||
if( !bReturn )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( tr.fraction == 1.f && !tr.allsolid && !tr.startsolid )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return bReturn;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return true if this object should be active
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::ShouldBeActive( void )
|
||||
{
|
||||
if ( IsDisabled() )
|
||||
return false;
|
||||
|
||||
// Placing and/or constructing objects shouldn't be active
|
||||
if ( IsPlacing() || IsBuilding() || IsCarried() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Set the object's type
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::SetType( int iObjectType )
|
||||
{
|
||||
m_iObjectType = iObjectType;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : act -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::SetActivity( Activity act )
|
||||
{
|
||||
// Hrm, it's not actually a studio model...
|
||||
if ( !GetModelPtr() )
|
||||
return;
|
||||
|
||||
int sequence = SelectWeightedSequence( act );
|
||||
if ( sequence != ACTIVITY_NOT_AVAILABLE )
|
||||
{
|
||||
m_Activity = act;
|
||||
SetObjectSequence( sequence );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Activity = ACT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Output : Activity
|
||||
//-----------------------------------------------------------------------------
|
||||
Activity CBaseObject::GetActivity( ) const
|
||||
{
|
||||
return m_Activity;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Thin wrapper over CBaseAnimating::SetSequence to do bookkeeping.
|
||||
// Input : sequence -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::SetObjectSequence( int sequence )
|
||||
{
|
||||
ResetSequence( sequence );
|
||||
|
||||
SetCycle( GetReversesBuildingConstructionSpeed() != 0.0f ? 1.0f : 0.0f );
|
||||
|
||||
#if !defined( CLIENT_DLL )
|
||||
if ( IsUsingClientSideAnimation() )
|
||||
{
|
||||
ResetClientsideFrame();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::OnGoActive( void )
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
while ( m_nDefaultUpgradeLevel + 1 > m_iUpgradeLevel )
|
||||
{
|
||||
StartUpgrading();
|
||||
}
|
||||
|
||||
// Play startup animation
|
||||
PlayStartupAnimation();
|
||||
|
||||
// Switch to the on state
|
||||
if ( GetModelPtr() )
|
||||
{
|
||||
int index = FindBodygroupByName( "powertoggle" );
|
||||
if ( index >= 0 )
|
||||
{
|
||||
SetBodygroup( index, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDisabledState();
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseObject::OnGoInactive( void )
|
||||
{
|
||||
#ifndef CLIENT_DLL
|
||||
if ( GetModelPtr() )
|
||||
{
|
||||
// Switch to the off state
|
||||
int index = FindBodygroupByName( "powertoggle" );
|
||||
if ( index >= 0 )
|
||||
{
|
||||
SetBodygroup( index, 0 );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : collisionGroup -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::ShouldCollide( int collisionGroup, int contentsMask ) const
|
||||
{
|
||||
if ( collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT )
|
||||
{
|
||||
if ( GetCollisionGroup() == TFCOLLISION_GROUP_OBJECT_SOLIDTOPLAYERMOVEMENT )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch( GetTeamNumber() )
|
||||
{
|
||||
case TF_TEAM_RED:
|
||||
if ( !( contentsMask & CONTENTS_REDTEAM ) )
|
||||
return false;
|
||||
break;
|
||||
|
||||
case TF_TEAM_BLUE:
|
||||
if ( !( contentsMask & CONTENTS_BLUETEAM ) )
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::ShouldCollide( collisionGroup, contentsMask );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Should objects repel players on the same team
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::ShouldPlayersAvoid( void )
|
||||
{
|
||||
return ( GetCollisionGroup() == TFCOLLISION_GROUP_OBJECT );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Do we have to be built on an attachment point
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::MustBeBuiltOnAttachmentPoint( void ) const
|
||||
{
|
||||
return (m_fObjectFlags & OF_MUST_BE_BUILT_ON_ATTACHMENT) != 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Find a place in the world where we should try to build this object
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::CalculatePlacementPos( void )
|
||||
{
|
||||
CTFPlayer *pPlayer = GetOwner();
|
||||
|
||||
if ( !pPlayer )
|
||||
return false;
|
||||
|
||||
// Calculate build angles
|
||||
QAngle vecAngles = vec3_angle;
|
||||
vecAngles.y = pPlayer->EyeAngles().y;
|
||||
|
||||
QAngle objAngles = vecAngles;
|
||||
|
||||
SetAbsAngles( objAngles );
|
||||
|
||||
UpdateDesiredBuildRotation( gpGlobals->frametime );
|
||||
|
||||
objAngles.y = objAngles.y + m_flCurrentBuildRotation;
|
||||
|
||||
SetLocalAngles( objAngles );
|
||||
AngleVectors( vecAngles, &m_vecBuildForward );
|
||||
|
||||
// Adjust build distance based upon object size
|
||||
Vector2D vecObjectRadius;
|
||||
vecObjectRadius.x = MAX( fabs( m_vecBuildMins.m_Value.x ), fabs( m_vecBuildMaxs.m_Value.x ) );
|
||||
vecObjectRadius.y = MAX( fabs( m_vecBuildMins.m_Value.y ), fabs( m_vecBuildMaxs.m_Value.y ) );
|
||||
|
||||
Vector2D vecPlayerRadius;
|
||||
Vector vecPlayerMins = pPlayer->WorldAlignMins();
|
||||
Vector vecPlayerMaxs = pPlayer->WorldAlignMaxs();
|
||||
vecPlayerRadius.x = MAX( fabs( vecPlayerMins.x ), fabs( vecPlayerMaxs.x ) );
|
||||
vecPlayerRadius.y = MAX( fabs( vecPlayerMins.y ), fabs( vecPlayerMaxs.y ) );
|
||||
|
||||
m_flBuildDistance = vecObjectRadius.Length() + vecPlayerRadius.Length() + 4; // small safety buffer
|
||||
Vector vecBuildOrigin = pPlayer->WorldSpaceCenter() + m_vecBuildForward * m_flBuildDistance;
|
||||
|
||||
m_vecBuildOrigin = vecBuildOrigin;
|
||||
Vector vErrorOrigin = vecBuildOrigin - (m_vecBuildMaxs - m_vecBuildMins) * 0.5f - m_vecBuildMins;
|
||||
|
||||
Vector vBuildDims = m_vecBuildMaxs - m_vecBuildMins;
|
||||
Vector vHalfBuildDims = vBuildDims * 0.5;
|
||||
Vector vHalfBuildDimsXY( vHalfBuildDims.x, vHalfBuildDims.y, 0 );
|
||||
|
||||
// Here, we start at the highest Z we'll allow for the top of the object. Then
|
||||
// we sweep an XY cross section downwards until it hits the ground.
|
||||
//
|
||||
// The rule is that the top of to box can't go lower than the player's feet, and the bottom of the
|
||||
// box can't go higher than the player's head.
|
||||
//
|
||||
// To simplify things in here, we treat the box as though it's symmetrical about all axes
|
||||
// (so mins = -maxs), then reoffset the box at the very end.
|
||||
Vector vHalfPlayerDims = (pPlayer->WorldAlignMaxs() - pPlayer->WorldAlignMins()) * 0.5f;
|
||||
float flBoxTopZ = pPlayer->WorldSpaceCenter().z + vHalfPlayerDims.z + vBuildDims.z;
|
||||
float flBoxBottomZ = pPlayer->WorldSpaceCenter().z - vHalfPlayerDims.z - vBuildDims.z;
|
||||
|
||||
// First, find the ground (ie: where the bottom of the box goes).
|
||||
trace_t tr;
|
||||
float bottomZ = 0;
|
||||
int nIterations = 8;
|
||||
float topZ = flBoxTopZ;
|
||||
float topZInc = (flBoxBottomZ - flBoxTopZ) / (nIterations-1);
|
||||
int iIteration;
|
||||
for ( iIteration = 0; iIteration < nIterations; iIteration++ )
|
||||
{
|
||||
UTIL_TraceHull(
|
||||
Vector( m_vecBuildOrigin.x, m_vecBuildOrigin.y, topZ ),
|
||||
Vector( m_vecBuildOrigin.x, m_vecBuildOrigin.y, flBoxBottomZ ),
|
||||
-vHalfBuildDimsXY, vHalfBuildDimsXY, MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
|
||||
bottomZ = tr.endpos.z;
|
||||
|
||||
// If there is no ground, then we can't place here.
|
||||
if ( tr.fraction == 1 )
|
||||
{
|
||||
m_vecBuildOrigin = vErrorOrigin;
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we found enough space to fit our object, place here
|
||||
if ( topZ - bottomZ > vBuildDims.z && !tr.startsolid )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
topZ += topZInc;
|
||||
}
|
||||
|
||||
if ( iIteration == nIterations )
|
||||
{
|
||||
m_vecBuildOrigin = vErrorOrigin;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now see if the range we've got leaves us room for our box.
|
||||
if ( topZ - bottomZ < vBuildDims.z )
|
||||
{
|
||||
m_vecBuildOrigin = vErrorOrigin;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow buildables on the train just yet.
|
||||
if ( tr.m_pEnt && tr.m_pEnt->IsBSPModel() )
|
||||
{
|
||||
if ( FClassnameIs( tr.m_pEnt, "func_tracktrain" ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify that it's not on too much of a slope by seeing how far the corners are from the ground.
|
||||
Vector vBottomCenter( m_vecBuildOrigin.x, m_vecBuildOrigin.y, bottomZ );
|
||||
if ( !VerifyCorner( vBottomCenter, -vHalfBuildDims.x, -vHalfBuildDims.y ) ||
|
||||
!VerifyCorner( vBottomCenter, +vHalfBuildDims.x, +vHalfBuildDims.y ) ||
|
||||
!VerifyCorner( vBottomCenter, +vHalfBuildDims.x, -vHalfBuildDims.y ) ||
|
||||
!VerifyCorner( vBottomCenter, -vHalfBuildDims.x, +vHalfBuildDims.y ) )
|
||||
{
|
||||
m_vecBuildOrigin = vErrorOrigin;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ok, now we know the Z range where this box can fit.
|
||||
Vector vBottomLeft = m_vecBuildOrigin - vHalfBuildDims;
|
||||
vBottomLeft.z = bottomZ;
|
||||
m_vecBuildOrigin = vBottomLeft - m_vecBuildMins;
|
||||
|
||||
m_vecBuildCenterOfMass = m_vecBuildOrigin + Vector( 0, 0, vHalfBuildDims.z );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Checks a position to make sure a corner of a building can live there
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::VerifyCorner( const Vector &vBottomCenter, float xOffset, float yOffset )
|
||||
{
|
||||
// NOTE: I am changing the 0.1 on the bottom start to 2.0 to deal with the epsilon differnece
|
||||
// between the trace hull and trace line version of collision against a rotated bsp object.
|
||||
// I will probably want to change the code if we find more bugs around this, but for now as
|
||||
// a test changing it hear should be fine.
|
||||
// Start slightly above the surface
|
||||
Vector vStart( vBottomCenter.x + xOffset, vBottomCenter.y + yOffset, vBottomCenter.z + 2.0 );
|
||||
|
||||
trace_t tr;
|
||||
UTIL_TraceLine(
|
||||
vStart,
|
||||
vStart - Vector( 0, 0, TF_OBJ_GROUND_CLEARANCE ),
|
||||
MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
|
||||
|
||||
// Cannot build on very steep slopes ( > 45 degrees )
|
||||
if ( tr.fraction < 1.0f )
|
||||
{
|
||||
Vector vecUp(0,0,1);
|
||||
tr.plane.normal.NormalizeInPlace();
|
||||
float flDot = DotProduct( tr.plane.normal, vecUp );
|
||||
|
||||
if ( flDot < 0.65 )
|
||||
{
|
||||
// Too steep
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !tr.startsolid && tr.fraction < 1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Check that the selected position is buildable
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseObject::IsPlacementPosValid( void )
|
||||
{
|
||||
bool bValid = CalculatePlacementPos();
|
||||
|
||||
if ( !bValid )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CTFPlayer *pPlayer = GetOwner();
|
||||
|
||||
if ( !pPlayer )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
if ( !EstimateValidBuildPos() )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Verify that the entire object can fit here
|
||||
// Important! here we want to collide with players and other buildings, but not dropped weapons
|
||||
trace_t tr;
|
||||
UTIL_TraceEntity( this, m_vecBuildOrigin, m_vecBuildOrigin, MASK_SOLID, NULL, COLLISION_GROUP_PLAYER, &tr );
|
||||
|
||||
if ( tr.fraction < 1.0f )
|
||||
return false;
|
||||
|
||||
// Make sure we can see the final position
|
||||
UTIL_TraceLine( pPlayer->EyePosition(), m_vecBuildOrigin + Vector(0,0,m_vecBuildMaxs[2] * 0.5), MASK_PLAYERSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr );
|
||||
if ( tr.fraction < 1.0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shared, update the build rotation
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CBaseObject::UpdateDesiredBuildRotation( float flFrameTime )
|
||||
{
|
||||
// approach desired build rotation
|
||||
float flBuildRotation = 90.0f * m_iDesiredBuildRotations;
|
||||
|
||||
m_flCurrentBuildRotation = ApproachAngle( flBuildRotation, m_flCurrentBuildRotation, tf_obj_build_rotation_speed.GetFloat() * flFrameTime );
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef BASEOBJECT_SHARED_H
|
||||
#define BASEOBJECT_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#define CBaseObject C_BaseObject
|
||||
#endif
|
||||
|
||||
class CBaseObject;
|
||||
typedef CHandle<CBaseObject> ObjectHandle;
|
||||
struct BuildPoint_t
|
||||
{
|
||||
// If this is true, then objects are parented to the attachment point instead of
|
||||
// parented to the entity's abs origin + angles. That way, they'll move if the
|
||||
// attachment point animates.
|
||||
bool m_bPutInAttachmentSpace;
|
||||
|
||||
int m_iAttachmentNum;
|
||||
ObjectHandle m_hObject;
|
||||
bool m_bValidObjects[ OBJ_LAST ];
|
||||
};
|
||||
|
||||
#define TF_OBJ_GROUND_CLEARANCE 32
|
||||
|
||||
// Shared header file for players
|
||||
#if defined( CLIENT_DLL )
|
||||
#include "c_baseobject.h"
|
||||
#else
|
||||
#include "tf_obj.h"
|
||||
#endif
|
||||
|
||||
#endif // BASEOBJECT_SHARED_H
|
||||
@@ -0,0 +1,188 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF HealthKit.
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "entity_bonuspack.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_logic_robot_destruction.h"
|
||||
#include "tf_player.h"
|
||||
#include "particle_parse.h"
|
||||
#include "tf_fx.h"
|
||||
#endif
|
||||
|
||||
#define TF_POWERCORE_RED_PICKUP "powercore_embers_red"
|
||||
#define TF_POWERCORE_BLUE_PICKUP "powercore_embers_blue"
|
||||
#define BONUS_PACK_BLINK_CONTEXT "blink_think"
|
||||
|
||||
ConVar tf_bonuspack_score( "tf_bonuspack_score", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
|
||||
#define BLINK_TIME 5.f
|
||||
#define REMOVE_TIME 20.f
|
||||
#define PICKUP_TIME 0.5f
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( BonusPack, DT_CBonusPack )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CBonusPack, DT_CBonusPack )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CBonusPack )
|
||||
END_DATADESC()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( item_bonuspack, CBonusPack );
|
||||
|
||||
IMPLEMENT_AUTO_LIST( IBonusPackAutoList );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBonusPack::CBonusPack()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
m_bAutoMaterialize = false;
|
||||
#else
|
||||
SetCycle( RandomFloat() );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusPack::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
BaseClass::BaseClass::Spawn();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
const char *pszParticleName = GetTeamNumber() == TF_TEAM_RED ? "powercore_alert_blue" : "powercore_alert_red";
|
||||
DispatchParticleEffect( pszParticleName, PATTACH_POINT_FOLLOW, this, "particle_spawn" );
|
||||
|
||||
SetModel( GetPowerupModel() );
|
||||
CollisionProp()->UseTriggerBounds( true, 64 );
|
||||
m_flCanPickupTime = gpGlobals->curtime + PICKUP_TIME;
|
||||
m_nBlinkCount = 0;
|
||||
m_flKillTime = gpGlobals->curtime + REMOVE_TIME + BLINK_TIME;
|
||||
SetContextThink( &CBonusPack::BlinkThink, gpGlobals->curtime + REMOVE_TIME, BONUS_PACK_BLINK_CONTEXT );
|
||||
SetContextThink( &CBonusPack::SUB_Remove, m_flKillTime, "RemoveThink" );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusPack::Precache( void )
|
||||
{
|
||||
// We deliberately allow late precaches here
|
||||
bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
|
||||
CBaseEntity::SetAllowPrecache( true );
|
||||
PrecacheParticleSystem( TF_POWERCORE_RED_PICKUP );
|
||||
PrecacheParticleSystem( TF_POWERCORE_BLUE_PICKUP );
|
||||
BaseClass::Precache();
|
||||
CBaseEntity::SetAllowPrecache( bAllowPrecache );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBonusPack::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( ValidTouch( pPlayer ) && gpGlobals->curtime >= m_flCanPickupTime )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( !pTFPlayer )
|
||||
return true;
|
||||
|
||||
// Play a particle colored the color of the player that picked it up
|
||||
Vector vecOrigin = GetAbsOrigin() + Vector( 0,0,5 );
|
||||
CPVSFilter pvsfilter( vecOrigin );
|
||||
const char *pszParticleName = pPlayer->GetTeamNumber() == TF_TEAM_RED ? TF_POWERCORE_RED_PICKUP : TF_POWERCORE_BLUE_PICKUP;
|
||||
TE_TFParticleEffect( pvsfilter, 0.f, pszParticleName, vecOrigin, vec3_angle );
|
||||
|
||||
if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
|
||||
{
|
||||
CTFRobotDestructionLogic::GetRobotDestructionLogic()->ScorePoints( GetTeamNumber()
|
||||
, tf_bonuspack_score.GetInt()
|
||||
, SCORE_CORES_COLLECTED
|
||||
, ToTFPlayer( pPlayer ) );
|
||||
}
|
||||
|
||||
int iBoostMax = pTFPlayer->m_Shared.GetMaxBuffedHealth();
|
||||
// Cap it to the max we'll boost a player's health
|
||||
int nHealthToAdd = clamp( 5, 0, iBoostMax - pTFPlayer->GetHealth() );
|
||||
// Give health
|
||||
pPlayer->TakeHealth( nHealthToAdd, DMG_GENERIC | DMG_IGNORE_MAXHEALTH );
|
||||
|
||||
for ( int i=0;i<TF_AMMO_COUNT;i++ )
|
||||
{
|
||||
pPlayer->GiveAmmo( 5, i );
|
||||
}
|
||||
|
||||
pPlayer->SetLastObjectiveTime( gpGlobals->curtime );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBonusPack::ValidTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
if( pPlayer->GetTeamNumber() != GetTeamNumber() )
|
||||
return false;
|
||||
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( !pTFPlayer )
|
||||
return false;
|
||||
|
||||
// No invis spies
|
||||
if ( pTFPlayer->m_Shared.InCond( TF_COND_STEALTHED ) || pTFPlayer->m_Shared.GetPercentInvisible() > 0.25f )
|
||||
return false;
|
||||
|
||||
// No disguised spies
|
||||
if ( pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) || pTFPlayer->m_Shared.InCond( TF_COND_DISGUISING ) )
|
||||
return false;
|
||||
|
||||
// No bonk'd scouts
|
||||
if ( pTFPlayer->m_Shared.InCond( TF_COND_PHASE ) || pTFPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
|
||||
return false;
|
||||
|
||||
// No teleporting players
|
||||
if ( pTFPlayer->m_Shared.InCond( TF_COND_SELECTED_TO_TELEPORT ) )
|
||||
return false;
|
||||
|
||||
// No invulns
|
||||
if ( pTFPlayer->m_Shared.IsInvulnerable() )
|
||||
return false;
|
||||
|
||||
return BaseClass::ValidTouch( pPlayer );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusPack::BlinkThink()
|
||||
{
|
||||
float flTimeToKill = m_flKillTime - gpGlobals->curtime;
|
||||
float flNextBlink = RemapValClamped( flTimeToKill, BLINK_TIME, 0.f, 0.5f, 0.1f );
|
||||
|
||||
SetContextThink( &CBonusPack::BlinkThink, gpGlobals->curtime + flNextBlink, BONUS_PACK_BLINK_CONTEXT );
|
||||
|
||||
SetRenderMode( kRenderTransAlpha );
|
||||
|
||||
++m_nBlinkCount;
|
||||
if ( m_nBlinkCount % 2 == 0 )
|
||||
{
|
||||
SetRenderColorA( 25 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRenderColorA( 255 );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF HealthKit.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef ENTITY_BONUSPACK_H
|
||||
#define ENTITY_BONUSPACK_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_powerup.h"
|
||||
#include "entity_currencypack.h"
|
||||
#else
|
||||
#include "c_entity_currencypack.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CBonusPack C_BonusPack
|
||||
#endif
|
||||
|
||||
DECLARE_AUTO_LIST( IBonusPackAutoList );
|
||||
|
||||
class CBonusPack
|
||||
#ifdef GAME_DLL
|
||||
: public CCurrencyPack
|
||||
#else
|
||||
: public C_BaseAnimating
|
||||
#endif
|
||||
, public IBonusPackAutoList
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_CLASS( CBonusPack, CCurrencyPack );
|
||||
#else
|
||||
DECLARE_CLASS( CBonusPack, C_BaseAnimating );
|
||||
#endif
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CBonusPack();
|
||||
|
||||
virtual void Spawn( void ) OVERRIDE;
|
||||
virtual void Precache( void ) OVERRIDE;
|
||||
#ifdef GAME_DLL
|
||||
virtual bool AffectedByRadiusCollection() const OVERRIDE { return false; }
|
||||
virtual bool MyTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
virtual bool ValidTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
|
||||
virtual const char *GetDefaultPowerupModel( void ) OVERRIDE
|
||||
{
|
||||
return "models/bots/bot_worker/bot_worker_powercore.mdl";
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void BlinkThink();
|
||||
|
||||
int m_nBlinkCount;
|
||||
float m_flKillTime;
|
||||
float m_flCanPickupTime;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // ENTITY_BONUSPACK_H
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,453 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF Flag.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef ENTITY_CAPTURE_FLAG_H
|
||||
#define ENTITY_CAPTURE_FLAG_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tf_item.h"
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CCaptureFlag C_CaptureFlag
|
||||
#else
|
||||
class CTFBot;
|
||||
#endif
|
||||
|
||||
#define TF_FLAG_THINK_TIME 0.25f
|
||||
#define TF_FLAG_OWNER_PICKUP_TIME 3.0f
|
||||
|
||||
#define TF_FLAG_TRAIL_ALPHA 96
|
||||
#define TF_FLAG_NUMBEROFSKINS 3
|
||||
|
||||
#define TF_FLAG_MODEL "models/flag/briefcase.mdl"
|
||||
#define TF_FLAG_ICON "../hud/objectives_flagpanel_carried"
|
||||
#define TF_FLAG_EFFECT "player_intel_papertrail"
|
||||
#define TF_FLAG_TRAIL "flagtrail"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTF Flag defines.
|
||||
//
|
||||
|
||||
#define TF_CTF_ENEMY_STOLEN "CaptureFlag.EnemyStolen"
|
||||
#define TF_CTF_ENEMY_DROPPED "CaptureFlag.EnemyDropped"
|
||||
#define TF_CTF_ENEMY_CAPTURED "CaptureFlag.EnemyCaptured"
|
||||
#define TF_CTF_ENEMY_RETURNED "CaptureFlag.EnemyReturned"
|
||||
|
||||
#define TF_CTF_TEAM_STOLEN "CaptureFlag.TeamStolen"
|
||||
#define TF_CTF_TEAM_DROPPED "CaptureFlag.TeamDropped"
|
||||
#define TF_CTF_TEAM_CAPTURED "CaptureFlag.TeamCaptured"
|
||||
#define TF_CTF_TEAM_RETURNED "CaptureFlag.TeamReturned"
|
||||
|
||||
#define TF_CTF_FLAGSPAWN "CaptureFlag.FlagSpawn"
|
||||
|
||||
#define TF_CTF_CAPTURED_TEAM_SCORE 1
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Attack/Defend Flag defines.
|
||||
//
|
||||
|
||||
#define TF_AD_ENEMY_STOLEN "AttackDefend.EnemyStolen"
|
||||
#define TF_AD_ENEMY_DROPPED "AttackDefend.EnemyDropped"
|
||||
#define TF_AD_ENEMY_CAPTURED "AttackDefend.EnemyCaptured"
|
||||
#define TF_AD_ENEMY_RETURNED "AttackDefend.EnemyReturned"
|
||||
|
||||
#define TF_MVM_AD_ENEMY_STOLEN "MVM.AttackDefend.EnemyStolen"
|
||||
#define TF_MVM_AD_ENEMY_DROPPED "MVM.AttackDefend.EnemyDropped"
|
||||
#define TF_MVM_AD_ENEMY_CAPTURED "MVM.AttackDefend.EnemyCaptured"
|
||||
#define TF_MVM_AD_ENEMY_RETURNED "MVM.AttackDefend.EnemyReturned"
|
||||
|
||||
#define TF_AD_TEAM_STOLEN "AttackDefend.TeamStolen"
|
||||
#define TF_AD_TEAM_DROPPED "AttackDefend.TeamDropped"
|
||||
#define TF_AD_TEAM_CAPTURED "AttackDefend.TeamCaptured"
|
||||
#define TF_AD_TEAM_RETURNED "AttackDefend.TeamReturned"
|
||||
|
||||
#define TF_AD_CAPTURED_SOUND "AttackDefend.Captured"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Invade Flag defines.
|
||||
//
|
||||
|
||||
#define TF_INVADE_ENEMY_STOLEN "Invade.EnemyStolen"
|
||||
#define TF_INVADE_ENEMY_DROPPED "Invade.EnemyDropped"
|
||||
#define TF_INVADE_ENEMY_CAPTURED "Invade.EnemyCaptured"
|
||||
|
||||
#define TF_INVADE_TEAM_STOLEN "Invade.TeamStolen"
|
||||
#define TF_INVADE_TEAM_DROPPED "Invade.TeamDropped"
|
||||
#define TF_INVADE_TEAM_CAPTURED "Invade.TeamCaptured"
|
||||
|
||||
#define TF_INVADE_FLAG_RETURNED "Invade.FlagReturned"
|
||||
|
||||
#define TF_INVADE_CAPTURED_TEAM_SCORE 1
|
||||
|
||||
#define TF_INVADE_NEUTRAL_TIME 30.0f
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Resource Flag defines.
|
||||
//
|
||||
|
||||
#define TF_RESOURCE_FLAGSPAWN "Resource.FlagSpawn"
|
||||
|
||||
#define TF_RESOURCE_ENEMY_STOLEN "Announcer.SD_TheirTeamHasFlag"
|
||||
#define TF_RESOURCE_ENEMY_DROPPED "Announcer.SD_TheirTeamDroppedFlag"
|
||||
#define TF_RESOURCE_ENEMY_CAPTURED "Announcer.SD_TheirTeamCapped"
|
||||
#define TF_RESOURCE_TEAM_STOLEN "Announcer.SD_OurTeamHasFlag"
|
||||
#define TF_RESOURCE_TEAM_DROPPED "Announcer.SD_OurTeamDroppedFlag"
|
||||
#define TF_RESOURCE_TEAM_CAPTURED "Announcer.SD_OurTeamCapped"
|
||||
#define TF_RESOURCE_RETURNED "Announcer.SD_FlagReturned"
|
||||
|
||||
// Halloween event strings
|
||||
#define TF_RESOURCE_EVENT_ENEMY_STOLEN "Announcer.SD_Event_TheirTeamHasFlag"
|
||||
#define TF_RESOURCE_EVENT_ENEMY_DROPPED "Announcer.SD_Event_TheirTeamDroppedFlag"
|
||||
#define TF_RESOURCE_EVENT_TEAM_STOLEN "Announcer.SD_Event_OurTeamHasFlag"
|
||||
#define TF_RESOURCE_EVENT_TEAM_DROPPED "Announcer.SD_Event_OurTeamDroppedFlag"
|
||||
#define TF_RESOURCE_EVENT_RETURNED "Announcer.SD_Event_FlagReturned"
|
||||
#define TF_RESOURCE_EVENT_NAGS "Announcer.SD_Event_FlagNags"
|
||||
#define TF_RESOURCE_EVENT_RED_CAPPED "Announcer.SD_Event_CappedRed"
|
||||
#define TF_RESOURCE_EVENT_BLUE_CAPPED "Announcer.SD_Event_CappedBlu"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Robot Destruction Flag defines.
|
||||
//
|
||||
|
||||
#define TF_RD_ENEMY_STOLEN "RD.EnemyStolen"
|
||||
#define TF_RD_ENEMY_DROPPED "RD.EnemyDropped"
|
||||
#define TF_RD_ENEMY_CAPTURED "RD.EnemyCaptured"
|
||||
#define TF_RD_ENEMY_RETURNED "RD.EnemyReturned"
|
||||
|
||||
#define TF_RD_TEAM_STOLEN "RD.TeamStolen"
|
||||
#define TF_RD_TEAM_DROPPED "RD.TeamDropped"
|
||||
#define TF_RD_TEAM_CAPTURED "RD.TeamCaptured"
|
||||
#define TF_RD_TEAM_RETURNED "RD.TeamReturned"
|
||||
|
||||
#define TF_RESOURCE_CAPTURED_TEAM_SCORE 1
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Powerup mode defines.
|
||||
//
|
||||
|
||||
#define TF_RUNE_INTEL_CAPTURED "CaptureFlag.TeamCapturedExcited"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Robot Destruction defines
|
||||
//
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CCaptureFlagReturnIcon C_CaptureFlagReturnIcon
|
||||
#define CBaseAnimating C_BaseAnimating
|
||||
#endif
|
||||
|
||||
class CCaptureFlagReturnIcon: public CBaseAnimating
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CCaptureFlagReturnIcon, CBaseEntity );
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CCaptureFlagReturnIcon();
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
virtual int DrawModel( int flags );
|
||||
void DrawReturnProgressBar( void );
|
||||
|
||||
virtual RenderGroup_t GetRenderGroup( void );
|
||||
virtual bool ShouldDraw( void ) { return true; }
|
||||
|
||||
virtual void GetRenderBounds( Vector& theMins, Vector& theMaxs );
|
||||
|
||||
private:
|
||||
|
||||
IMaterial *m_pReturnProgressMaterial_Empty; // For labels above players' heads.
|
||||
IMaterial *m_pReturnProgressMaterial_Full;
|
||||
|
||||
#else
|
||||
public:
|
||||
virtual void Spawn( void );
|
||||
virtual int UpdateTransmitState( void );
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTF Flag class.
|
||||
//
|
||||
DECLARE_AUTO_LIST( ICaptureFlagAutoList );
|
||||
class CCaptureFlag : public CTFItem, public ICaptureFlagAutoList
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS( CCaptureFlag, CTFItem );
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CCaptureFlag();
|
||||
~CCaptureFlag();
|
||||
|
||||
unsigned int GetItemID( void ) const OVERRIDE;
|
||||
|
||||
void Precache( void );
|
||||
void Spawn( void );
|
||||
|
||||
virtual void UpdateOnRemove( void );
|
||||
|
||||
void FlagTouch( CBaseEntity *pOther );
|
||||
|
||||
bool IsDisabled( void ) const;
|
||||
void SetDisabled( bool bDisabled );
|
||||
void SetVisibleWhenDisabled( bool bVisible );
|
||||
bool IsPoisonous( void ) { return m_flTimeToSetPoisonous > 0 && gpGlobals->curtime > m_flTimeToSetPoisonous; }
|
||||
float GetPoisonTime( void ) const { return m_flTimeToSetPoisonous; }
|
||||
|
||||
bool IsVisibleWhenDisabled( void ) { return m_bVisibleWhenDisabled; }
|
||||
|
||||
CBaseEntity *GetPrevOwner( void ) { return m_hPrevOwner.Get(); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the flag status
|
||||
//-----------------------------------------------------------------------------
|
||||
void SetFlagStatus( int iStatus, CBasePlayer *pNewOwner = NULL );
|
||||
|
||||
// Game DLL Functions
|
||||
#ifdef GAME_DLL
|
||||
CCaptureFlag &operator=( const CCaptureFlag& rhs );
|
||||
virtual void Activate( void );
|
||||
|
||||
static CCaptureFlag* Create( const Vector& vecOrigin, const char *pszModelName, ETFFlagType type );
|
||||
|
||||
// Input handlers
|
||||
void InputEnable( inputdata_t &inputdata );
|
||||
void InputDisable( inputdata_t &inputdata );
|
||||
void InputRoundActivate( inputdata_t &inputdata );
|
||||
void InputForceDrop( inputdata_t &inputdata );
|
||||
void InputForceReset( inputdata_t &inputdata );
|
||||
void InputForceResetSilent( inputdata_t &inputdata );
|
||||
void InputForceResetAndDisableSilent( inputdata_t &inputdata );
|
||||
void InputSetReturnTime( inputdata_t &inputdata );
|
||||
void InputShowTimer( inputdata_t &inputdata );
|
||||
void InputForceGlowDisabled( inputdata_t &inputdata );
|
||||
|
||||
void Think( void );
|
||||
|
||||
void CreateReturnIcon( void );
|
||||
void DestroyReturnIcon( void );
|
||||
|
||||
void ResetFlagReturnTime( void ) { m_flResetTime = 0; }
|
||||
void SetFlagReturnIn( float flTime )
|
||||
{
|
||||
m_flResetTime = gpGlobals->curtime + flTime;
|
||||
m_flMaxResetTime = flTime;
|
||||
}
|
||||
|
||||
void SetFlagReturnIn( float flTime, float flMaxResetTime )
|
||||
{
|
||||
m_flResetTime = gpGlobals->curtime + flTime;
|
||||
m_flMaxResetTime = flMaxResetTime;
|
||||
}
|
||||
|
||||
void ResetFlagNeutralTime( void ) { m_flNeutralTime = 0; }
|
||||
void SetFlagNeutralIn( float flTime )
|
||||
{
|
||||
m_flNeutralTime = gpGlobals->curtime + flTime;
|
||||
m_flMaxResetTime = flTime;
|
||||
}
|
||||
bool IsCaptured( void ){ return m_bCaptured; }
|
||||
|
||||
int UpdateTransmitState();
|
||||
|
||||
void StartFlagTrail ( void );
|
||||
void RemoveFlagTrail ( void );
|
||||
EHANDLE m_pFlagTrail;
|
||||
float m_flFlagTrailLife;
|
||||
bool m_bInstantTrailRemove;
|
||||
|
||||
int GetNumTags() const { return m_tags.Count(); }
|
||||
const char* GetTag( int i ) const { return m_tags[i]; }
|
||||
void AddFollower( CTFBot* pBot );
|
||||
void RemoveFollower( CTFBot* pBot );
|
||||
int GetNumFollowers() const { return m_followers.Count(); }
|
||||
|
||||
void AddPointValue( int nPoints );
|
||||
|
||||
#else // CLIENT DLL Functions
|
||||
virtual bool ShouldDraw() OVERRIDE;
|
||||
virtual bool IsVisibleToTargetID() const OVERRIDE;
|
||||
virtual const char *GetIDString( void ) { return "entity_capture_flag"; };
|
||||
|
||||
virtual void OnPreDataChanged( DataUpdateType_t updateType );
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType );
|
||||
|
||||
void CreateSiren( void );
|
||||
void DestroySiren( void );
|
||||
|
||||
void ManageTrailEffects( void );
|
||||
|
||||
CNewParticleEffect *m_pGlowTrailEffect;
|
||||
CNewParticleEffect *m_pPaperTrailEffect;
|
||||
|
||||
virtual void Simulate( void );
|
||||
|
||||
float GetMaxResetTime() { return m_flMaxResetTime; }
|
||||
float GetReturnProgress( void );
|
||||
|
||||
public:
|
||||
|
||||
void UpdateGlowEffect( void );
|
||||
virtual bool ShouldHideGlowEffect( void );
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: Both of these should be updated to work with floats instead of ints.
|
||||
int GetReturnTime( int nMaxReturnTime );
|
||||
int GetMaxReturnTime( void );
|
||||
|
||||
void Capture( CTFPlayer *pPlayer, int nCapturePoint );
|
||||
virtual void PickUp( CTFPlayer *pPlayer, bool bInvisible );
|
||||
virtual void Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown = false, bool bMessage = true );
|
||||
|
||||
ETFFlagType GetType( void ) const { return (ETFFlagType)m_nType.Get(); }
|
||||
|
||||
bool IsDropped( void );
|
||||
bool IsHome( void );
|
||||
bool IsStolen( void );
|
||||
|
||||
void ResetFlag( void )
|
||||
{
|
||||
Reset();
|
||||
ResetMessage();
|
||||
}
|
||||
|
||||
const char *GetFlagModel( void );
|
||||
void GetHudIcon( int nTeam, char *pchName, int nBuffSize );
|
||||
const char *GetPaperEffect( void );
|
||||
void GetTrailEffect( int nTeam, char *pchName, int nBuffSize );
|
||||
|
||||
int GetPointValue() const { return m_nPointValue.Get(); }
|
||||
private:
|
||||
|
||||
void Reset( void );
|
||||
void ResetMessage( void );
|
||||
void InternalForceReset( bool bSilent = false );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void PlaySound( IRecipientFilter& filter, const char *pszString, int iTeam = TEAM_ANY );
|
||||
|
||||
float m_flNextTeamSoundTime[TF_TEAM_COUNT];
|
||||
|
||||
void SetGlowEnabled( bool bGlowEnabled ){ m_bGlowEnabled = bGlowEnabled; }
|
||||
#endif
|
||||
|
||||
bool IsGlowEnabled( void ){ return m_bGlowEnabled; }
|
||||
|
||||
private:
|
||||
|
||||
CNetworkVar( bool, m_bDisabled ); // Enabled/Disabled?
|
||||
CNetworkVar( bool, m_bVisibleWhenDisabled );
|
||||
CNetworkVar( int, m_nType ); // Type of game this flag will be used for.
|
||||
|
||||
CNetworkVar( int, m_nFlagStatus );
|
||||
CNetworkVar( float, m_flResetTime ); // Time until the flag is placed back at spawn.
|
||||
CNetworkVar( float, m_flMaxResetTime ); // Time the flag takes to return in the current mode
|
||||
CNetworkVar( float, m_flNeutralTime ); // Time until the flag becomes neutral (used for the invade gametype)
|
||||
CNetworkHandle( CBaseEntity, m_hPrevOwner );
|
||||
CNetworkVar( int, m_nPointValue ); // How many points this flag is worth when scored. Used in Robot Destruction mode.
|
||||
CNetworkVar( float, m_flAutoCapTime );
|
||||
CNetworkVar( bool, m_bGlowEnabled );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
string_t m_iszModel;
|
||||
string_t m_iszHudIcon;
|
||||
string_t m_iszPaperEffect;
|
||||
string_t m_iszTrailEffect;
|
||||
|
||||
string_t m_iszTags;
|
||||
CUtlStringList m_tags;
|
||||
|
||||
CUtlVector< CHandle< CTFBot > > m_followers;
|
||||
#endif
|
||||
|
||||
CNetworkString( m_szModel, MAX_PATH );
|
||||
CNetworkString( m_szHudIcon, MAX_PATH );
|
||||
CNetworkString( m_szPaperEffect, MAX_PATH );
|
||||
CNetworkString( m_szTrailEffect, MAX_PATH );
|
||||
CNetworkVar( int, m_nUseTrailEffect );
|
||||
|
||||
|
||||
int m_iOriginalTeam;
|
||||
float m_flOwnerPickupTime;
|
||||
|
||||
int GetReturnTimeShotClockMode( int nStartReturnTime );
|
||||
inline bool IsFlagShotClockModePossible() const
|
||||
{
|
||||
return m_nType == TF_FLAGTYPE_CTF
|
||||
|| m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION
|
||||
|| m_nType == TF_FLAGTYPE_RESOURCE_CONTROL;
|
||||
}
|
||||
|
||||
float m_flLastPickupTime; // What the time was of the last pickup by any player.
|
||||
float m_flLastResetDuration; // How long was the last time to reset before being picked up?
|
||||
|
||||
int m_nReturnTime; // Length of time (in seconds) before dropped flag/intelligence returns to base.
|
||||
int m_nNeutralType; // Type of neutral flag (only used for Invade game type).
|
||||
int m_nScoringType; // Type of scoring for flag capture (only used for Invade game type).
|
||||
|
||||
bool m_bReturnBetweenWaves; // Used in MvM mode to determine if the flag should return between waves.
|
||||
bool m_bUseShotClockMode; // Used to determine whether we should be using shot clock mode or not.
|
||||
|
||||
CNetworkVar( float, m_flTimeToSetPoisonous ); // Time to set the flag as poisonous
|
||||
|
||||
EHANDLE m_hReturnIcon;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
Vector m_vecResetPos; // The position the flag should respawn (reset) at.
|
||||
QAngle m_vecResetAng; // The angle the flag should respawn (reset) at.
|
||||
|
||||
COutputEvent m_outputOnReturn; // Fired when the flag is returned via timer.
|
||||
COutputEvent m_outputOnPickUp; // Fired when the flag is picked up.
|
||||
COutputEvent m_outputOnPickUpTeam1; // Fired when the flag is picked up by RED.
|
||||
COutputEvent m_outputOnPickUpTeam2; // Fired when the flag is picked up by BLU.
|
||||
COutputEvent m_outputOnDrop; // Fired when the flag is dropped.
|
||||
COutputEvent m_outputOnCapture; // Fired when the flag is captured.
|
||||
COutputEvent m_OnCapTeam1;
|
||||
COutputEvent m_OnCapTeam2;
|
||||
COutputEvent m_OnTouchSameTeam;
|
||||
|
||||
bool m_bAllowOwnerPickup;
|
||||
|
||||
bool m_bCaptured;
|
||||
|
||||
EHANDLE m_hInitialPlayer;
|
||||
|
||||
EHANDLE m_hInitialParent;
|
||||
Vector m_vecOffset;
|
||||
|
||||
#else
|
||||
|
||||
IMaterial *m_pReturnProgressMaterial_Empty; // For labels above players' heads.
|
||||
IMaterial *m_pReturnProgressMaterial_Full;
|
||||
|
||||
int m_nOldTeamNumber;
|
||||
EHANDLE m_hOldOwner;
|
||||
|
||||
CGlowObject *m_pGlowEffect;
|
||||
CGlowObject *m_pCarrierGlowEffect;
|
||||
HPARTICLEFFECT m_hSirenEffect;
|
||||
|
||||
bool m_bOldGlowEnabled;
|
||||
#endif
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#endif // ENTITY_CAPTURE_FLAG_H
|
||||
@@ -0,0 +1,760 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF HealthKit.
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "entity_halloween_pickup.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "items.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_team.h"
|
||||
#include "engine/IEngineSound.h"
|
||||
#include "entity_halloween_pickup.h"
|
||||
#include "tf_fx.h"
|
||||
#include "tf_logic_halloween_2014.h"
|
||||
#endif // GAME_DLL
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
#include "tf_shareddefs.h"
|
||||
#include "tf_duckleaderboard.h"
|
||||
|
||||
|
||||
#define TF_HALLOWEEN_PICKUP_RETURN_DELAY 10
|
||||
|
||||
#ifdef GAME_DLL
|
||||
IMPLEMENT_AUTO_LIST( IHalloweenGiftSpawnAutoList );
|
||||
#endif // GAME_DLL
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTF Halloween Pickup defines.
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( HalloweenPickup, DT_CHalloweenPickup )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CHalloweenPickup, DT_CHalloweenPickup )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CHalloweenPickup )
|
||||
DEFINE_KEYFIELD( m_iszSound, FIELD_STRING, "pickup_sound" ),
|
||||
DEFINE_KEYFIELD( m_iszParticle, FIELD_STRING, "pickup_particle" ),
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DEFINE_OUTPUT( m_OnRedPickup, "OnRedPickup" ),
|
||||
DEFINE_OUTPUT( m_OnBluePickup, "OnBluePickup" ),
|
||||
#endif
|
||||
END_DATADESC();
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_halloween_pickup, CHalloweenPickup );
|
||||
|
||||
// ************************************************************************************
|
||||
BEGIN_DATADESC( CBonusDuckPickup )
|
||||
// DEFINE_KEYFIELD( m_iszSound, FIELD_STRING, "pickup_sound" ),
|
||||
// DEFINE_KEYFIELD( m_iszParticle, FIELD_STRING, "pickup_particle" ),
|
||||
END_DATADESC();
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( BonusDuckPickup, DT_CBonusDuckPickup )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CBonusDuckPickup, DT_CBonusDuckPickup )
|
||||
#ifdef GAME_DLL
|
||||
SendPropBool( SENDINFO( m_bSpecial ) ),
|
||||
#else
|
||||
RecvPropBool( RECVINFO( m_bSpecial ) ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_bonus_duck_pickup, CBonusDuckPickup );
|
||||
// ************************************************************************************
|
||||
#ifdef GAME_DLL
|
||||
LINK_ENTITY_TO_CLASS( tf_halloween_gift_spawn_location, CHalloweenGiftSpawnLocation );
|
||||
#endif
|
||||
// ************************************************************************************
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( HalloweenGiftPickup, DT_CHalloweenGiftPickup )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CHalloweenGiftPickup, DT_CHalloweenGiftPickup )
|
||||
#ifdef CLIENT_DLL
|
||||
RecvPropEHandle( RECVINFO( m_hTargetPlayer ) ),
|
||||
#else
|
||||
SendPropEHandle( SENDINFO( m_hTargetPlayer ) ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_DATADESC( CHalloweenGiftPickup )
|
||||
END_DATADESC();
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_halloween_gift_pickup, CHalloweenGiftPickup );
|
||||
// ************************************************************************************
|
||||
|
||||
// ************************************************************************************
|
||||
|
||||
|
||||
ConVar tf_halloween_gift_lifetime( "tf_halloween_gift_lifetime", "240", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
|
||||
#ifdef STAGING_ONLY
|
||||
ConVar tf_halloween_gift_soul_value( "tf_halloween_gift_soul_value", "10", FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTF Halloween Pickup functions.
|
||||
//
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CHalloweenPickup::CHalloweenPickup()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
ChangeTeam( TEAM_UNASSIGNED );
|
||||
#endif
|
||||
|
||||
m_iszSound = MAKE_STRING( "Halloween.Quack" );
|
||||
m_iszParticle = MAKE_STRING( "halloween_explosion" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CHalloweenPickup::~CHalloweenPickup()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Precache function for the pickup
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHalloweenPickup::Precache( void )
|
||||
{
|
||||
// We deliberately allow late precaches here
|
||||
bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
|
||||
CBaseEntity::SetAllowPrecache( true );
|
||||
PrecacheScriptSound( TF_HALLOWEEN_PICKUP_DEFAULT_SOUND );
|
||||
if ( m_iszSound != NULL_STRING )
|
||||
{
|
||||
PrecacheScriptSound( STRING( m_iszSound ) );
|
||||
}
|
||||
if ( m_iszParticle != NULL_STRING )
|
||||
{
|
||||
PrecacheParticleSystem( STRING( m_iszParticle ) );
|
||||
}
|
||||
BaseClass::Precache();
|
||||
CBaseEntity::SetAllowPrecache( bAllowPrecache );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHalloweenPickup::UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CHalloweenPickup::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
||||
{
|
||||
return FL_EDICT_ALWAYS;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: MyTouch function for the pickup
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHalloweenPickup::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
bool bSuccess = false;
|
||||
|
||||
if ( ValidTouch( pPlayer ) )
|
||||
{
|
||||
bSuccess = true;
|
||||
|
||||
switch( pPlayer->GetTeamNumber() )
|
||||
{
|
||||
case TF_TEAM_BLUE:
|
||||
m_OnBluePickup.FireOutput( this, this );
|
||||
break;
|
||||
case TF_TEAM_RED:
|
||||
m_OnRedPickup.FireOutput( this, this );
|
||||
break;
|
||||
}
|
||||
|
||||
Vector vecOrigin = GetAbsOrigin() + Vector( 0, 0, 32 );
|
||||
CPVSFilter filter( vecOrigin );
|
||||
if ( m_iszSound != NULL_STRING )
|
||||
{
|
||||
EmitSound( filter, entindex(), STRING( m_iszSound ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( filter, entindex(), TF_HALLOWEEN_PICKUP_DEFAULT_SOUND );
|
||||
}
|
||||
|
||||
if ( m_iszParticle != NULL_STRING )
|
||||
{
|
||||
TE_TFParticleEffect( filter, 0.0, STRING( m_iszParticle ), vecOrigin, vec3_angle );
|
||||
}
|
||||
|
||||
// Increment score directly during 2014 halloween
|
||||
if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
|
||||
{
|
||||
inputdata_t inputdata;
|
||||
|
||||
inputdata.pActivator = NULL;
|
||||
inputdata.pCaller = NULL;
|
||||
inputdata.value.SetInt( 1 );
|
||||
inputdata.nOutputID = 0;
|
||||
|
||||
if ( pPlayer->GetTeamNumber() == TF_TEAM_RED )
|
||||
{
|
||||
CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame()->InputScoreTeamRed( inputdata );
|
||||
}
|
||||
else
|
||||
{
|
||||
CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame()->InputScoreTeamBlue( inputdata );
|
||||
}
|
||||
}
|
||||
|
||||
if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer )
|
||||
{
|
||||
pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_DOOMSDAY_COLLECT_DUCKS );
|
||||
|
||||
IGameEvent *pEvent = gameeventmanager->CreateEvent( "halloween_duck_collected" );
|
||||
if ( pEvent )
|
||||
{
|
||||
pEvent->SetInt( "collector", pTFPlayer->GetUserID() );
|
||||
gameeventmanager->FireEvent( pEvent, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHalloweenPickup::ValidTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
|
||||
return false;
|
||||
|
||||
return BaseClass::ValidTouch( pPlayer );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
float CHalloweenPickup::GetRespawnDelay( void )
|
||||
{
|
||||
return TF_HALLOWEEN_PICKUP_RETURN_DELAY;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Do everything that our base does, but don't change our origin
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity* CHalloweenPickup::Respawn( void )
|
||||
{
|
||||
SetTouch( NULL );
|
||||
AddEffects( EF_NODRAW );
|
||||
|
||||
VPhysicsDestroyObject();
|
||||
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( FSOLID_TRIGGER );
|
||||
|
||||
m_bRespawning = true;
|
||||
|
||||
//UTIL_SetOrigin( this, g_pGameRules->VecItemRespawnSpot( this ) );// blip to whereever you should respawn.
|
||||
SetAbsAngles( g_pGameRules->VecItemRespawnAngles( this ) );// set the angles.
|
||||
|
||||
#if !defined( TF_DLL )
|
||||
UTIL_DropToFloor( this, MASK_SOLID );
|
||||
#endif
|
||||
|
||||
RemoveAllDecals(); //remove any decals
|
||||
|
||||
SetThink ( &CItem::Materialize );
|
||||
SetNextThink( gpGlobals->curtime + GetRespawnDelay() );
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CHalloweenPickup::ItemCanBeTouchedByPlayer( CBasePlayer *pPlayer )
|
||||
{
|
||||
if ( m_flThrowerTouchTime > 0.f && gpGlobals->curtime < m_flThrowerTouchTime )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return BaseClass::ItemCanBeTouchedByPlayer( pPlayer );
|
||||
}
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
// ***********************************************************************************************
|
||||
ConVar tf_duck_allow_team_pickup( "tf_duck_allow_team_pickup", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
|
||||
CBonusDuckPickup::CBonusDuckPickup()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
ChangeTeam( TEAM_UNASSIGNED );
|
||||
|
||||
m_iCreatorId = -1;
|
||||
m_iVictimId = -1;
|
||||
m_iAssisterId = -1;
|
||||
m_iFlags = 0;
|
||||
#else
|
||||
pGlowEffect = NULL;
|
||||
#endif
|
||||
|
||||
m_bSpecial = false;
|
||||
m_iszSound = MAKE_STRING( BONUS_DUCK_CREATED_SOUND );
|
||||
m_iszParticle = MAKE_STRING( "duck_pickup" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBonusDuckPickup::~CBonusDuckPickup()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
m_flLifeTime = 0;
|
||||
#else
|
||||
if ( pGlowEffect )
|
||||
{
|
||||
ParticleProp()->StopEmission( pGlowEffect );
|
||||
pGlowEffect = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::Precache( void )
|
||||
{
|
||||
// We deliberately allow late precaches here
|
||||
bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
|
||||
CBaseEntity::SetAllowPrecache( true );
|
||||
PrecacheParticleSystem( BONUS_DUCK_GLOW );
|
||||
PrecacheParticleSystem( BONUS_DUCK_TRAIL_RED );
|
||||
PrecacheParticleSystem( BONUS_DUCK_TRAIL_BLUE );
|
||||
PrecacheParticleSystem( BONUS_DUCK_TRAIL_SPECIAL_RED );
|
||||
PrecacheParticleSystem( BONUS_DUCK_TRAIL_SPECIAL_BLUE );
|
||||
PrecacheScriptSound( BONUS_DUCK_CREATED_SOUND );
|
||||
BaseClass::Precache();
|
||||
CBaseEntity::SetAllowPrecache( bAllowPrecache );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBonusDuckPickup::ValidTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
// Is the item enabled?
|
||||
if ( IsDisabled() )
|
||||
return false;
|
||||
|
||||
// Only touch a live player.
|
||||
if ( !pPlayer || !pPlayer->IsPlayer() || !pPlayer->IsAlive() )
|
||||
return false;
|
||||
|
||||
if ( ( GetTeamNumber() >= FIRST_GAME_TEAM ) && ( pPlayer->GetTeamNumber() == GetTeamNumber() ) )
|
||||
return false;
|
||||
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
#define DUCK_BLINK_TIME 3.0f
|
||||
void CBonusDuckPickup::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
//SetCycle( RandomFloat(0, 60.0f) );
|
||||
|
||||
//align to the ground so we're not standing on end
|
||||
QAngle angle = vec3_angle;
|
||||
// rotate randomly in yaw
|
||||
angle[1] = random->RandomFloat( 0, 360 );
|
||||
SetAbsAngles( angle );
|
||||
|
||||
float flLifeTime = GetLifeTime();
|
||||
m_flKillTime = gpGlobals->curtime + flLifeTime;
|
||||
m_nBlinkCount = 0;
|
||||
SetContextThink( &CBonusDuckPickup::BlinkThink, gpGlobals->curtime + flLifeTime - DUCK_BLINK_TIME, "BonusDuckBlinkThink" );
|
||||
SetContextThink( &CBonusDuckPickup::UpdateCollisionBounds, gpGlobals->curtime + 2.0f, "UpdateCollisionBoundsThink" );
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBonusDuckPickup::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
bool bSuccess = false;
|
||||
|
||||
if ( tf_duck_allow_team_pickup.GetBool() || ValidTouch( pPlayer ) )
|
||||
{
|
||||
bSuccess = true;
|
||||
|
||||
Vector vecOrigin = GetAbsOrigin();
|
||||
CPVSFilter pvsFilter( vecOrigin );
|
||||
if ( m_iszSound != NULL_STRING )
|
||||
{
|
||||
EmitSound( pvsFilter, entindex(), STRING( m_iszSound ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitSound( pvsFilter, entindex(), TF_HALLOWEEN_PICKUP_DEFAULT_SOUND );
|
||||
}
|
||||
|
||||
if ( m_iszParticle != NULL_STRING )
|
||||
{
|
||||
TE_TFParticleEffect( pvsFilter, 0.0, STRING( m_iszParticle ), vecOrigin, vec3_angle );
|
||||
}
|
||||
|
||||
if ( m_bSpecial )
|
||||
{
|
||||
CSingleUserRecipientFilter userfilter( pPlayer );
|
||||
UserMessageBegin( userfilter, "BonusDucks" );
|
||||
WRITE_BYTE( pPlayer->entindex() );
|
||||
WRITE_BYTE( true );
|
||||
MessageEnd();
|
||||
}
|
||||
|
||||
// Notify User that they picked up a EOTL duck if the holiday is active
|
||||
if ( pPlayer && TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_EOTL ) && !TFGameRules()->HaveCheatsBeenEnabledDuringLevel() )
|
||||
{
|
||||
int iFlags = m_iFlags;
|
||||
if ( m_bSpecial )
|
||||
{
|
||||
iFlags |= DUCK_FLAG_BONUS;
|
||||
}
|
||||
|
||||
// Send Message to Toucher and Creator if Creator is same team as toucher
|
||||
// Tell your team you picked up a duck
|
||||
// IsCreated, ID of Creator, ID of Victim, Count, IsGolden
|
||||
|
||||
// Message to Toucher
|
||||
{
|
||||
CSingleUserRecipientFilter userfilter( pPlayer );
|
||||
UserMessageBegin( userfilter, "EOTLDuckEvent" );
|
||||
WRITE_BYTE( false );
|
||||
WRITE_BYTE( m_iCreatorId );
|
||||
WRITE_BYTE( m_iVictimId );
|
||||
WRITE_BYTE( pPlayer->entindex() );
|
||||
WRITE_BYTE( GetTeamNumber() );
|
||||
WRITE_BYTE( 1 );
|
||||
WRITE_BYTE( iFlags );
|
||||
MessageEnd();
|
||||
}
|
||||
|
||||
// Notify Creator
|
||||
if ( m_iCreatorId != pPlayer->entindex() )
|
||||
{
|
||||
CBasePlayer *pCreator = UTIL_PlayerByIndex( m_iCreatorId );
|
||||
if ( pCreator && pCreator->InSameTeam( pPlayer ) )
|
||||
{
|
||||
CSingleUserRecipientFilter userfilter( pCreator );
|
||||
UserMessageBegin( userfilter, "EOTLDuckEvent" );
|
||||
WRITE_BYTE( false );
|
||||
WRITE_BYTE( m_iCreatorId );
|
||||
WRITE_BYTE( m_iVictimId );
|
||||
WRITE_BYTE( pPlayer->entindex() );
|
||||
WRITE_BYTE( GetTeamNumber() );
|
||||
WRITE_BYTE( 1 );
|
||||
WRITE_BYTE( iFlags );
|
||||
MessageEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Notify Assister someone picked up their duck as well
|
||||
if ( m_iAssisterId != -1 && m_iAssisterId != pPlayer->entindex() )
|
||||
{
|
||||
CBasePlayer *pAssister = UTIL_PlayerByIndex( m_iAssisterId );
|
||||
if ( pAssister && pAssister->InSameTeam( pPlayer ) )
|
||||
{
|
||||
CSingleUserRecipientFilter userfilter( pAssister );
|
||||
UserMessageBegin( userfilter, "EOTLDuckEvent" );
|
||||
WRITE_BYTE( false );
|
||||
WRITE_BYTE( m_iAssisterId );
|
||||
WRITE_BYTE( m_iVictimId );
|
||||
WRITE_BYTE( pPlayer->entindex() );
|
||||
WRITE_BYTE( GetTeamNumber() );
|
||||
WRITE_BYTE( 1 );
|
||||
WRITE_BYTE( iFlags );
|
||||
MessageEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::DropSingleInstance( Vector &vecLaunchVel, CBaseCombatCharacter *pThrower, float flThrowerTouchDelay, float flResetTime /*= 0.1f*/ )
|
||||
{
|
||||
// Remove ourselves after some time
|
||||
SetContextThink( &CBonusDuckPickup::NotifyFadeOut, gpGlobals->curtime + GetLifeTime(), "CBonusDuckPreRemoveThink" );
|
||||
|
||||
BaseClass::DropSingleInstance( vecLaunchVel, pThrower, flThrowerTouchDelay, flResetTime );
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::NotifyFadeOut( void )
|
||||
{
|
||||
//// Notify User that they picked up a EOTL duck if the holiday is active
|
||||
//if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_EOTL ) )
|
||||
//{
|
||||
// int iFlags = 0;
|
||||
// if ( m_bSpecial )
|
||||
// {
|
||||
// iFlags |= DUCK_FLAG_BONUS;
|
||||
// }
|
||||
// // Tell your team you picked up a duck
|
||||
// // IsCreated, ID of Creator, ID of Victim, Count, IsGolden
|
||||
// CTeamRecipientFilter userfilter( GetTeamNumber(), true );
|
||||
// UserMessageBegin( userfilter, "EOTLDuckEvent" );
|
||||
// WRITE_BYTE( false );
|
||||
// WRITE_BYTE( m_iCreatorId );
|
||||
// WRITE_BYTE( m_iVictimId );
|
||||
// WRITE_BYTE( 0 );
|
||||
// WRITE_BYTE( GetTeamNumber() );
|
||||
// WRITE_BYTE( 1 );
|
||||
// WRITE_BYTE( iFlags );
|
||||
// MessageEnd();
|
||||
//}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::UpdateCollisionBounds()
|
||||
{
|
||||
CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) );
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::BlinkThink()
|
||||
{
|
||||
float flTimeToKill = m_flKillTime - gpGlobals->curtime;
|
||||
float flNextBlink = RemapValClamped( flTimeToKill, DUCK_BLINK_TIME, 0.f, 0.3f, 0.05f );
|
||||
|
||||
SetContextThink( &CBonusDuckPickup::BlinkThink, gpGlobals->curtime + flNextBlink, "BonusDuckBlinkThink" );
|
||||
|
||||
SetRenderMode( kRenderTransAlpha );
|
||||
|
||||
++m_nBlinkCount;
|
||||
if ( m_nBlinkCount % 2 == 0 )
|
||||
{
|
||||
SetRenderColorA( 50 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRenderColorA( 255 );
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBonusDuckPickup::OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
BaseClass::OnDataChanged( updateType );
|
||||
|
||||
if ( updateType == DATA_UPDATE_CREATED )
|
||||
{
|
||||
if ( !IsDormant() )
|
||||
{
|
||||
|
||||
if ( pGlowEffect )
|
||||
{
|
||||
ParticleProp()->StopEmission( pGlowEffect );
|
||||
pGlowEffect = NULL;
|
||||
}
|
||||
|
||||
if ( m_bSpecial )
|
||||
{
|
||||
pGlowEffect = ParticleProp()->Create( BONUS_DUCK_GLOW, PATTACH_ABSORIGIN_FOLLOW, 0, Vector( 0, 0, 10 ) );
|
||||
|
||||
// these are fire and forget
|
||||
ParticleProp()->Create( ( GetTeamNumber() == TF_TEAM_RED ) ? BONUS_DUCK_TRAIL_SPECIAL_RED : BONUS_DUCK_TRAIL_SPECIAL_BLUE, PATTACH_ABSORIGIN_FOLLOW );
|
||||
}
|
||||
else
|
||||
{
|
||||
// these are fire and forget
|
||||
ParticleProp()->Create( ( GetTeamNumber() == TF_TEAM_RED ) ? BONUS_DUCK_TRAIL_RED : BONUS_DUCK_TRAIL_BLUE, PATTACH_ABSORIGIN_FOLLOW );
|
||||
}
|
||||
|
||||
CPVSFilter filter( GetAbsOrigin() );
|
||||
EmitSound( filter, entindex(), BONUS_DUCK_CREATED_SOUND );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Halloween Gift Spawn
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef GAME_DLL
|
||||
CHalloweenGiftSpawnLocation::CHalloweenGiftSpawnLocation()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------
|
||||
CHalloweenGiftPickup::CHalloweenGiftPickup()
|
||||
{
|
||||
m_hTargetPlayer = NULL;
|
||||
#ifdef CLIENT_DLL
|
||||
m_pPreviousTargetPlayer = NULL;
|
||||
#endif
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheScriptSound( "sf15.Merasmus.Gargoyle.Spawn" );
|
||||
PrecacheScriptSound( "sf15.Merasmus.Gargoyle.Gone" );
|
||||
PrecacheScriptSound( "sf15.Merasmus.Gargoyle.Got" );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::Spawn( void )
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
#ifdef GAME_DLL
|
||||
// Set a timer
|
||||
SetContextThink( &CHalloweenGiftPickup::DespawnGift, gpGlobals->curtime + tf_halloween_gift_lifetime.GetInt(), "DespawnGift" );
|
||||
AddSpawnFlags( SF_NORESPAWN );
|
||||
#endif // CLIENT_DLL
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//------------------------------------------------------------------------
|
||||
// Despawn (and notify client) and then remove
|
||||
//------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::DespawnGift()
|
||||
{
|
||||
SetTargetPlayer( NULL );
|
||||
SetContextThink( &CHalloweenGiftPickup::RemoveGift, gpGlobals->curtime + 1.0, "RemoveGift" );
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::RemoveGift()
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::SetTargetPlayer( CTFPlayer *pTarget )
|
||||
{
|
||||
m_hTargetPlayer = pTarget;
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
bool CHalloweenGiftPickup::ValidTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer && pTFPlayer != m_hTargetPlayer.Get() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
bool CHalloweenGiftPickup::MyTouch( CBasePlayer *pPlayer )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer && pTFPlayer != m_hTargetPlayer.Get() )
|
||||
return false;
|
||||
|
||||
// TODO: Give contract points
|
||||
|
||||
// Visual effects
|
||||
Vector vecOrigin = GetAbsOrigin();
|
||||
CPVSFilter filter( vecOrigin );
|
||||
|
||||
TE_TFParticleEffect( filter, 0.0, "duck_collect_green", vecOrigin, vec3_angle );
|
||||
|
||||
// Sound effects
|
||||
CSingleUserRecipientFilter touchingFilter( pPlayer );
|
||||
EmitSound( touchingFilter, entindex(), "Halloween.PumpkinPickup" );
|
||||
EmitSound( touchingFilter, entindex(), "sf15.Merasmus.Gargoyle.Got" );
|
||||
|
||||
// Give souls to the collecting player
|
||||
#ifdef STAGING_ONLY
|
||||
for( int i=0; i<tf_halloween_gift_soul_value.GetInt(); ++i )
|
||||
#else
|
||||
for( int i=0; i<10; ++i )
|
||||
#endif // STAGING_ONLY
|
||||
{
|
||||
TFGameRules()->DropHalloweenSoulPack( 1, vecOrigin, pPlayer, TEAM_SPECTATOR );
|
||||
}
|
||||
|
||||
// Achievement
|
||||
if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_MANN_MANOR ) )
|
||||
{
|
||||
pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_HALLOWEEN_COLLECT_GOODY_BAG );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
void CHalloweenGiftPickup::OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
BaseClass::OnDataChanged( updateType );
|
||||
|
||||
if ( updateType == DATA_UPDATE_DATATABLE_CHANGED )
|
||||
{
|
||||
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( pLocalPlayer )
|
||||
{
|
||||
// Gift Added
|
||||
if ( m_hTargetPlayer.Get() != NULL && m_pPreviousTargetPlayer == NULL && m_hTargetPlayer.Get() == pLocalPlayer )
|
||||
{
|
||||
// Notification
|
||||
CEconNotification *pNotification = new CEconNotification();
|
||||
pNotification->SetText( "#TF_HalloweenItem_SoulAppeared" );
|
||||
pNotification->SetLifetime( 5.0f );
|
||||
pNotification->SetSoundFilename( "ui/halloween_loot_spawn.wav" );
|
||||
NotificationQueue_Add( pNotification );
|
||||
pLocalPlayer->EmitSound( "sf15.Merasmus.Gargoyle.Spawn" );
|
||||
}
|
||||
// Gift Despawned
|
||||
if ( m_hTargetPlayer.Get() == NULL && m_pPreviousTargetPlayer != NULL && m_pPreviousTargetPlayer == pLocalPlayer )
|
||||
{
|
||||
// Notification
|
||||
CEconNotification *pNotification = new CEconNotification();
|
||||
pNotification->SetText( "#TF_HalloweenItem_SoulDisappeared" );
|
||||
pNotification->SetLifetime( 5.0f );
|
||||
pNotification->SetSoundFilename( "ui/halloween_loot_found.wav" );
|
||||
NotificationQueue_Add( pNotification );
|
||||
pLocalPlayer->EmitSound( "sf15.Merasmus.Gargoyle.Gone" );
|
||||
}
|
||||
|
||||
m_pPreviousTargetPlayer = m_hTargetPlayer.Get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
bool CHalloweenGiftPickup::ShouldDraw()
|
||||
{
|
||||
CTFPlayer *pOwner = m_hTargetPlayer.Get();
|
||||
if ( pOwner != C_TFPlayer::GetLocalTFPlayer() )
|
||||
return false;
|
||||
|
||||
return BaseClass::ShouldDraw();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF Halloween Pickup.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef ENTITY_HALLOWEEN_PICKUP_H
|
||||
#define ENTITY_HALLOWEEN_PICKUP_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_powerup.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_player.h"
|
||||
#endif
|
||||
|
||||
#include "ehandle.h"
|
||||
|
||||
#define TF_HALLOWEEN_PICKUP_MODEL "models/items/target_duck.mdl"
|
||||
#define TF_DUCK_PICKUP_MODEL "models/workshop/player/items/pyro/eotl_ducky/eotl_bonus_duck.mdl"
|
||||
#define TF_GIFT_MODEL "models/props_halloween/gargoyle_ghost.mdl"; //"models/props_halloween/halloween_gift.mdl";
|
||||
#define TF_HALLOWEEN_PICKUP_DEFAULT_SOUND "AmmoPack.Touch"
|
||||
|
||||
#define BONUS_DUCK_GLOW "superrare_beams1"
|
||||
#define BONUS_DUCK_TRAIL_RED "duck_collect_trail_red"
|
||||
#define BONUS_DUCK_TRAIL_BLUE "duck_collect_trail_blue"
|
||||
#define BONUS_DUCK_TRAIL_SPECIAL_RED "duck_collect_trail_special_red"
|
||||
#define BONUS_DUCK_TRAIL_SPECIAL_BLUE "duck_collect_trail_special_blue"
|
||||
#define BONUS_DUCK_CREATED_SOUND "Duck.Quack"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CBonusDuckPickup C_BonusDuckPickup
|
||||
#define CHalloweenPickup C_HalloweenPickup
|
||||
#define CHalloweenGiftPickup C_HalloweenGiftPickup
|
||||
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTF Halloween Pickup class.
|
||||
//
|
||||
|
||||
class CHalloweenPickup
|
||||
#ifdef GAME_DLL
|
||||
: public CTFPowerup
|
||||
#else
|
||||
: public C_BaseAnimating
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_CLASS( CHalloweenPickup, CTFPowerup );
|
||||
#else
|
||||
DECLARE_CLASS( CHalloweenPickup, C_BaseAnimating );
|
||||
#endif
|
||||
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CHalloweenPickup();
|
||||
~CHalloweenPickup();
|
||||
|
||||
virtual void Precache( void ) OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual int UpdateTransmitState() OVERRIDE;
|
||||
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo ) OVERRIDE;
|
||||
virtual bool ValidTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
virtual bool MyTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
virtual CBaseEntity* Respawn( void );
|
||||
|
||||
virtual const char *GetDefaultPowerupModel( void ) OVERRIDE
|
||||
{
|
||||
return TF_HALLOWEEN_PICKUP_MODEL;
|
||||
}
|
||||
|
||||
virtual float GetRespawnDelay( void ) OVERRIDE;
|
||||
|
||||
virtual bool ItemCanBeTouchedByPlayer( CBasePlayer *pPlayer );
|
||||
#endif // GAME_DLL
|
||||
|
||||
private:
|
||||
string_t m_iszSound;
|
||||
string_t m_iszParticle;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
COutputEvent m_OnRedPickup;
|
||||
COutputEvent m_OnBluePickup;
|
||||
#endif
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
class CBonusDuckPickup : public CHalloweenPickup
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CBonusDuckPickup, CHalloweenPickup );
|
||||
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CBonusDuckPickup();
|
||||
~CBonusDuckPickup();
|
||||
|
||||
virtual void Precache( void ) OVERRIDE;
|
||||
#ifdef GAME_DLL
|
||||
virtual const char *GetDefaultPowerupModel( void ) OVERRIDE
|
||||
{
|
||||
return TF_DUCK_PICKUP_MODEL;
|
||||
}
|
||||
|
||||
virtual float GetLifeTime() { if ( m_flLifeTime == 0) { m_flLifeTime = RandomFloat( 17.0f, 20.0f ); } return m_flLifeTime; }
|
||||
|
||||
virtual bool ValidTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
void Spawn( void );
|
||||
virtual bool MyTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
|
||||
void DropSingleInstance( Vector &vecLaunchVel, CBaseCombatCharacter *pThrower, float flThrowerTouchDelay, float flResetTime = 0.1f );
|
||||
void NotifyFadeOut( void );
|
||||
|
||||
void UpdateCollisionBounds();
|
||||
|
||||
// Make this a base class in powerup
|
||||
void BlinkThink();
|
||||
|
||||
void SetCreatorId( int value ) { m_iCreatorId = value; }
|
||||
int GetCreatorId( void ) { return m_iCreatorId; }
|
||||
|
||||
void SetAssisterId( int value ) { m_iAssisterId = value; }
|
||||
int GetAssisterId( void ) { return m_iAssisterId; }
|
||||
|
||||
void SetVictimId( int value ) { m_iVictimId = value; }
|
||||
int GetVictimId( void ) { return m_iVictimId; }
|
||||
|
||||
void SetSpecial( void ){ m_bSpecial = true; }
|
||||
void SetDuckFlag( int iFlag ) { m_iFlags |= iFlag; }
|
||||
#else
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType ) OVERRIDE;
|
||||
#endif // GAME_DLL
|
||||
|
||||
private:
|
||||
string_t m_iszSound;
|
||||
string_t m_iszParticle;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
float m_flLifeTime;
|
||||
float m_flKillTime;
|
||||
int m_nBlinkCount;
|
||||
int m_iCreatorId;
|
||||
int m_iAssisterId;
|
||||
int m_iVictimId;
|
||||
int m_iFlags;
|
||||
#else
|
||||
CNewParticleEffect *pGlowEffect;
|
||||
#endif
|
||||
|
||||
CNetworkVar( bool, m_bSpecial );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//----------------------------------------------------------------------------
|
||||
DECLARE_AUTO_LIST( IHalloweenGiftSpawnAutoList );
|
||||
|
||||
//*************************************************************************************************
|
||||
// Dumb entity that is placed in Hammer.
|
||||
// On Map load, server finds all the locations and makes note then deletes the entity
|
||||
class CHalloweenGiftSpawnLocation : public CBaseEntity, public IHalloweenGiftSpawnAutoList
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHalloweenGiftSpawnLocation, CBaseEntity );
|
||||
|
||||
CHalloweenGiftSpawnLocation();
|
||||
};
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
//*************************************************************************************************
|
||||
// Networked Entity that represents a gift. Only visible and 'touchable' by the intended target
|
||||
// Has a lifetime
|
||||
// A server can spawn multiple of these for different people or the same person but each gift has a single target
|
||||
class CHalloweenGiftPickup : public CHalloweenPickup
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHalloweenGiftPickup, CHalloweenPickup );
|
||||
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CHalloweenGiftPickup();
|
||||
//~CHalloweenGiftPickup();
|
||||
|
||||
virtual void Precache( void ) OVERRIDE;
|
||||
void Spawn( void );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void SetTargetPlayer( CTFPlayer *pTarget ); // Must be called before spawn
|
||||
void DespawnGift();
|
||||
void RemoveGift();
|
||||
|
||||
virtual const char *GetDefaultPowerupModel( void ) OVERRIDE
|
||||
{
|
||||
return TF_GIFT_MODEL;
|
||||
}
|
||||
|
||||
//virtual float GetLifeTime() { if ( m_flLifeTime == 0 ) { m_flLifeTime = RandomFloat( 17.0f, 20.0f ); } return m_flLifeTime; }
|
||||
virtual bool ValidTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
virtual bool MyTouch( CBasePlayer *pPlayer ) OVERRIDE;
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool ShouldDraw();
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType ) OVERRIDE;
|
||||
|
||||
CTFPlayer *m_pPreviousTargetPlayer;
|
||||
#endif
|
||||
|
||||
CNetworkVar( CHandle<CTFPlayer>, m_hTargetPlayer );
|
||||
|
||||
DECLARE_DATADESC();
|
||||
};
|
||||
|
||||
|
||||
|
||||
//*************************************************************************************************
|
||||
|
||||
|
||||
#endif // ENTITY_HALLOWEEN_PICKUP_H
|
||||
|
||||
|
||||
@@ -0,0 +1,611 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "teleport_vortex.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
#include "c_tf_fx.h"
|
||||
|
||||
inline float SCurve( float t )
|
||||
{
|
||||
t = clamp( t, 0.0f, 1.0f );
|
||||
return t * t * (3 - 2*t);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "tf_fx.h"
|
||||
#include "tf_team.h"
|
||||
#include "tf_weapon_sniperrifle.h"
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConVar vortex_float_osc_speed( "vortex_float_osc_speed", "2.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
ConVar vortex_float_amp( "vortex_float_amp", "5.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
ConVar vortex_fade_fraction_denom( "vortex_fade_fraction_denom", "10.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
ConVar vortex_book_offset( "vortex_book_offset", "5.0", FCVAR_HIDDEN | FCVAR_CHEAT | FCVAR_REPLICATED );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TeleportVortex, DT_TeleportVortex )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTeleportVortex, DT_TeleportVortex )
|
||||
#if defined( CLIENT_DLL )
|
||||
RecvPropInt( RECVINFO( m_iState ) ),
|
||||
#else
|
||||
SendPropInt( SENDINFO( m_iState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( teleport_vortex, CTeleportVortex );
|
||||
|
||||
BEGIN_DATADESC( CTeleportVortex )
|
||||
END_DATADESC()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED "eyeboss_tp_vortex"
|
||||
#define VORTEX_PARTICLE_EFFECT_EYEBALL_DIED "eyeboss_aura_angry"
|
||||
|
||||
#define VORTEX_BOOK_MODEL "models/props_halloween/bombonomicon.mdl"
|
||||
|
||||
#define VORTEX_SOUND_EYEBALL_MOVED "Halloween.TeleportVortex.EyeballMovedVortex"
|
||||
#define VORTEX_SOUND_EYEBALL_DIED "Halloween.TeleportVortex.EyeballDiedVortex"
|
||||
#define VORTEX_SOUND_BOOK_SPAWN "Halloween.TeleportVortex.BookSpawn"
|
||||
#define VORTEX_SOUND_BOOK_EXIT "Halloween.TeleportVortex.BookExit"
|
||||
|
||||
#define VORTEX_OPEN_OPEN_ANIM "flip_stimulated"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CTeleportVortex::CTeleportVortex()
|
||||
#ifdef GAME_DLL
|
||||
: m_iAutoSetupVortex( -1 )
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
CTeleportVortex::~CTeleportVortex()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
DestroyParticleEffect();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void CTeleportVortex::DestroyParticleEffect()
|
||||
{
|
||||
if ( m_pVortexEffect )
|
||||
{
|
||||
ParticleProp()->StopEmission( m_pVortexEffect );
|
||||
m_pVortexEffect = NULL;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool CTeleportVortex::KeyValue( const char *szKeyname, const char *szValue )
|
||||
{
|
||||
if ( !V_strnicmp( szKeyname, "type", 4 ) )
|
||||
{
|
||||
m_iAutoSetupVortex = atoi( szValue );
|
||||
}
|
||||
else
|
||||
{
|
||||
return BaseClass::KeyValue( szKeyname, szValue );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CTeleportVortex::Spawn( void )
|
||||
{
|
||||
Precache();
|
||||
BaseClass::Spawn();
|
||||
|
||||
AddEffects( EF_NODRAW );
|
||||
SetMoveType( MOVETYPE_NONE );
|
||||
SetSolid( SOLID_BBOX );
|
||||
CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) );
|
||||
SetModelName( NULL_STRING );
|
||||
SetSolidFlags( FSOLID_TRIGGER );
|
||||
SetCollisionGroup( COLLISION_GROUP_WEAPON );
|
||||
SetRenderMode( kRenderTransAlpha );
|
||||
SetRenderColorA( 255 );
|
||||
UseClientSideAnimation();
|
||||
|
||||
m_lifeTimer.Start( 5.0f );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
m_flScale = 1.0f;
|
||||
m_iOldState = VORTEXSTATE_INACTIVE;
|
||||
m_pVortexEffect = NULL;
|
||||
|
||||
ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
|
||||
|
||||
AddToLeafSystem( RENDER_GROUP_TWOPASS );
|
||||
#else
|
||||
// Default to purgatory
|
||||
m_pszWhere = "spawn_purgatory";
|
||||
|
||||
SetThink( &CTeleportVortex::VortexThink );
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
|
||||
m_nSoundCounter = 0;
|
||||
|
||||
m_bSplitTeam = false;
|
||||
|
||||
if ( m_iAutoSetupVortex >= 0 )
|
||||
{
|
||||
SetupVortex( m_iAutoSetupVortex != 0 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::Precache( void )
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( VORTEX_BOOK_MODEL );
|
||||
|
||||
// We deliberately allow late precaches here.
|
||||
bool bAllowPrecache = CBaseAnimating::IsPrecacheAllowed();
|
||||
CBaseAnimating::SetAllowPrecache( true );
|
||||
|
||||
#if GAME_DLL
|
||||
PrecacheScriptSound( VORTEX_SOUND_EYEBALL_MOVED );
|
||||
PrecacheScriptSound( VORTEX_SOUND_EYEBALL_DIED );
|
||||
PrecacheScriptSound( VORTEX_SOUND_BOOK_SPAWN );
|
||||
PrecacheScriptSound( VORTEX_SOUND_BOOK_EXIT );
|
||||
#endif
|
||||
|
||||
PrecacheParticleSystem( VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED );
|
||||
PrecacheParticleSystem( VORTEX_PARTICLE_EFFECT_EYEBALL_DIED );
|
||||
|
||||
CBaseAnimating::SetAllowPrecache( bAllowPrecache );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
void CTeleportVortex::Touch( CBaseEntity *pOther )
|
||||
{
|
||||
BaseClass::Touch( pOther );
|
||||
|
||||
if ( pOther && pOther->IsPlayer() )
|
||||
{
|
||||
if ( m_bSplitTeam )
|
||||
{
|
||||
const char* pszTeam = pOther->GetTeamNumber() == TF_TEAM_RED ? "_red" : "_blue";
|
||||
SendPlayerToTheUnderworld( ToTFPlayer( pOther ), CFmtStr( "%s%s", m_pszWhere.Get(), pszTeam ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
SendPlayerToTheUnderworld( ToTFPlayer( pOther ), m_pszWhere );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int CTeleportVortex::UpdateTransmitState( void )
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_PVSCHECK );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::StartTouch( CBaseEntity *pOther )
|
||||
{
|
||||
CBaseAnimating::StartTouch( pOther );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::SetupVortex( bool bIsDeathVortex, bool bSplitTeam /*= false*/ )
|
||||
{
|
||||
m_bSplitTeam = bSplitTeam;
|
||||
if ( bIsDeathVortex )
|
||||
{
|
||||
m_iState = VORTEXSTATE_ACTIVE_EYEBALL_DIED;
|
||||
m_pszWhere = "spawn_loot";
|
||||
RemoveEffects( EF_NODRAW );
|
||||
SetModel( VORTEX_BOOK_MODEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iState = VORTEXSTATE_ACTIVE_EYEBALL_MOVED;
|
||||
m_pszWhere = "spawn_purgatory";
|
||||
AddEffects( EF_NODRAW );
|
||||
}
|
||||
}
|
||||
|
||||
void CTeleportVortex::VortexThink()
|
||||
{
|
||||
if ( m_lifeTimer.IsElapsed() )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
StudioFrameAdvance();
|
||||
|
||||
if ( m_iState == VORTEXSTATE_ACTIVE_EYEBALL_DIED )
|
||||
{
|
||||
if ( m_nSoundCounter == 0 && ShouldDoBookRampIn() )
|
||||
{
|
||||
EmitSound( VORTEX_SOUND_BOOK_SPAWN );
|
||||
++m_nSoundCounter;
|
||||
}
|
||||
else if ( m_nSoundCounter == 1 && ShouldDoBookRampOut() )
|
||||
{
|
||||
EmitSound( VORTEX_SOUND_BOOK_EXIT );
|
||||
++m_nSoundCounter;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime );
|
||||
}
|
||||
|
||||
// suck nearby players into the vortex
|
||||
CUtlVector< CTFPlayer * > playerVector;
|
||||
CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
|
||||
CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
|
||||
|
||||
const float suctionRange = 500.0f;
|
||||
|
||||
for( int i=0; i<playerVector.Count(); ++i )
|
||||
{
|
||||
CTFPlayer *player = playerVector[i];
|
||||
|
||||
if ( player->GetFlags() & FL_ONGROUND )
|
||||
{
|
||||
// not airborne
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector toVortex = WorldSpaceCenter() - player->WorldSpaceCenter();
|
||||
float range = toVortex.NormalizeInPlace();
|
||||
|
||||
if ( range > suctionRange )
|
||||
{
|
||||
// too far
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( player->IsLookingTowards( WorldSpaceCenter() ) )
|
||||
{
|
||||
if ( player->IsLineOfSightClear( WorldSpaceCenter(), CBaseCombatCharacter::IGNORE_ACTORS, player ) )
|
||||
{
|
||||
player->ApplyAbsVelocityImpulse( 30.0f * toVortex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
void SendPlayerToTheUnderworld( CTFPlayer *teleportingPlayer, const char *where )
|
||||
{
|
||||
if ( !teleportingPlayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CUtlVector< CBaseEntity * > spawnVector;
|
||||
|
||||
CBaseEntity *spawnPoint = NULL;
|
||||
while( ( spawnPoint = gEntList.FindEntityByClassname( spawnPoint, "info_target" ) ) != NULL )
|
||||
{
|
||||
if ( FStrEq( STRING( spawnPoint->GetEntityName() ), where ) )
|
||||
{
|
||||
spawnVector.AddToTail( spawnPoint );
|
||||
}
|
||||
}
|
||||
|
||||
if ( spawnVector.Count() == 0 )
|
||||
{
|
||||
Warning( "SendPlayerToTheUnderworld: No info_target entities named '%s' found!\n", where );
|
||||
return;
|
||||
}
|
||||
|
||||
// collect enemies that could block our spawning
|
||||
CUtlVector< CTFPlayer * > enemyVector;
|
||||
CollectPlayers( &enemyVector, GetEnemyTeam( teleportingPlayer->GetTeamNumber() ), COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
const float nearRange = 25.0f;
|
||||
|
||||
// collect spots without players overlapping them
|
||||
CUtlVector< CBaseEntity * > openSpawnVector;
|
||||
for( int i=0; i<spawnVector.Count(); ++i )
|
||||
{
|
||||
int p;
|
||||
|
||||
for( p=0; p<enemyVector.Count(); ++p )
|
||||
{
|
||||
if ( ( spawnVector[i]->GetAbsOrigin() - enemyVector[p]->GetAbsOrigin() ).IsLengthLessThan( nearRange ) )
|
||||
{
|
||||
// a player is occupying this spawn
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( p == enemyVector.Count() )
|
||||
{
|
||||
// no players are near this spawn point
|
||||
openSpawnVector.AddToTail( spawnVector[i] );
|
||||
}
|
||||
}
|
||||
|
||||
CBaseEntity *teleportDestination = NULL;
|
||||
|
||||
if ( openSpawnVector.Count() == 0 )
|
||||
{
|
||||
// there are no free spawns - pick one and telefrag enemies standing there
|
||||
int which = RandomInt( 0, spawnVector.Count()-1 );
|
||||
|
||||
teleportDestination = spawnVector[ which ];
|
||||
|
||||
for( int p=0; p<enemyVector.Count(); ++p )
|
||||
{
|
||||
if ( ( teleportDestination->GetAbsOrigin() - enemyVector[p]->GetAbsOrigin() ).IsLengthLessThan( nearRange ) )
|
||||
{
|
||||
// telefrag!
|
||||
enemyVector[p]->TakeDamage( CTakeDamageInfo( teleportingPlayer, teleportingPlayer, 1000, DMG_CRUSH, TF_DMG_CUSTOM_TELEFRAG ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pick an open destination at random
|
||||
int which = RandomInt( 0, openSpawnVector.Count()-1 );
|
||||
|
||||
teleportDestination = openSpawnVector[ which ];
|
||||
}
|
||||
|
||||
if ( teleportDestination )
|
||||
{
|
||||
if ( teleportingPlayer->GetTeam() )
|
||||
{
|
||||
UTIL_LogPrintf( "HALLOWEEN: \"%s<%i><%s><%s>\" purgatory_teleport \"%s\"\n",
|
||||
teleportingPlayer->GetPlayerName(),
|
||||
teleportingPlayer->GetUserID(),
|
||||
teleportingPlayer->GetNetworkIDString(),
|
||||
teleportingPlayer->GetTeam()->GetName(),
|
||||
where );
|
||||
}
|
||||
|
||||
teleportingPlayer->Teleport( &teleportDestination->GetAbsOrigin(), &teleportDestination->GetAbsAngles(), &vec3_origin );
|
||||
|
||||
// When fighting Merasmus, give players full health on teleport
|
||||
if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_LAKESIDE ) )
|
||||
{
|
||||
if ( teleportingPlayer->IsAlive() )
|
||||
{
|
||||
teleportingPlayer->TakeHealth( teleportingPlayer->GetMaxHealth(), DMG_GENERIC );
|
||||
teleportingPlayer->m_Shared.HealthKitPickupEffects();
|
||||
teleportingPlayer->m_Shared.RemoveCond( TF_COND_HALLOWEEN_BOMB_HEAD );
|
||||
}
|
||||
}
|
||||
|
||||
if ( FStrEq( where, "spawn_loot" ) )
|
||||
{
|
||||
CReliableBroadcastRecipientFilter filter;
|
||||
UTIL_SayText2Filter( filter, teleportingPlayer, false, "#TF_Halloween_Loot_Island", teleportingPlayer->GetPlayerName() );
|
||||
}
|
||||
|
||||
teleportingPlayer->m_Shared.InstantlySniperUnzoom();
|
||||
|
||||
color32 fadeColor = { 255, 255, 255, 100 };
|
||||
UTIL_ScreenFade( teleportingPlayer, fadeColor, 0.25, 0.4, FFADE_IN );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::ClientThink()
|
||||
{
|
||||
// Fade in and out for first and last fraction of time
|
||||
const float flDuration = m_lifeTimer.GetCountdownDuration();
|
||||
const float flRampTime = flDuration / vortex_fade_fraction_denom.GetInt();
|
||||
const float flElapsed = m_lifeTimer.GetElapsedTime();
|
||||
|
||||
float t = 1.0f;
|
||||
if ( ShouldDoBookRampIn() )
|
||||
{
|
||||
t = SCurve( flElapsed / flRampTime );
|
||||
}
|
||||
else if ( ShouldDoBookRampOut() )
|
||||
{
|
||||
t = SCurve( 1.0f - ( flElapsed - flDuration + flRampTime ) / flRampTime );
|
||||
}
|
||||
|
||||
// SetRenderColorA( t * 255 );
|
||||
|
||||
m_flScale = t;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
|
||||
{
|
||||
// Translate root bone towards the player and oscillate a bit - if in the scale in/out period, translate up directly from the underworld, or down towards it.
|
||||
const Vector vecBob( vortex_book_offset.GetFloat(), 0.0f, vortex_float_amp.GetFloat() * sinf( vortex_float_osc_speed.GetFloat() * gpGlobals->curtime ) );
|
||||
pos[0] = vecBob + Vector( 0.0f, 0.0f, Lerp( m_flScale, -500.0f, 0.0f ) );
|
||||
|
||||
matrix3x4_t mRootBoneRotation;
|
||||
|
||||
// If the local player exists and is alive, render the book so that it's facing him
|
||||
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
|
||||
if ( pLocalPlayer && pLocalPlayer->IsAlive() )
|
||||
{
|
||||
const Vector vecBook = GetAbsOrigin();
|
||||
const Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
|
||||
Vector vecForward = vecPlayerPos - vecBook;
|
||||
if ( VectorNormalize( vecForward ) > 0.1f )
|
||||
{
|
||||
// Calculate a matrix based on the forward direction
|
||||
VectorMatrix( vecForward, mRootBoneRotation );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use whatever rotation is in the root bone already
|
||||
QuaternionMatrix( q[0], mRootBoneRotation );
|
||||
}
|
||||
|
||||
// Convert the matrix to a quaternion and slam the root bone rotation
|
||||
MatrixQuaternion( mRootBoneRotation, q[0] );
|
||||
|
||||
// Let the base class actually build the global transforms
|
||||
BaseClass::BuildTransformations( pStudioHdr, pos, q, cameraTransform, boneMask, boneComputed );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::PlayBookAnimation( const char *pAnimName )
|
||||
{
|
||||
int iAnimSequence = LookupSequence( pAnimName );
|
||||
if ( iAnimSequence )
|
||||
{
|
||||
SetSequence( iAnimSequence );
|
||||
SetPlaybackRate( 1.0f );
|
||||
SetCycle( 0 );
|
||||
ResetSequenceInfo();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void CTeleportVortex::OnDataChanged(DataUpdateType_t updateType)
|
||||
{
|
||||
BaseClass::OnDataChanged( updateType );
|
||||
|
||||
if ( m_iState != m_iOldState )
|
||||
{
|
||||
if ( m_iState != VORTEXSTATE_INACTIVE )
|
||||
{
|
||||
// Stop any existing particle effect if necessary
|
||||
AssertMsg( !m_pVortexEffect, "Particle effect should not be active!" );
|
||||
|
||||
// Create the particle effect and play a sound
|
||||
const char *pszEffectName;
|
||||
const char *pszSound;
|
||||
if ( m_iState == VORTEXSTATE_ACTIVE_EYEBALL_MOVED )
|
||||
{
|
||||
pszEffectName = VORTEX_PARTICLE_EFFECT_EYEBALL_MOVED;
|
||||
pszSound = VORTEX_SOUND_EYEBALL_MOVED;
|
||||
}
|
||||
else
|
||||
{
|
||||
pszEffectName = VORTEX_PARTICLE_EFFECT_EYEBALL_DIED;
|
||||
pszSound = VORTEX_SOUND_EYEBALL_DIED;
|
||||
|
||||
PlayBookAnimation( VORTEX_OPEN_OPEN_ANIM );
|
||||
}
|
||||
m_pVortexEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN );
|
||||
EmitSound( pszSound );
|
||||
}
|
||||
|
||||
m_iOldState = m_iState;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
float CTeleportVortex::GetRampTime()
|
||||
{
|
||||
const float flDuration = m_lifeTimer.GetCountdownDuration();
|
||||
return flDuration / vortex_fade_fraction_denom.GetInt();
|
||||
}
|
||||
|
||||
bool CTeleportVortex::ShouldDoBookRampIn()
|
||||
{
|
||||
return m_lifeTimer.GetElapsedTime() <= GetRampTime();
|
||||
}
|
||||
|
||||
bool CTeleportVortex::ShouldDoBookRampOut()
|
||||
{
|
||||
const float flDuration = m_lifeTimer.GetCountdownDuration();
|
||||
return m_lifeTimer.GetElapsedTime() >= flDuration - GetRampTime();
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CHightower_TeleportVortex C_Hightower_TeleportVortex
|
||||
#endif
|
||||
class CHightower_TeleportVortex : public CTeleportVortex
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_CLASS( CHightower_TeleportVortex, CTeleportVortex );
|
||||
|
||||
public:
|
||||
virtual void Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
#ifdef GAME_DLL
|
||||
m_nWinningTeam = TF_TEAM_COUNT; // Invalid
|
||||
SetupVortex( false, false );
|
||||
m_lifeTimer.Start( m_flDuration );
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void Touch( CBaseEntity *pOther )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
const char *pszTarget = CFmtStr( "%s_%s", m_pszDestinationBaseName, ( pOther->GetTeamNumber() == m_nWinningTeam ) ? "winner" : "loser" );
|
||||
SendPlayerToTheUnderworld( ToTFPlayer( pOther ), pszTarget );
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void SetAdvantageTeam( inputdata_t &inputdata )
|
||||
{
|
||||
m_nWinningTeam = FStrEq( inputdata.value.String(), "red" ) ? TF_TEAM_RED : TF_TEAM_BLUE;
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
|
||||
#ifdef GAME_DLL
|
||||
int m_nWinningTeam;
|
||||
const char* m_pszDestinationBaseName;
|
||||
float m_flDuration;
|
||||
#endif
|
||||
};
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( Hightower_TeleportVortex, DT_Hightower_TeleportVortex )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CHightower_TeleportVortex, DT_Hightower_TeleportVortex )
|
||||
#if defined( CLIENT_DLL )
|
||||
RecvPropInt( RECVINFO( m_iState ) ),
|
||||
#else
|
||||
SendPropInt( SENDINFO( m_iState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
LINK_ENTITY_TO_CLASS( hightower_teleport_vortex, CHightower_TeleportVortex );
|
||||
|
||||
BEGIN_DATADESC( CHightower_TeleportVortex )
|
||||
#ifdef GAME_DLL
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetAdvantageTeam", SetAdvantageTeam ),
|
||||
DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "lifetime" ),
|
||||
DEFINE_KEYFIELD( m_pszDestinationBaseName, FIELD_STRING, "target_base_name" ),
|
||||
#endif
|
||||
END_DATADESC()
|
||||
@@ -0,0 +1,97 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Teleport vortex for the Eyeball Boss
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef TELEPORT_VORTEX_H
|
||||
#define TELEPORT_VORTEX_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTeleportVortex C_TeleportVortex
|
||||
|
||||
#include "c_baseanimating.h"
|
||||
#else
|
||||
#include "baseanimating.h"
|
||||
#endif
|
||||
|
||||
enum EVortexState
|
||||
{
|
||||
VORTEXSTATE_INACTIVE,
|
||||
VORTEXSTATE_ACTIVE_EYEBALL_MOVED,
|
||||
VORTEXSTATE_ACTIVE_EYEBALL_DIED,
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Teleport vortex for the Eyeball Boss
|
||||
//
|
||||
class CTeleportVortex : public CBaseAnimating
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_CLASS( CTeleportVortex, CBaseAnimating );
|
||||
|
||||
public:
|
||||
CTeleportVortex();
|
||||
virtual ~CTeleportVortex();
|
||||
|
||||
virtual void Spawn( void );
|
||||
virtual void Precache( void );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void PlayBookAnimation( const char *pAnimName );
|
||||
|
||||
virtual void OnDataChanged(DataUpdateType_t updateType);
|
||||
virtual void ClientThink();
|
||||
virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed );
|
||||
|
||||
void DestroyParticleEffect();
|
||||
#else
|
||||
void SetupVortex( bool bIsDeathVortex, bool bSplitTeam = false );
|
||||
void VortexThink();
|
||||
|
||||
virtual bool KeyValue( const char *szKeyName, const char *szValue );
|
||||
virtual int UpdateTransmitState( void );
|
||||
virtual void StartTouch( CBaseEntity *pOther );
|
||||
virtual void Touch( CBaseEntity *pOther );
|
||||
|
||||
void SetAdvantageTeam( inputdata_t &inputdata );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
float GetRampTime();
|
||||
bool ShouldDoBookRampIn();
|
||||
bool ShouldDoBookRampOut();
|
||||
|
||||
CountdownTimer m_lifeTimer;
|
||||
|
||||
CNetworkVar( int, m_iState );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
CNewParticleEffect *m_pVortexEffect;
|
||||
float m_flScale;
|
||||
int m_iOldState; // This seems like it should not be necessary, but I'm getting OnDataChanged() called when m_iState has not actually changed, and updateType will be DATA_UPDATE_DATATABLE_CHANGED multiple times in a row. Presumably this is because the position is being set explicitly on the server.
|
||||
#else
|
||||
int m_iAutoSetupVortex;
|
||||
int m_nSoundCounter; // Count the number of sounds played
|
||||
CFmtStr m_pszWhere;
|
||||
bool m_bSplitTeam;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifndef CLIENT_DLL
|
||||
|
||||
void SendPlayerToTheUnderworld( CTFPlayer *teleportingPlayer, const char *where );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // TELEPORT_VORTEX_H
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_WEAPON_SPELLBOOK_H
|
||||
#define TF_WEAPON_SPELLBOOK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "GameEventListener.h"
|
||||
#include "tf_weapon_jar.h"
|
||||
#include "tf_weapon_throwable.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "tf_viewmodel.h"
|
||||
#include "econ_item_view.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include <vgui_controls/EditablePanel.h>
|
||||
#include "hudelement.h"
|
||||
#include "econ_controls.h"
|
||||
#include "c_tf_projectile_rocket.h"
|
||||
#include "econ_notifications.h"
|
||||
#include "vgui_controls/ImagePanel.h"
|
||||
|
||||
#define CTFSpellBook C_TFSpellBook
|
||||
#define CTFProjectile_SpellFireball C_TFProjectile_SpellFireball
|
||||
#define CTFProjectile_SpellBats C_TFProjectile_SpellBats
|
||||
#define CTFProjectile_SpellSpawnZombie C_TFProjectile_SpellSpawnZombie
|
||||
#define CTFProjectile_SpellSpawnHorde C_TFProjectile_SpellSpawnHorde
|
||||
#define CTFProjectile_SpellMirv C_TFProjectile_SpellMirv
|
||||
#define CTFProjectile_SpellPumpkin C_TFProjectile_SpellPumpkin
|
||||
|
||||
#define CTFProjectile_SpellSpawnBoss C_TFProjectile_SpellSpawnBoss
|
||||
#define CTFProjectile_SpellMeteorShower C_TFProjectile_SpellMeteorShower
|
||||
#define CTFProjectile_SpellTransposeTeleport C_TFProjectile_SpellTransposeTeleport
|
||||
#define CTFProjectile_SpellLightningOrb C_TFProjectile_SpellLightningOrb
|
||||
#define CTFProjectile_SpellVortex C_TFProjectile_SpellVortex
|
||||
|
||||
#define CTFProjectile_SpellKartOrb C_TFProjectile_SpellKartOrb
|
||||
#define CTFProjectile_SpellKartBats C_TFProjectile_SpellKartBats
|
||||
#define CTFProjectile_SpellKartMirv C_TFProjectile_SpellKartMirv
|
||||
#define CTFProjectile_SpellKartPumpkin C_TFProjectile_SpellKartPumpkin
|
||||
#else
|
||||
#include "tf_projectile_rocket.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
// For testing, hijack this basic menu but replace it later with TF specific UI
|
||||
class CHudSpellMenu : public CHudElement, public vgui::EditablePanel
|
||||
{
|
||||
DECLARE_CLASS_SIMPLE( CHudSpellMenu, EditablePanel );
|
||||
public:
|
||||
CHudSpellMenu( const char *pElementName );
|
||||
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
|
||||
virtual bool ShouldDraw( void );
|
||||
virtual void FireGameEvent( IGameEvent *event ) OVERRIDE;
|
||||
virtual void OnTick( void ) OVERRIDE;
|
||||
|
||||
void UpdateSpellText( int iSpellIndex, int iCharges );
|
||||
|
||||
private:
|
||||
vgui::ImagePanel *m_pSpellIcon;
|
||||
CExLabel *m_pKeyBinding;
|
||||
|
||||
int m_iPrevSelectedSpell;
|
||||
float m_iNextRollTime;
|
||||
float m_flRollTickGap;
|
||||
bool m_bTickSoundA;
|
||||
|
||||
bool m_bKillstreakMeterDrawing;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class CEquipSpellbookNotification : public CEconNotification
|
||||
{
|
||||
public:
|
||||
CEquipSpellbookNotification() : CEconNotification()
|
||||
{
|
||||
m_bHasTriggered = false;
|
||||
}
|
||||
|
||||
~CEquipSpellbookNotification()
|
||||
{
|
||||
if ( !m_bHasTriggered )
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void MarkForDeletion()
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
CEconNotification::MarkForDeletion();
|
||||
}
|
||||
|
||||
virtual EType NotificationType() { return eType_AcceptDecline; }
|
||||
virtual bool BShowInGameElements() const { return true; }
|
||||
|
||||
virtual void Accept();
|
||||
virtual void Trigger() { Accept(); }
|
||||
virtual void Decline() { MarkForDeletion(); }
|
||||
virtual void UpdateTick();
|
||||
|
||||
static bool IsNotificationType( CEconNotification *pNotification ) { return dynamic_cast< CEquipSpellbookNotification *>( pNotification ) != NULL; }
|
||||
|
||||
private:
|
||||
bool m_bHasTriggered;
|
||||
};
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void RemoveAll2013HalloweenTeleportSpellsInMidFlight( void );
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CTFSpellBook class.
|
||||
//
|
||||
class CTFSpellBook : public CTFThrowable
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFSpellBook, CTFThrowable );
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_PREDICTABLE();
|
||||
|
||||
CTFSpellBook();
|
||||
virtual int GetWeaponID( void ) const { return TF_WEAPON_SPELLBOOK; }
|
||||
virtual const char* GetEffectLabelText( void ) { return "#TF_KART"; }
|
||||
virtual void Precache( void );
|
||||
|
||||
virtual void PrimaryAttack();
|
||||
virtual void ItemPostFrame( void );
|
||||
|
||||
virtual void ItemBusyFrame( void );
|
||||
virtual void ItemHolsterFrame( void );
|
||||
|
||||
virtual bool ShowHudElement () { return false; }
|
||||
virtual bool VisibleInWeaponSelection( void ) { return false; }
|
||||
virtual bool CanBeSelected( void ) { return false; }
|
||||
|
||||
bool HasASpellWithCharges();
|
||||
|
||||
virtual CBaseEntity *FireJar( CTFPlayer *pPlayer ) OVERRIDE;
|
||||
|
||||
bool CanCastSpell( CTFPlayer *pPlayer );
|
||||
void PaySpellCost( CTFPlayer *pPlayer );
|
||||
void ClearSpell();
|
||||
|
||||
// Hack for infinite ammo
|
||||
virtual bool IsEnergyWeapon( void ) const { return true; }
|
||||
float Energy_GetMaxEnergy( void ) const { return 500; }
|
||||
float Energy_GetEnergy( void ) const { return 500; }
|
||||
bool Energy_FullyCharged( void ) const { return true; }
|
||||
bool Energy_HasEnergy( void ) { return true; }
|
||||
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void SaveLastWeapon( CBaseCombatWeapon *pWpn ) { m_pStoredLastWpn = pWpn; }
|
||||
|
||||
// Projectile Creation
|
||||
virtual void TossJarThink( void );
|
||||
virtual void CreateSpellRocket( const Vector &position, const QAngle &angles, const Vector &velocity,
|
||||
const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo );
|
||||
virtual void CreateSpellJar( const Vector &position, const QAngle &angles, const Vector &velocity,
|
||||
const AngularImpulse &angVelocity, CBaseCombatCharacter *pOwner, const CTFWeaponInfo &weaponInfo );
|
||||
|
||||
// Spell Helpers
|
||||
// Think
|
||||
void RollNewSpell( int iTier, bool bForceReroll = false );
|
||||
void SetSelectedSpell( int index );
|
||||
void SpeakSpellConceptIfAllowed();
|
||||
|
||||
// Spells
|
||||
void CastKartSpell();
|
||||
bool CastSpell( CTFPlayer *pPlayer, int iSpellIndex );
|
||||
|
||||
CHandle<CBaseCombatWeapon> m_pStoredLastWpn;
|
||||
|
||||
void RollNewSpellFinish( void );
|
||||
int m_iNextSpell;
|
||||
int m_iPreviouslyCastSpell;
|
||||
|
||||
#endif
|
||||
|
||||
virtual bool CanThrowUnderWater( void ){ return true; }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
float m_flTimeNextErrorSound;
|
||||
EHANDLE m_hHandEffectWeapon;
|
||||
HPARTICLEFFECT m_hHandEffect;
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
// Self Cast Spells
|
||||
static bool CastSelfHeal( CTFPlayer *pPlayer );
|
||||
static bool CastRocketJump( CTFPlayer *pPlayer );
|
||||
static bool CastSelfSpeedBoost( CTFPlayer *pPlayer );
|
||||
static bool CastSelfStealth( CTFPlayer *pPlayer );
|
||||
|
||||
static bool CastKartRocketJump( CTFPlayer *pPlayer );
|
||||
static bool CastKartUber( CTFPlayer *pPlayer );
|
||||
static bool CastKartBombHead( CTFPlayer *pPlayer );
|
||||
|
||||
static const char* GetHandEffect( CEconItemView *pItem, int iTier );
|
||||
|
||||
CNetworkVar( float, m_flTimeNextSpell );
|
||||
CNetworkVar( int, m_iSelectedSpellIndex );
|
||||
CNetworkVar( int, m_iSpellCharges );
|
||||
|
||||
CNetworkVar( bool, m_bFiredAttack );
|
||||
};
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CBaseEntity* CreateSpellSpawnZombie( CBaseCombatCharacter *pCaster, const Vector& vSpawnPosition, int nSkeletonType );
|
||||
#endif
|
||||
|
||||
|
||||
#endif // TF_WEAPON_SPELLBOOK_H
|
||||
@@ -0,0 +1,54 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef IHASBUILDPOINTS_H
|
||||
#define IHASBUILDPOINTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class CBaseObject;
|
||||
|
||||
// Derive from this interface if your entity can have objects placed on build points on it
|
||||
class IHasBuildPoints
|
||||
{
|
||||
public:
|
||||
// Tell me how many build points you have
|
||||
virtual int GetNumBuildPoints( void ) const = 0;
|
||||
|
||||
// Give me the origin & angles of the specified build point
|
||||
virtual bool GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles ) = 0;
|
||||
|
||||
// If the build point wants to parent built objects to an attachment point on the entity,
|
||||
// it'll return a value >= 1 here specifying which attachment to sit on.
|
||||
virtual int GetBuildPointAttachmentIndex( int iPoint ) const = 0;
|
||||
|
||||
// Can I build the specified object on the specified build point?
|
||||
virtual bool CanBuildObjectOnBuildPoint( int iPoint, int iObjectType ) = 0;
|
||||
|
||||
// I've finished building the specified object on the specified build point
|
||||
virtual void SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject ) = 0;
|
||||
|
||||
// Get the number of objects build on this entity
|
||||
virtual int GetNumObjectsOnMe( void ) = 0;
|
||||
|
||||
// Get the first object of type, return NULL if no such type available
|
||||
virtual CBaseObject *GetObjectOfTypeOnMe( int iObjectType ) = 0;
|
||||
|
||||
// Remove all objects built on me
|
||||
virtual void RemoveAllObjects( void ) = 0;
|
||||
|
||||
// Return the maximum distance that this entity's build points can be snapped to
|
||||
virtual float GetMaxSnapDistance( int iPoint ) = 0;
|
||||
|
||||
// Return true if it's possible that build points on this entity may move in local space (i.e. due to animation)
|
||||
virtual bool ShouldCheckForMovement( void ) = 0;
|
||||
|
||||
// I've finished building the specified object on the specified build point
|
||||
virtual int FindObjectOnBuildPoint( CBaseObject *pObject ) = 0;
|
||||
};
|
||||
|
||||
#endif // IHASBUILDPOINTS_H
|
||||
@@ -0,0 +1,77 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#define FLAGS_DEFAULT (FCVAR_NOTIFY | FCVAR_REPLICATED)
|
||||
|
||||
#define PASSTIME_CONVAR(NAME, STR, DESC) ConVar NAME(#NAME, #STR, FLAGS_DEFAULT, DESC)
|
||||
|
||||
PASSTIME_CONVAR( tf_passtime_scores_per_round, 5, "Number of scores it takes to win a round. Similar to tf_flag_caps_per_round." );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_damping_scale, 0.01f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_drag_coefficient, 0.01f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_inertia_scale, 1.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_mass, 1.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_model, models/passtime/ball/passtime_ball.mdl, "Needs a model with collision info. Map change required." ); // TODO allow override in map
|
||||
PASSTIME_CONVAR( tf_passtime_ball_sphere_collision, 1, "Boolean value. If nonzero, override mdl collision with a perfect sphere collider." );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_sphere_radius, 7.2f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_reset_time, 15, "How long the ball can be neutral before being automatically reset" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_rotdamping_scale, 1.0f, "Higher values will prevent the ball from rolling on the ground." );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_seek_range, 128, "How close players have to be for the ball to be drawn to them." );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_seek_speed_factor, 3f, "How fast the ball will move toward nearby players as a ratio of that player's max speed." );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_takedamage, 1, "Enables shooting the ball" );
|
||||
PASSTIME_CONVAR( tf_passtime_ball_takedamage_force, 800.0f, "Controls how much the ball responds to being shot" );
|
||||
PASSTIME_CONVAR( tf_passtime_flinch_boost, 0, "Intensity of flinch on taking damage while carrying the ball. 0 to use TF defaults." );
|
||||
PASSTIME_CONVAR( tf_passtime_mode_homing_lock_sec, 1.5f, "Number of seconds the ball carrier will stay locked on to a teammate after line of sight is broken." );
|
||||
PASSTIME_CONVAR( tf_passtime_mode_homing_speed, 1000.0f, "How fast the ball moves during a pass." );
|
||||
PASSTIME_CONVAR( tf_passtime_overtime_idle_sec, 5, "How many seconds the ball can be idle in overtime before the round ends.");
|
||||
PASSTIME_CONVAR( tf_passtime_player_reticles_enemies, 1, "Controls HUD reticles for enemies. 0 = never, 1 = when carrying ball, 2 = always." );
|
||||
PASSTIME_CONVAR( tf_passtime_player_reticles_friends, 2, "Controls HUD reticles for teammates. 0 = never, 1 = when carrying ball, 2 = always." );
|
||||
PASSTIME_CONVAR( tf_passtime_score_crit_sec, 5.0f, "How long a scoring team's crits last." );
|
||||
PASSTIME_CONVAR( tf_passtime_speedboost_on_get_ball_time, 2.0f, "How many seconds of speed boost players get when they get the ball." );
|
||||
PASSTIME_CONVAR( tf_passtime_steal_on_melee, 1, "Enables melee stealing." );
|
||||
PASSTIME_CONVAR( tf_passtime_teammate_steal_time, 45, "How many seconds a player can hold the ball before teammates can steal it." );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_scout, 0.1f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_soldier, 0.1f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_pyro, 0.1f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_demoman, 0.15f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_heavy, 0.175f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_engineer, 0.2f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_medic, 0.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_sniper, 0.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwarc_spy, 0.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_scout, 700.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_soldier, 800.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_pyro, 750.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_demoman, 850.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_heavy, 850.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_engineer, 850.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_medic, 900.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_sniper, 900.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_spy, 900.0f, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_throwspeed_velocity_scale, 0.33f, "How much player velocity to add when tossing (0=none 1=100%)" );
|
||||
PASSTIME_CONVAR( tf_passtime_save_stats, 0, "" );
|
||||
|
||||
PASSTIME_CONVAR( tf_passtime_experiment_telepass, 0, "None,\
|
||||
TeleportToCatcher,\
|
||||
SwapWithCatcher,\
|
||||
TeleportToCatcherMaintainPossession,");
|
||||
PASSTIME_CONVAR( tf_passtime_experiment_instapass_charge, 0, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_experiment_autopass, 0, "" );
|
||||
PASSTIME_CONVAR( tf_passtime_experiment_instapass, 0, "" );
|
||||
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_decayamount, 1, "How many points are removed are removed per decay. (must be integer)" );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_decaysec, 4.5f, "How many seconds per decay when the ball is held." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_decaysec_neutral, 1.5f, "How many seconds per decay when the ball is neutral." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_passpoints, 25, "How many ball meter points are awarded for a complete pass." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_threshold, 80, "How many ball meter points it takes to unlock bonus goals." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_airtimebonus, 40, "Ball meter points added per second of time a pass is in the air." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_maxairtimebonus, 100, "Cap on extra points added by tf_passtime_powerball_airtimebonus." );
|
||||
PASSTIME_CONVAR( tf_passtime_powerball_decay_delay, 10, "Number of seconds between ball reaching full charge and decay beginning." );
|
||||
PASSTIME_CONVAR( tf_passtime_pack_range, 512, "How close players must be to the ball carrier to be included in the pack." );
|
||||
PASSTIME_CONVAR( tf_passtime_pack_speed, 1, "When set to 1, all players near the ball carrier will move the same speed." );
|
||||
PASSTIME_CONVAR( tf_passtime_pack_hp_per_sec, 2.0f, "How many HP per second pack members are healed." );
|
||||
@@ -0,0 +1,86 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PASSTIME_CONVARS_H
|
||||
#define PASSTIME_CONVARS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "convar.h"
|
||||
|
||||
extern ConVar
|
||||
tf_passtime_scores_per_round,
|
||||
tf_passtime_ball_damping_scale,
|
||||
tf_passtime_ball_drag_coefficient,
|
||||
tf_passtime_ball_inertia_scale,
|
||||
tf_passtime_ball_mass,
|
||||
tf_passtime_ball_model,
|
||||
tf_passtime_ball_reset_time,
|
||||
tf_passtime_ball_rotdamping_scale,
|
||||
tf_passtime_ball_seek_range,
|
||||
tf_passtime_ball_seek_speed_factor,
|
||||
tf_passtime_ball_sphere_collision,
|
||||
tf_passtime_ball_sphere_radius,
|
||||
tf_passtime_ball_takedamage,
|
||||
tf_passtime_ball_takedamage_force,
|
||||
tf_passtime_flinch_boost,
|
||||
tf_passtime_mode_homing_lock_sec,
|
||||
tf_passtime_mode_homing_speed,
|
||||
tf_passtime_overtime_idle_sec,
|
||||
tf_passtime_player_reticles_enemies,
|
||||
tf_passtime_player_reticles_friends,
|
||||
tf_passtime_score_crit_sec,
|
||||
tf_passtime_speedboost_on_get_ball_time,
|
||||
tf_passtime_steal_on_melee,
|
||||
tf_passtime_teammate_steal_time,
|
||||
tf_passtime_throwarc_scout,
|
||||
tf_passtime_throwarc_sniper,
|
||||
tf_passtime_throwarc_soldier,
|
||||
tf_passtime_throwarc_demoman,
|
||||
tf_passtime_throwarc_medic,
|
||||
tf_passtime_throwarc_heavy,
|
||||
tf_passtime_throwarc_pyro,
|
||||
tf_passtime_throwarc_spy,
|
||||
tf_passtime_throwarc_engineer,
|
||||
tf_passtime_throwspeed_scout,
|
||||
tf_passtime_throwspeed_sniper,
|
||||
tf_passtime_throwspeed_soldier,
|
||||
tf_passtime_throwspeed_demoman,
|
||||
tf_passtime_throwspeed_medic,
|
||||
tf_passtime_throwspeed_heavy,
|
||||
tf_passtime_throwspeed_pyro,
|
||||
tf_passtime_throwspeed_spy,
|
||||
tf_passtime_throwspeed_engineer,
|
||||
tf_passtime_throwspeed_velocity_scale,
|
||||
tf_passtime_save_stats,
|
||||
|
||||
tf_passtime_experiment_telepass,
|
||||
tf_passtime_experiment_autopass,
|
||||
tf_passtime_experiment_instapass_charge,
|
||||
tf_passtime_experiment_instapass,
|
||||
|
||||
tf_passtime_powerball_decayamount,
|
||||
tf_passtime_powerball_decaysec,
|
||||
tf_passtime_powerball_decaysec_neutral,
|
||||
tf_passtime_powerball_passpoints,
|
||||
tf_passtime_powerball_threshold,
|
||||
tf_passtime_powerball_airtimebonus,
|
||||
tf_passtime_powerball_maxairtimebonus,
|
||||
tf_passtime_powerball_decay_delay,
|
||||
tf_passtime_pack_range,
|
||||
tf_passtime_pack_speed,
|
||||
tf_passtime_pack_hp_per_sec;
|
||||
|
||||
enum class EPasstimeExperiment_Telepass {
|
||||
None,
|
||||
TeleportToCatcher,
|
||||
SwapWithCatcher,
|
||||
TeleportToCatcherMaintainPossession,
|
||||
};
|
||||
|
||||
#endif // PASSTIME_CONVARS_H
|
||||
@@ -0,0 +1,249 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "passtime_game_events.h"
|
||||
#include "igameevents.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
using namespace PasstimeGameEvents;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
IGameEvent* CreateEvent()
|
||||
{
|
||||
IGameEvent *pEvent = gameeventmanager->CreateEvent( T::s_eventName );
|
||||
Assert( pEvent );
|
||||
return pEvent;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
bool IsType( IGameEvent *pEvent )
|
||||
{
|
||||
return FStrEq( pEvent->GetName(), T::s_eventName );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const BallGet::s_eventName = "pass_get";
|
||||
const char *const BallGet::s_keyOwnerIndex = "owner";
|
||||
|
||||
BallGet::BallGet( IGameEvent *pEvent )
|
||||
: ownerIndex( pEvent->GetInt( s_keyOwnerIndex ) )
|
||||
{
|
||||
Assert( IsType<BallGet>( pEvent ) );
|
||||
}
|
||||
|
||||
BallGet::BallGet( int ownerIndex_ )
|
||||
: ownerIndex( ownerIndex_ )
|
||||
{
|
||||
}
|
||||
|
||||
void BallGet::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<BallGet>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyOwnerIndex, ownerIndex );
|
||||
gameeventmanager->FireEvent(pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const Score::s_eventName = "pass_score";
|
||||
const char *const Score::s_keyScorerIndex = "scorer";
|
||||
const char *const Score::s_keyAssisterIndex = "assister";
|
||||
const char *const Score::s_keyNumPoints = "points";
|
||||
|
||||
Score::Score( IGameEvent *pEvent )
|
||||
: scorerIndex( pEvent->GetInt( s_keyScorerIndex ) )
|
||||
, assisterIndex( pEvent->GetInt( s_keyAssisterIndex ) )
|
||||
, numPoints( pEvent->GetInt( s_keyNumPoints ) )
|
||||
{
|
||||
Assert( IsType<Score>( pEvent ) );
|
||||
}
|
||||
|
||||
Score::Score( int scorerIndex_, int assisterIndex_, int numPoints_ )
|
||||
: scorerIndex( scorerIndex_ )
|
||||
, assisterIndex( assisterIndex_ )
|
||||
, numPoints( numPoints_ )
|
||||
{
|
||||
}
|
||||
|
||||
Score::Score( int scorerIndex_, int numPoints_ )
|
||||
: scorerIndex( scorerIndex_ )
|
||||
, assisterIndex( -1 )
|
||||
, numPoints( numPoints_ )
|
||||
{
|
||||
}
|
||||
|
||||
void Score::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<Score>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyScorerIndex, scorerIndex );
|
||||
pEvent->SetInt( s_keyAssisterIndex, assisterIndex );
|
||||
pEvent->SetInt( s_keyNumPoints, numPoints );
|
||||
gameeventmanager->FireEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const BallFree::s_eventName = "pass_free";
|
||||
const char *const BallFree::s_keyOwnerIndex = "owner";
|
||||
const char *const BallFree::s_keyAttackerIndex = "attacker";
|
||||
|
||||
BallFree::BallFree( IGameEvent *pEvent )
|
||||
: ownerIndex( pEvent->GetInt( s_keyOwnerIndex ) )
|
||||
, attackerIndex( pEvent->GetInt( s_keyAttackerIndex ) )
|
||||
{
|
||||
Assert( IsType<BallFree>( pEvent ) );
|
||||
}
|
||||
|
||||
BallFree::BallFree()
|
||||
: ownerIndex( -1 )
|
||||
, attackerIndex( -1 )
|
||||
{
|
||||
}
|
||||
|
||||
BallFree::BallFree( int ownerIndex_ )
|
||||
: ownerIndex( ownerIndex_ )
|
||||
, attackerIndex( -1 )
|
||||
{
|
||||
}
|
||||
|
||||
BallFree::BallFree( int ownerIndex_, int attackerIndex_ )
|
||||
: ownerIndex( ownerIndex_ )
|
||||
, attackerIndex( attackerIndex_ )
|
||||
{
|
||||
}
|
||||
|
||||
void BallFree::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<BallFree>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyOwnerIndex, ownerIndex );
|
||||
pEvent->SetInt( s_keyAttackerIndex, attackerIndex );
|
||||
gameeventmanager->FireEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const PassCaught::s_eventName = "pass_pass_caught";
|
||||
const char *const PassCaught::s_keyPasserIndex = "passer";
|
||||
const char *const PassCaught::s_keyCatcherIndex = "catcher";
|
||||
const char *const PassCaught::s_keyDist = "dist";
|
||||
const char *const PassCaught::s_keyDuration = "duration";
|
||||
|
||||
PassCaught::PassCaught( IGameEvent *pEvent )
|
||||
: passerIndex( pEvent->GetInt( s_keyPasserIndex ) )
|
||||
, catcherIndex( pEvent->GetInt( s_keyCatcherIndex ) )
|
||||
, dist( pEvent->GetFloat( s_keyDist ) )
|
||||
, duration( pEvent->GetFloat( s_keyDuration ) )
|
||||
{
|
||||
Assert( IsType<PassCaught>( pEvent ) );
|
||||
}
|
||||
|
||||
PassCaught::PassCaught()
|
||||
: passerIndex( -1 )
|
||||
, catcherIndex( -1 )
|
||||
, dist( 0 )
|
||||
, duration( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
PassCaught::PassCaught( int passerIndex_, int catcherIndex_, float dist_, float duration_ )
|
||||
: passerIndex( passerIndex_ )
|
||||
, catcherIndex( catcherIndex_ )
|
||||
, dist( dist_ )
|
||||
, duration( duration_ )
|
||||
{
|
||||
}
|
||||
|
||||
void PassCaught::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<PassCaught>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyPasserIndex, passerIndex );
|
||||
pEvent->SetInt( s_keyCatcherIndex, catcherIndex );
|
||||
pEvent->SetFloat( s_keyDist, dist );
|
||||
pEvent->SetFloat( s_keyDuration, duration );
|
||||
gameeventmanager->FireEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const BallStolen::s_eventName = "pass_ball_stolen";
|
||||
const char *const BallStolen::s_keyVictimIndex = "victim";
|
||||
const char *const BallStolen::s_keyAttackerIndex = "attacker";
|
||||
|
||||
BallStolen::BallStolen( IGameEvent *pEvent )
|
||||
: victimIndex( pEvent->GetInt( s_keyVictimIndex ) )
|
||||
, attackerIndex( pEvent->GetInt( s_keyAttackerIndex ) )
|
||||
{
|
||||
Assert( IsType<BallStolen>( pEvent ) );
|
||||
}
|
||||
|
||||
BallStolen::BallStolen()
|
||||
: victimIndex( -1 )
|
||||
, attackerIndex( -1 )
|
||||
{
|
||||
}
|
||||
|
||||
BallStolen::BallStolen( int victimIndex_, int attackerIndex_ )
|
||||
: victimIndex( victimIndex_ )
|
||||
, attackerIndex( attackerIndex_ )
|
||||
{
|
||||
}
|
||||
|
||||
void BallStolen::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<BallStolen>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyVictimIndex, victimIndex );
|
||||
pEvent->SetInt( s_keyAttackerIndex, attackerIndex );
|
||||
gameeventmanager->FireEvent( pEvent );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *const BallBlocked::s_eventName = "pass_ball_blocked";
|
||||
const char *const BallBlocked::s_keyOwnerIndex = "owner";
|
||||
const char *const BallBlocked::s_keyBlockerIndex = "blocker";
|
||||
|
||||
BallBlocked::BallBlocked( IGameEvent *pEvent )
|
||||
: ownerIndex( pEvent->GetInt( s_keyOwnerIndex ) )
|
||||
, blockerIndex( pEvent->GetInt( s_keyBlockerIndex ) )
|
||||
{
|
||||
Assert( IsType<BallBlocked>( pEvent ) );
|
||||
}
|
||||
|
||||
BallBlocked::BallBlocked()
|
||||
: ownerIndex( -1 )
|
||||
, blockerIndex( -1 )
|
||||
{
|
||||
}
|
||||
|
||||
BallBlocked::BallBlocked( int ownerIndex_, int blockerIndex_ )
|
||||
: ownerIndex( ownerIndex_ )
|
||||
, blockerIndex( blockerIndex_ )
|
||||
{
|
||||
}
|
||||
|
||||
void BallBlocked::Fire()
|
||||
{
|
||||
if ( IGameEvent *pEvent = CreateEvent<BallBlocked>() )
|
||||
{
|
||||
pEvent->SetInt( s_keyOwnerIndex, ownerIndex );
|
||||
pEvent->SetInt( s_keyBlockerIndex, blockerIndex );
|
||||
gameeventmanager->FireEvent( pEvent );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PASSTIME_GAME_EVENTS_H
|
||||
#define PASSTIME_GAME_EVENTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
class IGameEvent;
|
||||
namespace PasstimeGameEvents
|
||||
{
|
||||
// TODO: this was done following valve's style of having different events
|
||||
// for everything, but these particular events have a lot of overlap and
|
||||
// might be better implemented as a single "ball event" that has an enum
|
||||
// specifying what kind it is. It would cut down on the number of strcmp
|
||||
// calls in the event handling functions. Or maybe we could just not use
|
||||
// 1000s of strcmps for each event dispatch and use a lookup table of some kind.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct BallGet
|
||||
{
|
||||
BallGet( IGameEvent *pEvent );
|
||||
BallGet( int ownerIndex );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyOwnerIndex;
|
||||
int ownerIndex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct Score
|
||||
{
|
||||
Score( IGameEvent *pEvent );
|
||||
Score( int scorerIndex, int assisterIndex, int numPoints );
|
||||
Score( int scorerIndex_, int numPoints_ );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyScorerIndex;
|
||||
static const char *const s_keyAssisterIndex;
|
||||
static const char *const s_keyNumPoints;
|
||||
int scorerIndex;
|
||||
int assisterIndex;
|
||||
int numPoints;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct BallFree
|
||||
{
|
||||
BallFree( IGameEvent *pEvent );
|
||||
BallFree();
|
||||
BallFree( int ownerIndex );
|
||||
BallFree( int ownerIndex, int attackerIndex );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyOwnerIndex;
|
||||
static const char *const s_keyAttackerIndex;
|
||||
int ownerIndex;
|
||||
int attackerIndex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PassCaught
|
||||
{
|
||||
PassCaught( IGameEvent *pEvent );
|
||||
PassCaught();
|
||||
PassCaught( int passerIndex, int catcherIndex, float dist, float duration );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyPasserIndex;
|
||||
static const char *const s_keyCatcherIndex;
|
||||
static const char *const s_keyDist;
|
||||
static const char *const s_keyDuration;
|
||||
int passerIndex;
|
||||
int catcherIndex;
|
||||
float dist;
|
||||
float duration;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct BallStolen
|
||||
{
|
||||
BallStolen( IGameEvent *pEvent );
|
||||
BallStolen();
|
||||
BallStolen( int victimIndex, int attackerIndex );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyVictimIndex;
|
||||
static const char *const s_keyAttackerIndex;
|
||||
int victimIndex;
|
||||
int attackerIndex;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct BallBlocked
|
||||
{
|
||||
BallBlocked( IGameEvent *pEvent );
|
||||
BallBlocked();
|
||||
BallBlocked( int ownerIndex, int blockerIndex );
|
||||
void Fire();
|
||||
|
||||
static const char *const s_eventName;
|
||||
static const char *const s_keyOwnerIndex;
|
||||
static const char *const s_keyBlockerIndex;
|
||||
int ownerIndex;
|
||||
int blockerIndex;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // PASSTIME_GAME_EVENTS_H
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "quest_objective_manager.h"
|
||||
#include "gcsdk/gcclient.h"
|
||||
#include "gc_clientsystem.h"
|
||||
#include "econ_quests.h"
|
||||
#include "steamworks_gamestats.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "entity_halloween_pickup.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "econ_notifications.h"
|
||||
#include "tf_item_inventory.h"
|
||||
#include "clientmode_tf.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern ConVar tf_mm_trusted;
|
||||
|
||||
CQuestObjectiveManager::CQuestObjectiveManager()
|
||||
{}
|
||||
|
||||
CQuestObjectiveManager::~CQuestObjectiveManager()
|
||||
{
|
||||
SO_TRACKER_SPEW( "Destroying CQuestObjectiveManager\n", SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
Shutdown();
|
||||
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
GCClientSystem()->GetGCClient()->RemoveSOCacheListener( steamID, this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSOTrackerManager::SOTrackerMap_t::KeyType_t CQuestObjectiveManager::GetKeyForObjectTracker( const CSharedObject* pItem, CSteamID steamIDOwner )
|
||||
{
|
||||
return assert_cast< const CEconItem* >( pItem )->GetItemID();
|
||||
}
|
||||
|
||||
bool CQuestObjectiveManager::ShouldTrackObject( const CSteamID & steamIDOwner, const CSharedObject *pObject ) const
|
||||
{
|
||||
// We only care about items!
|
||||
if( pObject->GetTypeID() != CEconItem::k_nTypeID )
|
||||
return false;
|
||||
|
||||
CEconItem *pItem = (CEconItem *)pObject;
|
||||
const GameItemDefinition_t* pItemDef = pItem->GetItemDefinition();
|
||||
|
||||
// Not a quest? Don't care
|
||||
if ( pItemDef->GetQuestDef() == NULL )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Not accepting item %llu with defindex %d. It doesn't have a quest def.\n", pItem->GetID(), pItemDef->GetDefinitionIndex() ), SO_TRACKER_SPEW_TRACKER_ACCEPTANCE );
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only create trackers for identified items
|
||||
if ( IsQuestItemUnidentified( pItem ) )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Not accepting item %llu with defindex %d. It's not identified.\n", pItem->GetID(), pItemDef->GetDefinitionIndex() ), SO_TRACKER_SPEW_TRACKER_ACCEPTANCE );
|
||||
return false;
|
||||
}
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Accepting item %llu with defindex %d.\n", pItem->GetID(), pItemDef->GetDefinitionIndex() ), SO_TRACKER_SPEW_TRACKER_ACCEPTANCE );
|
||||
return true;
|
||||
}
|
||||
|
||||
int CQuestObjectiveManager::CompareRecords( const ::google::protobuf::Message* pNewProtoMsg, const ::google::protobuf::Message* pExistingProtoMsg ) const
|
||||
{
|
||||
const CMsgGCQuestObjective_PointsChange* pNew = assert_cast< const CMsgGCQuestObjective_PointsChange* >( pNewProtoMsg );
|
||||
const CMsgGCQuestObjective_PointsChange* pExisting = assert_cast< const CMsgGCQuestObjective_PointsChange* >( pExistingProtoMsg );
|
||||
|
||||
int nNewPoints = pNew->standard_points() + pNew->bonus_points();
|
||||
int nExistingPoints = pExisting->standard_points() + pExisting->bonus_points();
|
||||
|
||||
return nNewPoints - nExistingPoints;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void CQuestObjectiveManager::UpdateFromServer( itemid_t nID, uint32 nStandardPoints, uint32 nBonusPoints )
|
||||
{
|
||||
CQuestItemTracker* pTracker = assert_cast< CQuestItemTracker* >( GetTracker( nID ) );
|
||||
if ( pTracker )
|
||||
{
|
||||
pTracker->UpdateFromServer( nStandardPoints, nBonusPoints );
|
||||
}
|
||||
else
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Got update from server, but itemID: %llu doesn't exist!", nID ), SO_TRACKER_SPEW_OBJECTIVES );
|
||||
}
|
||||
}
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void CQuestObjectiveManager::SendMessageForCommit( const ::google::protobuf::Message* pProtoMessage ) const
|
||||
{
|
||||
GCSDK::CProtoBufMsg< CMsgGCQuestObjective_PointsChange > msg( k_EMsgGCQuestObjective_PointsChange );
|
||||
msg.Body() = *assert_cast< const CMsgGCQuestObjective_PointsChange* >( pProtoMessage );
|
||||
GCClientSystem()->BSendMessage( msg );
|
||||
}
|
||||
#endif
|
||||
|
||||
CFmtStr CQuestObjectiveManager::GetDebugObjectDescription( const CSharedObject* pSObject ) const
|
||||
{
|
||||
const CEconItem* pItem = assert_cast< const CEconItem* >( pSObject );
|
||||
return CFmtStr( "%llu (%s)", pItem->GetItemID(), pItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( pItem ) );
|
||||
}
|
||||
|
||||
CBaseSOTracker* CQuestObjectiveManager::AllocateNewTracker( const CSharedObject* pItem, CSteamID steamIDOwner, CSOTrackerManager* pManager ) const
|
||||
{
|
||||
return new CQuestItemTracker( pItem, steamIDOwner, pManager );
|
||||
}
|
||||
|
||||
::google::protobuf::Message* CQuestObjectiveManager::AllocateNewProtoMessage() const
|
||||
{
|
||||
return new CMsgGCQuestObjective_PointsChange();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handle the GC responding to an earlier commit. Remove any unacknowledged
|
||||
// commits records we have.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestObjectiveManager::OnCommitRecieved( const ::google::protobuf::Message* pProtoMsg )
|
||||
{
|
||||
const CMsgGCQuestObjective_PointsChange* pPointsChangeMsg = assert_cast< const CMsgGCQuestObjective_PointsChange* >( pProtoMsg );
|
||||
// Check if we should update points. This happens when the record comes from a server
|
||||
// where the player has disconnected from (this could be ourselves).
|
||||
if ( pPointsChangeMsg->update_base_points() )
|
||||
{
|
||||
CQuestItemTracker* pItemTracker = assert_cast<CQuestItemTracker*>( GetTracker( pPointsChangeMsg->quest_item_id() ) );
|
||||
if ( pItemTracker )
|
||||
{
|
||||
pItemTracker->UpdatePointsFromSOItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CON_COMMAND( tf_quests_spew_trackers, "Spews all currently active quest trackers" )
|
||||
{
|
||||
QuestObjectiveManager()->Spew();
|
||||
}
|
||||
|
||||
CON_COMMAND( ensure_so_trackers_for_steamid, "Ensures a steamID has all the trackers it should have, with extra spew along the way" )
|
||||
{
|
||||
if ( args.ArgC() != 2 )
|
||||
{
|
||||
Warning( "Need the 64bit representation of a steamID as well\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
CSteamID steamID( (uint32)V_atoi( args[1] ),
|
||||
|
||||
// GetUniverse() DOESNT WORK on servers, so we're hacking this for now
|
||||
#ifdef STAGING_ONLY
|
||||
k_EUniverseDev,
|
||||
#else
|
||||
k_EUniversePublic,
|
||||
#endif
|
||||
k_EAccountTypeIndividual );
|
||||
if ( !steamID.IsValid() )
|
||||
{
|
||||
Warning( "SteamID is not valid!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
g_nQuestSpewFlags |= SO_TRACKER_SPEW_TRACKER_ACCEPTANCE;
|
||||
QuestObjectiveManager()->EnsureTrackersForPlayer( steamID );
|
||||
g_nQuestSpewFlags &= ~SO_TRACKER_SPEW_TRACKER_ACCEPTANCE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ( defined( DEBUG ) || defined( STAGING_ONLY ) ) && defined( GAME_DLL )
|
||||
CON_COMMAND( tf_quests_spew_unacknowledged_commits, "Spews info on all unacknowledged commits" )
|
||||
{
|
||||
QuestObjectiveManager()->DBG_SpewPendingCommits();
|
||||
}
|
||||
#endif // ( defined( DEBUG ) || defined( STAGING_ONLY ) ) && defined( GAME_DLL )
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: GC Msg handler for points change response
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCQuestObjective_PointsChangeResponse : public GCSDK::CGCClientJob
|
||||
{
|
||||
public:
|
||||
CGCQuestObjective_PointsChangeResponse( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {}
|
||||
|
||||
virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
|
||||
{
|
||||
GCSDK::CProtoBufMsg< CMsgGCQuestObjective_PointsChange > msg( pNetPacket );
|
||||
|
||||
QuestObjectiveManager()->AcknowledgeCommit( &msg.Body(), msg.Body().quest_item_id() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
GC_REG_JOB( GCSDK::CGCClient, CGCQuestObjective_PointsChangeResponse, "CGCQuestObjective_PointsChangeResponse", k_EMsgGCQuestObjective_PointsChange, GCSDK::k_EServerTypeGCClient );
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
|
||||
|
||||
#if ( defined( DEBUG ) || defined( STAGING_ONLY ) ) && defined( GAME_DLL )
|
||||
CON_COMMAND( tf_quests_complete_all, "Completes all quests" )
|
||||
{
|
||||
QuestObjectiveManager()->DBG_CompleteQuests();
|
||||
}
|
||||
|
||||
void CQuestObjectiveManager::DBG_CompleteQuests()
|
||||
{
|
||||
CTFPlayer *pPlayer = ToTFPlayer( UTIL_GetCommandClient() );
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
CSteamID steamIDForPlayer;
|
||||
if ( !pPlayer->GetSteamID( &steamIDForPlayer ) )
|
||||
return;
|
||||
|
||||
CTFPlayerInventory* pInv = TFInventoryManager()->GetInventoryForPlayer( steamIDForPlayer );
|
||||
if ( pInv )
|
||||
{
|
||||
int iCount = pInv->GetItemCount();
|
||||
for ( int i = 0; i < iCount; i++ )
|
||||
{
|
||||
CEconItemView *pItem = pInv->GetItem(i);
|
||||
if ( !pItem )
|
||||
continue;
|
||||
|
||||
if( !pItem->GetStaticData() || !pItem->GetStaticData()->GetQuestDef() )
|
||||
continue;
|
||||
|
||||
CQuestItemTracker* pTracker = assert_cast<CQuestItemTracker*>( GetTracker( pItem->GetItemID() ) );
|
||||
if ( pTracker )
|
||||
{
|
||||
pTracker->DBG_CompleteQuest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ( defined( DEBUG ) || defined( STAGING_ONLY ) ) && defined( GAME_DLL )
|
||||
@@ -0,0 +1,156 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef QUEST_OBJECTIVE_MANAGER_H
|
||||
#define QUEST_OBJECTIVE_MANAGER_H
|
||||
|
||||
#include "GameEventListener.h"
|
||||
#include "econ_item_constants.h"
|
||||
#include "econ_item_inventory.h"
|
||||
#include "tf_quest_restriction.h"
|
||||
#include "econ_dynamic_recipe.h"
|
||||
#include "shared_object_tracker.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined( _WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
|
||||
class CQuestItemTracker;
|
||||
|
||||
class CBaseQuestObjectiveTracker : public CTFQuestEvaluator
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CBaseQuestObjectiveTracker, CBaseQuestObjectiveTracker )
|
||||
|
||||
CBaseQuestObjectiveTracker( const CTFQuestObjectiveDefinition* pObjective, CQuestItemTracker* pParent );
|
||||
virtual ~CBaseQuestObjectiveTracker();
|
||||
|
||||
uint32 GetObjectiveDefIndex() const { return m_nObjectiveDefIndex; }
|
||||
|
||||
// CTFQuestConditionEvaluator specific
|
||||
virtual const char *GetConditionName() const OVERRIDE { return "tracker"; }
|
||||
virtual bool IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const;
|
||||
virtual const CTFPlayer *GetQuestOwner() const OVERRIDE;
|
||||
virtual void EvaluateCondition( CTFQuestEvaluator *pSender, int nScore ) OVERRIDE;
|
||||
virtual void ResetCondition() OVERRIDE;
|
||||
|
||||
bool UpdateConditions();
|
||||
|
||||
protected:
|
||||
const CTFPlayer* GetTrackedPlayer() const;
|
||||
void IncrementCount( int nIncrementValue );
|
||||
|
||||
uint32 m_nObjectiveDefIndex;
|
||||
|
||||
private:
|
||||
CTFQuestEvaluator *m_pEvaluator;
|
||||
CQuestItemTracker *m_pParent;
|
||||
};
|
||||
|
||||
|
||||
class CQuestItemTracker : public CBaseSOTracker
|
||||
{
|
||||
public:
|
||||
CQuestItemTracker( const CSharedObject* pItem, CSteamID SteamIDOwner, CSOTrackerManager* pManager );
|
||||
~CQuestItemTracker();
|
||||
|
||||
virtual void OnUpdate() OVERRIDE;
|
||||
virtual void OnRemove() OVERRIDE;
|
||||
|
||||
void UpdatePointsFromSOItem();
|
||||
|
||||
const CBaseQuestObjectiveTracker* FindTrackerForDefIndex( uint32 nDefIndex ) const;
|
||||
inline const CUtlVector< const CBaseQuestObjectiveTracker* >& GetTrackers() const { return m_vecObjectiveTrackers; }
|
||||
|
||||
uint32 GetEarnedStandardPoints() const;
|
||||
uint32 GetEarnedBonusPoints() const;
|
||||
const CEconItem* GetItem() const { return static_cast< const CEconItem* >( m_pSObject ); }
|
||||
|
||||
void IncrementCount( uint32 nIncrementValue, const CQuestObjectiveDefinition* pObjective );
|
||||
virtual void CommitChangesToDB() OVERRIDE;
|
||||
|
||||
int IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void UpdateFromServer( uint32 nStandardPoints, uint32 nBonusPoints );
|
||||
#else
|
||||
void SendUpdateToClient( const CQuestObjectiveDefinition* pObjective );
|
||||
#endif
|
||||
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
void DBG_CompleteQuest();
|
||||
#endif
|
||||
|
||||
virtual void Spew() const OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
bool DoesObjectiveNeedToBeTracked( const CQuestObjectiveDefinition* pObjective ) const;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
uint32 m_nStartingStandardPoints;
|
||||
uint32 m_nStartingBonusPoints;
|
||||
#endif
|
||||
|
||||
uint32 m_nStandardPoints;
|
||||
uint32 m_nBonusPoints;
|
||||
|
||||
const CEconItem* m_pItem;
|
||||
|
||||
CUtlVector< const CBaseQuestObjectiveTracker* > m_vecObjectiveTrackers;
|
||||
};
|
||||
|
||||
// A class to handle the creation and deletion of quest objective trackers. Automatically
|
||||
// subscribes to the local player's SOCache and will subscribe to any connecting players'
|
||||
// SOCaches when they connect.
|
||||
class CQuestObjectiveManager : public CSOTrackerManager
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CQuestObjectiveManager, CSOTrackerManager )
|
||||
|
||||
CQuestObjectiveManager();
|
||||
virtual ~CQuestObjectiveManager();
|
||||
|
||||
virtual SOTrackerMap_t::KeyType_t GetKeyForObjectTracker( const CSharedObject* pItem, CSteamID steamIDOwner ) OVERRIDE;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void UpdateFromServer( itemid_t nID, uint32 nStandardPoints, uint32 nBonusPoints );
|
||||
#endif
|
||||
|
||||
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
void DBG_CompleteQuests();
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef GAME_DLL
|
||||
void SendMessageForCommit( const ::google::protobuf::Message* pProtoMessage ) const;
|
||||
#endif
|
||||
|
||||
virtual int GetType() const OVERRIDE { return CEconItem::k_nTypeID; }
|
||||
virtual const char* GetName() const { return "QuestObjectiveManager"; }
|
||||
virtual CFmtStr GetDebugObjectDescription( const CSharedObject* pItem ) const;
|
||||
virtual CBaseSOTracker* AllocateNewTracker( const CSharedObject* pItem, CSteamID steamIDOwner, CSOTrackerManager* pManager ) const OVERRIDE;
|
||||
virtual ::google::protobuf::Message* AllocateNewProtoMessage() const OVERRIDE;
|
||||
virtual void OnCommitRecieved( const ::google::protobuf::Message* pProtoMsg ) OVERRIDE;
|
||||
virtual bool ShouldTrackObject( const CSteamID & steamIDOwner, const CSharedObject *pObject ) const OVERRIDE;
|
||||
virtual int CompareRecords( const ::google::protobuf::Message* pNewProtoMsg, const ::google::protobuf::Message* pExistingProtoMsg ) const OVERRIDE;
|
||||
};
|
||||
|
||||
CQuestObjectiveManager* QuestObjectiveManager();
|
||||
|
||||
#endif // QUEST_OBJECTIVE_MANAGER_H
|
||||
@@ -0,0 +1,550 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "quest_objective_manager.h"
|
||||
#include "gcsdk/gcclient.h"
|
||||
#include "gc_clientsystem.h"
|
||||
#include "econ_quests.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "schemainitutils.h"
|
||||
#include "econ_item_system.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "quest_log_panel.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
ConVar tf_quests_commit_every_point( "tf_quests_commit_every_point", "0", FCVAR_REPLICATED );
|
||||
ConVar tf_quests_progress_enabled( "tf_quests_progress_enabled", "1", FCVAR_REPLICATED );
|
||||
#endif
|
||||
|
||||
|
||||
CQuestObjectiveManager *QuestObjectiveManager( void )
|
||||
{
|
||||
static CQuestObjectiveManager g_QuestObjectiveManager;
|
||||
return &g_QuestObjectiveManager;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseQuestObjectiveTracker::CBaseQuestObjectiveTracker( const CTFQuestObjectiveDefinition* pObjective, CQuestItemTracker* pParent )
|
||||
: m_nObjectiveDefIndex( pObjective->GetDefinitionIndex() )
|
||||
, m_pParent( pParent )
|
||||
, m_pEvaluator( NULL )
|
||||
{
|
||||
KeyValues *pKVConditions = pObjective->GetConditionsKeyValues();
|
||||
|
||||
AssertMsg( !m_pEvaluator, "%s", CFmtStr( "Too many input for operator '%s'.", GetConditionName() ).Get() );
|
||||
|
||||
const char *pszType = pKVConditions->GetString( "type" );
|
||||
m_pEvaluator = CreateEvaluatorByName( pszType, this );
|
||||
AssertMsg( m_pEvaluator != NULL, "%s", CFmtStr( "Failed to create quest condition name '%s' for '%s'", pszType, GetConditionName() ).Get() );
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Creating objective tracker def %d for quest def %d on item %llu for user %s\n",
|
||||
pObjective->GetDefinitionIndex(),
|
||||
pParent->GetItem()->GetItemDefinition()->GetDefinitionIndex(),
|
||||
pParent->GetItem()->GetID(),
|
||||
pParent->GetOwnerSteamID().Render() ),
|
||||
SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT );
|
||||
|
||||
if ( !m_pEvaluator->BInitFromKV( pKVConditions, NULL ) )
|
||||
{
|
||||
AssertMsg( false, "Failed to init from KeyValues" );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseQuestObjectiveTracker::~CBaseQuestObjectiveTracker()
|
||||
{
|
||||
if ( m_pEvaluator )
|
||||
{
|
||||
delete m_pEvaluator;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseQuestObjectiveTracker::IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const
|
||||
{
|
||||
return m_pEvaluator->IsValidForPlayer( pOwner, invalidReasons );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
const CTFPlayer *CBaseQuestObjectiveTracker::GetQuestOwner() const
|
||||
{
|
||||
return GetTrackedPlayer();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseQuestObjectiveTracker::EvaluateCondition( CTFQuestEvaluator *pSender, int nScore )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
// tracker should be the root
|
||||
Assert( !GetParent() );
|
||||
IncrementCount( nScore );
|
||||
ResetCondition();
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseQuestObjectiveTracker::ResetCondition()
|
||||
{
|
||||
m_pEvaluator->ResetCondition();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseQuestObjectiveTracker::UpdateConditions()
|
||||
{
|
||||
const CTFQuestObjectiveDefinition *pObjective = (CTFQuestObjectiveDefinition*)ItemSystem()->GetItemSchema()->GetQuestObjectiveByDefIndex( m_nObjectiveDefIndex );
|
||||
if ( !pObjective )
|
||||
return false;
|
||||
|
||||
// clean up previous evaluator
|
||||
if ( m_pEvaluator )
|
||||
{
|
||||
delete m_pEvaluator;
|
||||
m_pEvaluator = NULL;
|
||||
}
|
||||
|
||||
CUtlVector< CUtlString > vecErrors;
|
||||
return BInitFromKV( pObjective->GetConditionsKeyValues(), &vecErrors );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
const CTFPlayer* CBaseQuestObjectiveTracker::GetTrackedPlayer() const
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
return ToTFPlayer( C_BasePlayer::GetLocalPlayer() );
|
||||
#else
|
||||
return m_pParent->GetTrackedPlayer();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CBaseQuestObjectiveTracker::IncrementCount( int nIncrementValue )
|
||||
{
|
||||
const CTFQuestObjectiveDefinition *pObjective = (CTFQuestObjectiveDefinition*)ItemSystem()->GetItemSchema()->GetQuestObjectiveByDefIndex( m_nObjectiveDefIndex );
|
||||
Assert( pObjective );
|
||||
if ( !pObjective )
|
||||
return;
|
||||
|
||||
uint32 nPointsToAdd = nIncrementValue * pObjective->GetPoints();
|
||||
m_pParent->IncrementCount( nPointsToAdd, pObjective );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CQuestItemTracker::CQuestItemTracker( const CSharedObject* pItem, CSteamID SteamIDOwner, CSOTrackerManager* pManager )
|
||||
: CBaseSOTracker( pItem, SteamIDOwner, pManager )
|
||||
, m_pItem( NULL )
|
||||
, m_nStandardPoints( 0 )
|
||||
, m_nBonusPoints( 0 )
|
||||
#ifdef GAME_DLL
|
||||
, m_nStartingStandardPoints( 0 )
|
||||
, m_nStartingBonusPoints( 0 )
|
||||
#endif
|
||||
{
|
||||
m_pItem = assert_cast< const CEconItem* >( pItem );
|
||||
// Retrieve starting numbers
|
||||
UpdatePointsFromSOItem();
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Creating tracker for quest %d on item %llu for user %s with %dsp and %dbp\n",
|
||||
GetItem()->GetItemDefinition()->GetDefinitionIndex(),
|
||||
GetItem()->GetID(),
|
||||
GetOwnerSteamID().Render(),
|
||||
GetEarnedStandardPoints(),
|
||||
GetEarnedBonusPoints() ),
|
||||
SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
|
||||
// Create trackers for each objective
|
||||
QuestObjectiveDefVec_t vecChosenObjectives;
|
||||
m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledObjectivesForItem( vecChosenObjectives, m_pItem );
|
||||
FOR_EACH_VEC( vecChosenObjectives, i )
|
||||
{
|
||||
if ( !DoesObjectiveNeedToBeTracked( vecChosenObjectives[i] ) )
|
||||
continue;
|
||||
|
||||
CBaseQuestObjectiveTracker* pNewTracker = new CBaseQuestObjectiveTracker( vecChosenObjectives[i], this );
|
||||
m_vecObjectiveTrackers.AddToTail( pNewTracker );
|
||||
}
|
||||
|
||||
if ( m_vecObjectiveTrackers.IsEmpty() )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Did not create any objective trackers for quest %d on item %llu for user %s with %dsp and %dbp\n",
|
||||
GetItem()->GetItemDefinition()->GetDefinitionIndex(),
|
||||
GetItem()->GetID(),
|
||||
GetOwnerSteamID().Render(),
|
||||
GetEarnedStandardPoints(),
|
||||
GetEarnedBonusPoints() ),
|
||||
SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CQuestItemTracker::~CQuestItemTracker()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
SO_TRACKER_SPEW( CFmtStr( "Deleting tracker for quest %u on item %llu with %usp and %ubp\n",
|
||||
m_pItem->GetItemDefinition()->GetDefinitionIndex(),
|
||||
m_pItem->GetItemID(),
|
||||
m_nStandardPoints,
|
||||
m_nBonusPoints ),
|
||||
SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
#else
|
||||
SO_TRACKER_SPEW( CFmtStr( "Deleting tracker for quest %u on item %llu with %usp %ussp %ubp %usbp\n",
|
||||
m_pItem->GetItemDefinition()->GetDefinitionIndex(),
|
||||
m_pItem->GetItemID(),
|
||||
m_nStandardPoints,
|
||||
m_nStartingStandardPoints,
|
||||
m_nBonusPoints,
|
||||
m_nStartingBonusPoints ),
|
||||
SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
#endif
|
||||
m_vecObjectiveTrackers.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Take a look at our item and update what we think our points are
|
||||
// based on the attributes on the item IF they are greater. We NEVER
|
||||
// want to lose progress for any reason.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::UpdatePointsFromSOItem()
|
||||
{
|
||||
uint32 nNewPoints = 0;
|
||||
static CSchemaAttributeDefHandle pAttribDef_EarnedStandardPoints( "quest earned standard points" );
|
||||
m_pItem->FindAttribute( pAttribDef_EarnedStandardPoints, &nNewPoints );
|
||||
#ifdef GAME_DLL
|
||||
m_nStartingStandardPoints = Max( nNewPoints, m_nStartingStandardPoints );
|
||||
#else
|
||||
m_nStandardPoints = Max( nNewPoints, m_nStandardPoints );
|
||||
#endif
|
||||
|
||||
nNewPoints = 0;
|
||||
static CSchemaAttributeDefHandle pAttribDef_EarnedBonusPoints( "quest earned bonus points" );
|
||||
m_pItem->FindAttribute( pAttribDef_EarnedBonusPoints, &nNewPoints );
|
||||
#ifdef GAME_DLL
|
||||
m_nStartingBonusPoints = Max( nNewPoints, m_nStartingBonusPoints );
|
||||
#else
|
||||
m_nBonusPoints = Max( nNewPoints, m_nBonusPoints );
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
SendUpdateToClient( NULL );
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Updated points from item. CS:%d S:%d CB:%d B:%d\n", m_nStandardPoints, m_nStartingStandardPoints, m_nBonusPoints, m_nStartingBonusPoints ), SO_TRACKER_SPEW_OBJECTIVES );
|
||||
#else
|
||||
SO_TRACKER_SPEW( CFmtStr( "Updated points from item. S:%d B:%d\n", m_nStandardPoints, m_nBonusPoints ), SO_TRACKER_SPEW_OBJECTIVES );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
const CBaseQuestObjectiveTracker* CQuestItemTracker::FindTrackerForDefIndex( uint32 nDefIndex ) const
|
||||
{
|
||||
FOR_EACH_VEC( m_vecObjectiveTrackers, i )
|
||||
{
|
||||
if ( m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() == nDefIndex )
|
||||
{
|
||||
return m_vecObjectiveTrackers[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 CQuestItemTracker::GetEarnedStandardPoints() const
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
return m_nStartingStandardPoints + m_nStandardPoints;
|
||||
#else
|
||||
return m_nStandardPoints;
|
||||
#endif
|
||||
}
|
||||
uint32 CQuestItemTracker::GetEarnedBonusPoints() const
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
return m_nStartingBonusPoints + m_nBonusPoints;
|
||||
#else
|
||||
return m_nBonusPoints;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::IncrementCount( uint32 nIncrementValue, const CQuestObjectiveDefinition* pObjective )
|
||||
{
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
if ( !tf_quests_progress_enabled.GetBool() )
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
Assert( pObjective );
|
||||
Assert( m_pItem );
|
||||
if ( !pObjective || !m_pItem )
|
||||
return;
|
||||
|
||||
auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef();
|
||||
Assert( pQuestDef );
|
||||
if ( !pQuestDef )
|
||||
return;
|
||||
|
||||
if ( g_pVGuiLocalize && ( g_nQuestSpewFlags & SO_TRACKER_SPEW_OBJECTIVES ) )
|
||||
{
|
||||
locchar_t loc_IntermediateName[ MAX_ITEM_NAME_LENGTH ];
|
||||
locchar_t locValue[ MAX_ITEM_NAME_LENGTH ];
|
||||
loc_sprintf_safe( locValue, LOCCHAR( "%d" ), pObjective->GetPoints() );
|
||||
loc_scpy_safe( loc_IntermediateName, CConstructLocalizedString( g_pVGuiLocalize->Find( pObjective->GetDescriptionToken() ), locValue ) );
|
||||
char szTempObjectiveName[256];
|
||||
::ILocalize::ConvertUnicodeToANSI( loc_IntermediateName, szTempObjectiveName, sizeof( szTempObjectiveName ));
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Increment for quest: %llu Objective: \"%s\" %d->%d (+%d)\n"
|
||||
, m_pItem->GetItemID()
|
||||
, szTempObjectiveName
|
||||
, m_nStandardPoints + m_nBonusPoints
|
||||
, m_nStandardPoints + m_nBonusPoints + nIncrementValue
|
||||
, nIncrementValue ), SO_TRACKER_SPEW_OBJECTIVES );
|
||||
}
|
||||
|
||||
// Regardless of standard or bonus, we fill the standard gauge first
|
||||
uint32 nMaxStandardPoints = pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints();
|
||||
int nAmountToAdd = Min( nMaxStandardPoints, nIncrementValue );
|
||||
m_nStandardPoints += nAmountToAdd;
|
||||
nIncrementValue -= nAmountToAdd;
|
||||
|
||||
// If any bonus points left, fill in bonus points
|
||||
if ( pObjective->IsAdvanced() && nIncrementValue > 0 )
|
||||
{
|
||||
uint32 nMaxBonusPoints = pQuestDef->GetMaxBonusPoints() + pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints() - GetEarnedBonusPoints();
|
||||
m_nBonusPoints += Min( nMaxBonusPoints, nIncrementValue );
|
||||
}
|
||||
|
||||
bool bShouldCommit = IsQuestItemReadyToTurnIn( m_pItem );
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
bShouldCommit |= tf_quests_commit_every_point.GetBool();
|
||||
#endif
|
||||
|
||||
// Once we're over the turn-in threshhold, we need to record every point made.
|
||||
if ( bShouldCommit )
|
||||
{
|
||||
CommitChangesToDB();
|
||||
}
|
||||
|
||||
SendUpdateToClient( pObjective );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Remove and delete any objective trackers that are no longer needed.
|
||||
// One is considered not needed if it's a tracker for a "standard"
|
||||
// objective and we're done getting standard points, or if we're at
|
||||
// full bonus points, then there's no way for us to get points anymore
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::OnUpdate()
|
||||
{
|
||||
FOR_EACH_VEC_BACK( m_vecObjectiveTrackers, i )
|
||||
{
|
||||
const CQuestObjectiveDefinition *pObjective = GEconItemSchema().GetQuestObjectiveByDefIndex( m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() );
|
||||
Assert( pObjective );
|
||||
if ( !pObjective || !DoesObjectiveNeedToBeTracked( pObjective ) )
|
||||
{
|
||||
delete m_vecObjectiveTrackers[ i ];
|
||||
m_vecObjectiveTrackers.Remove( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::OnRemove()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
CommitRecord_t* pRecord = m_pManager->GetCommitRecord( m_pItem->GetItemID() );
|
||||
if ( pRecord )
|
||||
{
|
||||
CMsgGCQuestObjective_PointsChange* pProto = assert_cast< CMsgGCQuestObjective_PointsChange* >( pRecord->m_pProtoMsg );
|
||||
pProto->set_update_base_points( true );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CQuestItemTracker::Spew() const
|
||||
{
|
||||
CBaseSOTracker::Spew();
|
||||
|
||||
FOR_EACH_VEC( m_vecObjectiveTrackers, i )
|
||||
{
|
||||
DevMsg( "Tracking objective: %d\n", m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CQuestItemTracker::DoesObjectiveNeedToBeTracked( const CQuestObjectiveDefinition* pObjective ) const
|
||||
{
|
||||
auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef();
|
||||
|
||||
Assert( pObjective );
|
||||
if ( pObjective && pQuestDef )
|
||||
{
|
||||
// If there's standard points to be earned, all objectives need to be tracked
|
||||
if ( pQuestDef->GetMaxStandardPoints() > 0 && GetEarnedStandardPoints() < pQuestDef->GetMaxStandardPoints() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If this objective is advanced, only track it if there's bonus points to be earned
|
||||
if ( pObjective->IsAdvanced() )
|
||||
{
|
||||
return pQuestDef->GetMaxBonusPoints() > 0 && GetEarnedBonusPoints() < pQuestDef->GetMaxBonusPoints();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::CommitChangesToDB()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( GetQuestLog() && GetTrackedPlayer() == C_TFPlayer::GetLocalTFPlayer() )
|
||||
{
|
||||
GetQuestLog()->MarkQuestsDirty();
|
||||
}
|
||||
#else // GAME_DLL
|
||||
|
||||
// Nothing to commit? Bail
|
||||
if ( m_nStandardPoints == 0 && m_nBonusPoints == 0 )
|
||||
return;
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "CommitChangesToDB: %llu S:%d B:%d\n"
|
||||
, m_pItem->GetItemID()
|
||||
, GetEarnedStandardPoints()
|
||||
, GetEarnedBonusPoints() ), 0 );
|
||||
|
||||
CSteamID ownerSteamID( m_pItem->GetAccountID(), GetUniverse(), k_EAccountTypeIndividual );
|
||||
|
||||
CMsgGCQuestObjective_PointsChange record;
|
||||
|
||||
// Cook up our message
|
||||
record.set_owner_steamid( ownerSteamID.ConvertToUint64() );
|
||||
record.set_quest_item_id( m_pItem->GetItemID() );
|
||||
record.set_standard_points( GetEarnedStandardPoints() );
|
||||
record.set_bonus_points( GetEarnedBonusPoints() ); // Here's the meat
|
||||
|
||||
m_pManager->AddCommitRecord( &record, record.quest_item_id(), true );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CQuestItemTracker::IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const
|
||||
{
|
||||
int nNumInvalid = 0;
|
||||
FOR_EACH_VEC( m_vecObjectiveTrackers, i )
|
||||
{
|
||||
m_vecObjectiveTrackers[ i ]->IsValidForPlayer( pOwner, invalidReasons );
|
||||
|
||||
if ( !invalidReasons.IsValid() )
|
||||
{
|
||||
++nNumInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
return nNumInvalid;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The server has changed scores. Apply those changes here
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::UpdateFromServer( uint32 nStandardPoints, uint32 nBonusPoints )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Updating \"%s's\" standard points: %d->%d bonus points: %d->%d\n"
|
||||
, m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( m_pItem )
|
||||
, m_nStandardPoints
|
||||
, nStandardPoints
|
||||
, m_nBonusPoints
|
||||
, nBonusPoints )
|
||||
, SO_TRACKER_SPEW_OBJECTIVES );
|
||||
|
||||
m_nStandardPoints = nStandardPoints;
|
||||
m_nBonusPoints = nBonusPoints;
|
||||
}
|
||||
#else
|
||||
void CQuestItemTracker::SendUpdateToClient( const CQuestObjectiveDefinition* pObjective )
|
||||
{
|
||||
const CTFPlayer* pPlayer = GetTrackedPlayer();
|
||||
|
||||
// They might've disconnected, so let's check if they're still around
|
||||
if ( pPlayer )
|
||||
{
|
||||
// Update the user on their progress
|
||||
CSingleUserRecipientFilter filter( GetTrackedPlayer() );
|
||||
filter.MakeReliable();
|
||||
UserMessageBegin( filter, "QuestObjectiveCompleted" );
|
||||
itemid_t nID = m_pItem->GetItemID();
|
||||
WRITE_BITS( &nID, 64 );
|
||||
WRITE_WORD( GetEarnedStandardPoints() );
|
||||
WRITE_WORD( GetEarnedBonusPoints() );
|
||||
WRITE_WORD( pObjective ? pObjective->GetDefinitionIndex() : (uint32)-1 );
|
||||
MessageEnd();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( DEBUG ) || defined( STAGING_ONLY )
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CQuestItemTracker::DBG_CompleteQuest()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
|
||||
auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef();
|
||||
uint32 nStandardPointsDelta = pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints();
|
||||
|
||||
// Cheat!
|
||||
if ( m_vecObjectiveTrackers.Count() )
|
||||
{
|
||||
const_cast< CBaseQuestObjectiveTracker* >( m_vecObjectiveTrackers[0] )->EvaluateCondition( NULL, nStandardPointsDelta );
|
||||
}
|
||||
|
||||
CommitChangesToDB();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,566 @@
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "shared_object_tracker.h"
|
||||
#include "gcsdk/gcclient.h"
|
||||
#include "gc_clientsystem.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "econ_notifications.h"
|
||||
#include "clientmode_tf.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
short g_nQuestSpewFlags = 0;
|
||||
|
||||
void SOTrackerSpew( const char* pszBuff, int nType )
|
||||
{
|
||||
if ( ( g_nQuestSpewFlags & nType ) == 0 )
|
||||
return;
|
||||
|
||||
Color questDebugColor =
|
||||
#ifdef GAME_DLL
|
||||
Color( 255, 100, 0, 255 );
|
||||
ConColorMsg( questDebugColor, "[SVTrackers]: %s", pszBuff );
|
||||
#else
|
||||
Color( 255, 200, 0, 255 );
|
||||
ConColorMsg( questDebugColor, "[CLTrackers]: %s", pszBuff );
|
||||
#endif
|
||||
}
|
||||
|
||||
void SOTrackerSpewTypeToggle( const CCommand &args )
|
||||
{
|
||||
if ( args.ArgC() != 2 )
|
||||
{
|
||||
Warning( "Incorrect parameters. Format: command_toggle_SO_TRACKER_SPEW_type <type>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
CUtlString strType( args[1] );
|
||||
strType.ToLower();
|
||||
int nBitMask = 0;
|
||||
|
||||
if ( FStrEq( strType, "objectives" ) )
|
||||
{
|
||||
nBitMask = SO_TRACKER_SPEW_OBJECTIVES;
|
||||
}
|
||||
else if ( FStrEq( strType, "itemtrackers" ) )
|
||||
{
|
||||
nBitMask = SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT;
|
||||
}
|
||||
else if ( FStrEq( strType, "objectivetrackers" ) )
|
||||
{
|
||||
nBitMask = SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT;
|
||||
}
|
||||
else if ( FStrEq( strType, "commits" ) )
|
||||
{
|
||||
nBitMask = SO_TRACKER_SPEW_GC_COMMITS;
|
||||
}
|
||||
else if ( FStrEq( strType, "socache" ) )
|
||||
{
|
||||
nBitMask = SO_TRACKER_SPEW_SOCACHE_ACTIVITY;
|
||||
}
|
||||
else if ( FStrEq( strType, "all" ) )
|
||||
{
|
||||
nBitMask = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if ( nBitMask == 0 )
|
||||
{
|
||||
Warning( "Invalid type. Valid types are: objectives, itemtrackers, objectivetrackers, commits, or all for everything\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
g_nQuestSpewFlags ^= nBitMask;
|
||||
|
||||
DevMsg( "%s %s\n", strType.Get(), g_nQuestSpewFlags & nBitMask ? "ENABLED" : "DISABLED" );
|
||||
}
|
||||
|
||||
ConCommand tf_so_tracker_spew_type_toggle( "tf_so_tracker_spew_type_toggle", SOTrackerSpewTypeToggle, NULL
|
||||
#ifdef CLIENT_DLL
|
||||
, FCVAR_CHEAT
|
||||
#endif
|
||||
);
|
||||
|
||||
CBaseSOTracker::CBaseSOTracker( const CSharedObject* pSObject, CSteamID steamIDOwner, CSOTrackerManager* pManager )
|
||||
: m_pSObject( pSObject )
|
||||
, m_steamIDOwner( steamIDOwner )
|
||||
, m_pManager( pManager )
|
||||
{
|
||||
Assert( m_pSObject );
|
||||
Assert( m_pManager );
|
||||
}
|
||||
|
||||
CBaseSOTracker::~CBaseSOTracker()
|
||||
{}
|
||||
|
||||
void CBaseSOTracker::Spew() const
|
||||
{
|
||||
DevMsg( "Tracker for object type %d\n", m_pSObject->GetTypeID() );
|
||||
m_pSObject->Dump();
|
||||
}
|
||||
|
||||
CSOTrackerManager::CSOTrackerManager()
|
||||
: m_mapItemTrackers( DefLessFunc( SOTrackerMap_t::KeyType_t ) )
|
||||
, m_mapUnacknowledgedCommits( DefLessFunc( CommitsMap_t::KeyType_t ) )
|
||||
#ifdef GAME_DLL
|
||||
, CAutoGameSystemPerFrame( "CSOTrackerManager" )
|
||||
#endif
|
||||
{}
|
||||
|
||||
|
||||
CSOTrackerManager::~CSOTrackerManager()
|
||||
{
|
||||
SO_TRACKER_SPEW( "Destroying CQuestObjectiveManager\n", SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::Initialize()
|
||||
{
|
||||
ListenForGameEvent( "schema_updated" );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
ListenForGameEvent( "player_spawn" );
|
||||
ListenForGameEvent( "player_initial_spawn" );
|
||||
ListenForGameEvent( "server_spawn" );
|
||||
ListenForGameEvent( "server_shutdown" );
|
||||
ListenForGameEvent( "player_disconnect" );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::Shutdown()
|
||||
{
|
||||
CommitAllChanges();
|
||||
|
||||
m_mapItemTrackers.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::FireGameEvent( IGameEvent *pEvent )
|
||||
{
|
||||
const char* pszName = pEvent->GetName();
|
||||
|
||||
if ( FStrEq( pszName, "schema_updated" ) )
|
||||
{
|
||||
// Recreate all existing trackers
|
||||
m_mapItemTrackers.PurgeAndDeleteElements();
|
||||
|
||||
CUtlVector< CSteamID > vecIDsToUpdate;
|
||||
#ifdef GAME_DLL
|
||||
// On the server, we need need new trackers for everyone
|
||||
for ( int i = 1; i<= gpGlobals->maxClients; i++)
|
||||
{
|
||||
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
||||
if ( pPlayer )
|
||||
{
|
||||
CSteamID& steamID = vecIDsToUpdate[ vecIDsToUpdate.AddToTail() ];
|
||||
pPlayer->GetSteamID( &steamID );
|
||||
}
|
||||
}
|
||||
#else
|
||||
// On the client we just need new trackers for us
|
||||
vecIDsToUpdate.AddToTail( steamapicontext->SteamUser()->GetSteamID() );
|
||||
#endif
|
||||
|
||||
FOR_EACH_VEC( vecIDsToUpdate, i )
|
||||
{
|
||||
EnsureTrackersForPlayer( vecIDsToUpdate[ i ] );
|
||||
}
|
||||
}
|
||||
else if ( FStrEq( pszName, "server_spawn" ) )
|
||||
{
|
||||
CommitAllChanges();
|
||||
}
|
||||
else if ( FStrEq( pszName, "server_shutdown" ) )
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
#ifdef GAME_DLL
|
||||
else if ( FStrEq( pszName, "player_disconnect" ) )
|
||||
{
|
||||
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( pEvent->GetInt("userid") ) );
|
||||
if ( pPlayer )
|
||||
{
|
||||
CSteamID steamID;
|
||||
pPlayer->GetSteamID( &steamID );
|
||||
SO_TRACKER_SPEW( CFmtStr( "Unsubscribing from SOCache for user %s\n", steamID.Render() ), SO_TRACKER_SPEW_SOCACHE_ACTIVITY );
|
||||
GCClientSystem()->GetGCClient()->RemoveSOCacheListener( steamID, this );
|
||||
}
|
||||
}
|
||||
else if ( FStrEq( pszName, "player_spawn" ) )
|
||||
{
|
||||
const int nUserID = pEvent->GetInt( "userid" );
|
||||
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
|
||||
EnsureTrackersForPlayer( pPlayer );
|
||||
}
|
||||
else if ( FStrEq( pszName, "player_initial_spawn" ) )
|
||||
{
|
||||
|
||||
CTFPlayer *pNewPlayer = ToTFPlayer( UTIL_PlayerByIndex( pEvent->GetInt( "index" ) ) );
|
||||
Assert( pNewPlayer );
|
||||
// We want to listen for SO caches
|
||||
if ( pNewPlayer && !pNewPlayer->IsBot() )
|
||||
{
|
||||
CSteamID steamID;
|
||||
pNewPlayer->GetSteamID( &steamID );
|
||||
if( steamID.IsValid() )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Subscribing to SOCache for user %s\n", steamID.Render() ), SO_TRACKER_SPEW_SOCACHE_ACTIVITY );
|
||||
GCClientSystem()->GetGCClient()->AddSOCacheListener( steamID, this );
|
||||
|
||||
EnsureTrackersForPlayer( steamID );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent )
|
||||
{
|
||||
HandleSOEvent( steamIDOwner, pObject, TRACKER_CREATE_OR_UPDATE );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent )
|
||||
{
|
||||
HandleSOEvent( steamIDOwner, pObject, TRACKER_CREATE_OR_UPDATE );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::SODestroyed( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent)
|
||||
{
|
||||
HandleSOEvent( steamIDOwner, pObject, TRACKER_REMOVE );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "SOCacheSubscribed recieved for user %s\n", steamIDOwner.Render() ), SO_TRACKER_SPEW_SOCACHE_ACTIVITY );
|
||||
// Clear out trackers that are all now invalid
|
||||
RemoveTrackersForSteamID( steamIDOwner );
|
||||
EnsureTrackersForPlayer( steamIDOwner );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "SOCacheUnsubscribed recieved for user %s\n", steamIDOwner.Render() ), SO_TRACKER_SPEW_SOCACHE_ACTIVITY );
|
||||
RemoveTrackersForSteamID( steamIDOwner );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::HandleSOEvent( const CSteamID & steamIDOwner, const CSharedObject *pObject, ETrackerHandling_t eHandling )
|
||||
{
|
||||
if ( !ShouldTrackObject( steamIDOwner, pObject ) )
|
||||
return;
|
||||
|
||||
UpdateTrackerForItem( pObject, eHandling, steamIDOwner );
|
||||
}
|
||||
|
||||
CBaseSOTracker* CSOTrackerManager::GetTracker( SOTrackerMap_t::KeyType_t nKey ) const
|
||||
{
|
||||
auto idx = m_mapItemTrackers.Find( nKey );
|
||||
if ( idx != m_mapItemTrackers.InvalidIndex() )
|
||||
{
|
||||
return m_mapItemTrackers[ idx ];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CommitRecord_t* CSOTrackerManager::GetCommitRecord( CommitsMap_t::KeyType_t nKey )
|
||||
{
|
||||
auto idx = m_mapUnacknowledgedCommits.Find( nKey );
|
||||
if ( idx != m_mapUnacknowledgedCommits.InvalidIndex() )
|
||||
{
|
||||
return m_mapUnacknowledgedCommits[ idx ];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CSOTrackerManager::UpdateTrackerForItem( const CSharedObject* pItem, ETrackerHandling_t eHandling, CSteamID steamIDOwner )
|
||||
{
|
||||
// Do we want to make sure we have a tracker, or that we dont have a tracker
|
||||
const bool bWantsTracker = eHandling != TRACKER_REMOVE;
|
||||
auto idx = m_mapItemTrackers.Find( GetKeyForObjectTracker( pItem, steamIDOwner ) );
|
||||
|
||||
// Wants a tracker and doesnt have one?
|
||||
if ( bWantsTracker && idx == m_mapItemTrackers.InvalidIndex() )
|
||||
{
|
||||
CreateAndAddTracker( pItem, steamIDOwner );
|
||||
}
|
||||
else if ( !bWantsTracker && idx != m_mapItemTrackers.InvalidIndex() ) // Doesnt want a tracker and has one?
|
||||
{
|
||||
RemoveAndDeleteTrackerAtIndex( idx );
|
||||
}
|
||||
else if ( idx != m_mapItemTrackers.InvalidIndex() )
|
||||
{
|
||||
m_mapItemTrackers[ idx ]->OnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void CSOTrackerManager::EnsureTrackersForPlayer( const CSteamID& steamIDPlayer )
|
||||
{
|
||||
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamIDPlayer );
|
||||
if ( !pSOCache )
|
||||
return;
|
||||
|
||||
CGCClientSharedObjectTypeCache *pSOTypeCache = pSOCache->FindTypeCache( GetType() );
|
||||
|
||||
if ( !pSOTypeCache )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "No SOCache for %s in %s!\n", steamIDPlayer.Render(), __FUNCTION__ ), SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through existing trackers and remove orphaned ones
|
||||
FOR_EACH_MAP_FAST( m_mapItemTrackers, i )
|
||||
{
|
||||
// If we didn't find the object in our cache, remove the tracker
|
||||
if ( m_mapItemTrackers[ i ]->GetOwnerSteamID() == steamIDPlayer &&
|
||||
pSOTypeCache->FindSharedObject( *m_mapItemTrackers[ i ]->GetSObject() ) == NULL )
|
||||
{
|
||||
RemoveAndDeleteTrackerAtIndex( i );
|
||||
i = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through SOTypeCache and ensure we have trackers for every object
|
||||
for ( uint32 i=0; i < pSOTypeCache->GetCount(); ++i )
|
||||
{
|
||||
CSharedObject* pObject = pSOTypeCache->GetObject( i );
|
||||
if ( ShouldTrackObject( steamIDPlayer, pObject ) )
|
||||
{
|
||||
UpdateTrackerForItem( pObject, TRACKER_CREATE_OR_UPDATE, steamIDPlayer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSOTrackerManager::EnsureTrackersForPlayer( CTFPlayer* pPlayer )
|
||||
{
|
||||
if ( pPlayer && !pPlayer->IsBot() )
|
||||
{
|
||||
CSteamID steamID;
|
||||
pPlayer->GetSteamID( &steamID );
|
||||
if( steamID.IsValid() )
|
||||
{
|
||||
EnsureTrackersForPlayer( steamID );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::CreateAndAddTracker( const CSharedObject* pItem, CSteamID steamIDOwner )
|
||||
{
|
||||
CBaseSOTracker* pItemTracker = AllocateNewTracker( pItem, steamIDOwner, this );
|
||||
auto nKey = GetKeyForObjectTracker( pItem, steamIDOwner );
|
||||
m_mapItemTrackers.Insert( nKey, pItemTracker );
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Created tracker for object: %s\n", GetDebugObjectDescription( pItem ).Get() ), SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::RemoveAndDeleteTrackerAtIndex( SOTrackerMap_t::IndexType_t idx )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Deleted tracker for object: %s\n", GetDebugObjectDescription( m_mapItemTrackers[ idx ]->GetSObject() ).Get() ), SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT );
|
||||
|
||||
delete m_mapItemTrackers[ idx ];
|
||||
m_mapItemTrackers.RemoveAt( idx );
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::RemoveTrackersForSteamID( const CSteamID & steamIDOwner )
|
||||
{
|
||||
// We need to remove all trackers for the user
|
||||
FOR_EACH_MAP_FAST( m_mapItemTrackers, idx )
|
||||
{
|
||||
// Don't care about the itemIDs, just the steamID
|
||||
if ( m_mapItemTrackers[ idx ]->GetOwnerSteamID() == steamIDOwner )
|
||||
{
|
||||
m_mapItemTrackers[ idx ]->CommitChangesToDB();
|
||||
m_mapItemTrackers[ idx ]->OnRemove();
|
||||
|
||||
delete m_mapItemTrackers[ idx ];
|
||||
m_mapItemTrackers.RemoveAt( idx );
|
||||
idx = -1; // Reset to be safe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSOTrackerManager::CommitAllChanges()
|
||||
{
|
||||
// Commit everything
|
||||
FOR_EACH_MAP_FAST( m_mapItemTrackers, idx )
|
||||
{
|
||||
m_mapItemTrackers[ idx ]->CommitChangesToDB();
|
||||
}
|
||||
}
|
||||
|
||||
void CSOTrackerManager::Spew()
|
||||
{
|
||||
DevMsg( "--- Spewing all trackers for %s ---\n", GetName() );
|
||||
|
||||
FOR_EACH_MAP( m_mapItemTrackers, i )
|
||||
{
|
||||
const CBaseSOTracker* pTracker = m_mapItemTrackers[ i ];
|
||||
CSteamID steamID( m_mapItemTrackers.Key( i ) );
|
||||
DevMsg( "\tTrackers for %s:\n", steamID.Render() );
|
||||
pTracker->Spew();
|
||||
DevMsg( "\t---\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
void CSOTrackerManager::CommitRecord( CommitRecord_t* pRecord ) const
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Sending %fs old record to GC for SObject. %s\n", Plat_FloatTime() - pRecord->m_flReportedTime, pRecord->m_pProtoMsg->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
|
||||
SendMessageForCommit( pRecord->m_pProtoMsg );
|
||||
|
||||
pRecord->m_flLastCommitTime = Plat_FloatTime();
|
||||
}
|
||||
|
||||
void CSOTrackerManager::FrameUpdatePreEntityThink()
|
||||
{
|
||||
// Rate limit to once a second
|
||||
double flNextCommitTime = m_flLastUnacknowledgeCommitTime + 1.f;
|
||||
double flNow = Plat_FloatTime();
|
||||
|
||||
if ( flNow > flNextCommitTime )
|
||||
{
|
||||
m_flLastUnacknowledgeCommitTime = flNow;
|
||||
|
||||
auto i = m_mapUnacknowledgedCommits.FirstInorder();
|
||||
while( i != m_mapUnacknowledgedCommits.InvalidIndex() )
|
||||
{
|
||||
auto currentIndex = i;
|
||||
i = m_mapUnacknowledgedCommits.NextInorder( i );
|
||||
|
||||
// Give records 10 minutes to get themselves reported and acknowledged
|
||||
if ( flNow - m_mapUnacknowledgedCommits[ currentIndex ]->m_flReportedTime > 600.f )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Record is %fs old. Abandoning. %s\n", m_mapUnacknowledgedCommits[ currentIndex ]->m_flReportedTime, m_mapUnacknowledgedCommits[ currentIndex ]->m_pProtoMsg->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
m_mapUnacknowledgedCommits.RemoveAt( currentIndex );
|
||||
}
|
||||
else if ( m_mapUnacknowledgedCommits[ currentIndex ]->m_flLastCommitTime + 30.f < flNow )
|
||||
{
|
||||
// Only try committing for a given contract once every 30 seconds
|
||||
CommitRecord( m_mapUnacknowledgedCommits[ currentIndex ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Add a record of a commit to the GC. This is so we can listen for a
|
||||
// response from the GC (or lack thereof) and attempt to re-commit if needed
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTrackerManager::AddCommitRecord( const ::google::protobuf::Message* pRecord, uint64 nKey, bool bRequireResponse )
|
||||
{
|
||||
// If we don't require a response, don't create a commit record that we have to track. Just commit right now
|
||||
if ( !bRequireResponse )
|
||||
{
|
||||
SendMessageForCommit( pRecord );
|
||||
return;
|
||||
}
|
||||
|
||||
bool bShouldCommitNow = false;
|
||||
|
||||
// Check if there's no record for this commit
|
||||
auto idx = m_mapUnacknowledgedCommits.Find( nKey );
|
||||
if ( idx == m_mapUnacknowledgedCommits.InvalidIndex() )
|
||||
{
|
||||
// Add it if nothing for this item
|
||||
::google::protobuf::Message* pCopy = AllocateNewProtoMessage();
|
||||
pCopy->CopyFrom( *pRecord );
|
||||
idx = m_mapUnacknowledgedCommits.Insert( nKey, new CommitRecord_t( pCopy ) );
|
||||
bShouldCommitNow = true;
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Creating new commit record for SObject: %s\n", pRecord->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
}
|
||||
else
|
||||
{
|
||||
::google::protobuf::Message* pExisting = m_mapUnacknowledgedCommits[ idx ]->m_pProtoMsg;
|
||||
// Check if this new record is more up to date than an existing commit record. If so, update the existing one
|
||||
if ( CompareRecords( pRecord, pExisting ) > 0 )
|
||||
{
|
||||
pExisting->CopyFrom( *pRecord );
|
||||
bShouldCommitNow = true;
|
||||
|
||||
SO_TRACKER_SPEW( CFmtStr( "Updating existing commit record for SObject: %s\n", pRecord->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
}
|
||||
else
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Existing commit record for SObject is more up to date: %s\n", pExisting->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
}
|
||||
}
|
||||
|
||||
if ( bShouldCommitNow )
|
||||
{
|
||||
CommitRecord( m_mapUnacknowledgedCommits[ idx ] );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Handle the GC responding to an earlier commit. Remove any unacknowledged
|
||||
// commits records we have.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTrackerManager::AcknowledgeCommit( const ::google::protobuf::Message* pRecord, uint64 nKey )
|
||||
{
|
||||
OnCommitRecieved( pRecord );
|
||||
|
||||
// Find the record
|
||||
auto idx = m_mapUnacknowledgedCommits.Find( nKey );
|
||||
if ( idx != m_mapUnacknowledgedCommits.InvalidIndex() )
|
||||
{
|
||||
::google::protobuf::Message* pCommitRecord = m_mapUnacknowledgedCommits[ idx ]->m_pProtoMsg;
|
||||
|
||||
// See if we have a matching record. If so, remove it
|
||||
if ( CompareRecords( pCommitRecord, pRecord ) == 0 )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Got matched response for with record: %s\n", pRecord->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
|
||||
delete m_mapUnacknowledgedCommits[ idx ];
|
||||
m_mapUnacknowledgedCommits.RemoveAt( idx );
|
||||
}
|
||||
else
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Ignoring stale response with record: %s\n", pRecord->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Force a spew of all unacknowledged commits
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTrackerManager::DBG_SpewPendingCommits()
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "Unacknowledged commits: %d\n", m_mapUnacknowledgedCommits.Count() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
FOR_EACH_MAP( m_mapUnacknowledgedCommits, i )
|
||||
{
|
||||
SO_TRACKER_SPEW( CFmtStr( "%d: %s\n", i, m_mapUnacknowledgedCommits[ i ]->m_pProtoMsg->DebugString().c_str() ), SO_TRACKER_SPEW_GC_COMMITS );
|
||||
}
|
||||
}
|
||||
|
||||
#if ( defined( DEBUG ) || defined( STAGING_ONLY ) ) && defined( GAME_DLL )
|
||||
CON_COMMAND( tf_quests_spew_unacknowledged_commits, "Spews info on all unacknowledged commits" )
|
||||
{
|
||||
// QuestObjectiveManager()->DBG_SpewPendingCommits();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef SHARED_OBJECT_MANAGER_H
|
||||
#define SHARED_OBJECT_MANAGER_H
|
||||
|
||||
#include "GameEventListener.h"
|
||||
#include "econ_item_constants.h"
|
||||
#include "econ_item_inventory.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#include "local_steam_shared_object_listener.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined( _WIN32 )
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
using namespace GCSDK;
|
||||
class CSOTrackerManager;
|
||||
|
||||
extern short g_nQuestSpewFlags;
|
||||
#define SO_TRACKER_SPEW_OBJECTIVES 1<<0
|
||||
#define SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT 1<<1
|
||||
#define SO_TRACKER_SPEW_GC_COMMITS 1<<2
|
||||
#define SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT 1<<3
|
||||
#define SO_TRACKER_SPEW_SOCACHE_ACTIVITY 1<<4
|
||||
#define SO_TRACKER_SPEW_TRACKER_ACCEPTANCE 1<<5
|
||||
void SOTrackerSpew( const char* pszBuff, int nType );
|
||||
#define SO_TRACKER_SPEW( pszBuff, nType ) SOTrackerSpew( pszBuff, nType );
|
||||
|
||||
class CBaseSOTracker
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS_NOBASE( CBaseSOTracker )
|
||||
|
||||
CBaseSOTracker( const CSharedObject* pSObject, CSteamID steamIDOwner, CSOTrackerManager* pManager );
|
||||
virtual ~CBaseSOTracker();
|
||||
|
||||
const CSharedObject* GetSObject() const { return m_pSObject; }
|
||||
const CSteamID GetOwnerSteamID() const { return m_steamIDOwner; }
|
||||
const CTFPlayer* GetTrackedPlayer() const { return ToTFPlayer( GetPlayerBySteamID( m_steamIDOwner ) ); }
|
||||
virtual void Spew() const;
|
||||
|
||||
virtual void CommitChangesToDB() = 0;
|
||||
virtual void OnUpdate() = 0;
|
||||
virtual void OnRemove() = 0;
|
||||
protected:
|
||||
|
||||
const CSharedObject* m_pSObject;
|
||||
CSteamID m_steamIDOwner;
|
||||
CSOTrackerManager* m_pManager;
|
||||
};
|
||||
|
||||
struct CommitRecord_t
|
||||
{
|
||||
CommitRecord_t( ::google::protobuf::Message* pMessage )
|
||||
: m_flLastCommitTime( Plat_FloatTime() )
|
||||
, m_flReportedTime( Plat_FloatTime() )
|
||||
, m_pProtoMsg( pMessage )
|
||||
{}
|
||||
|
||||
~CommitRecord_t()
|
||||
{
|
||||
if ( m_pProtoMsg )
|
||||
delete m_pProtoMsg;
|
||||
}
|
||||
|
||||
double m_flLastCommitTime;
|
||||
double m_flReportedTime;
|
||||
::google::protobuf::Message* m_pProtoMsg;
|
||||
|
||||
private:
|
||||
CommitRecord_t(); // Nope
|
||||
};
|
||||
typedef CUtlMap< uint64, CommitRecord_t* > CommitsMap_t;
|
||||
|
||||
// A class to handle the creation and deletion of shared object trackers. Automatically
|
||||
// subscribes to the local player's SOCache and will subscribe to any connecting players'
|
||||
// SOCaches when they connect.
|
||||
#ifdef GAME_DLL
|
||||
class CSOTrackerManager : public ISharedObjectListener, public CGameEventListener, public CAutoGameSystemPerFrame
|
||||
#else
|
||||
class CSOTrackerManager : public CLocalSteamSharedObjectListener, public CGameEventListener, public CAutoGameSystemPerFrame
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
|
||||
DECLARE_CLASS_NOBASE( CSOTrackerManager )
|
||||
|
||||
typedef CUtlMap< uint64, CBaseSOTracker * > SOTrackerMap_t;
|
||||
|
||||
CSOTrackerManager();
|
||||
virtual ~CSOTrackerManager();
|
||||
|
||||
virtual void Initialize();
|
||||
virtual void Shutdown();
|
||||
|
||||
virtual void FireGameEvent( IGameEvent *pEvent ) OVERRIDE;
|
||||
void EnsureTrackersForPlayer( const CSteamID& steamIDPlayer );
|
||||
void EnsureTrackersForPlayer( CTFPlayer* pPlayer );
|
||||
|
||||
void SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
|
||||
void PreSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE {};
|
||||
void SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
|
||||
void PostSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE {};
|
||||
void SODestroyed( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) OVERRIDE;
|
||||
void SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE;
|
||||
void SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void FrameUpdatePreEntityThink() OVERRIDE;
|
||||
void AddCommitRecord( const ::google::protobuf::Message* pRecord, uint64 nKey, bool bRequireResponse );
|
||||
void AcknowledgeCommit( const ::google::protobuf::Message* pRecord, uint64 nKey );
|
||||
void DBG_SpewPendingCommits();
|
||||
#endif
|
||||
void Spew();
|
||||
|
||||
|
||||
virtual SOTrackerMap_t::KeyType_t GetKeyForObjectTracker( const CSharedObject* pItem, CSteamID steamIDOwner ) = 0;
|
||||
CBaseSOTracker* GetTracker( SOTrackerMap_t::KeyType_t nKey ) const;
|
||||
template< class T >
|
||||
T GetTypedTracker( SOTrackerMap_t::KeyType_t nKey ) const { return assert_cast< T >( GetTracker( nKey ) ); }
|
||||
CommitRecord_t* GetCommitRecord( CommitsMap_t::KeyType_t );
|
||||
protected:
|
||||
|
||||
enum ETrackerHandling_t
|
||||
{
|
||||
TRACKER_CREATE_OR_UPDATE = 0,
|
||||
TRACKER_REMOVE,
|
||||
};
|
||||
|
||||
void UpdateTrackerForItem( const CSharedObject* pItem, ETrackerHandling_t eHandling, CSteamID steamIDOwner );
|
||||
|
||||
private:
|
||||
|
||||
virtual int GetType() const = 0;
|
||||
virtual const char* GetName() const = 0;
|
||||
virtual CFmtStr GetDebugObjectDescription( const CSharedObject* pItem ) const = 0;
|
||||
virtual CBaseSOTracker* AllocateNewTracker( const CSharedObject* pItem, CSteamID steamIDOwner, CSOTrackerManager* pManager ) const = 0;
|
||||
virtual ::google::protobuf::Message* AllocateNewProtoMessage() const = 0;
|
||||
virtual void OnCommitRecieved( const ::google::protobuf::Message* pProtoMsg ) = 0;
|
||||
virtual bool ShouldTrackObject( const CSteamID & steamIDOwner, const CSharedObject *pObject ) const = 0;
|
||||
virtual int CompareRecords( const ::google::protobuf::Message* pNewProtoMsg, const ::google::protobuf::Message* pExistingProtoMsg ) const = 0;
|
||||
|
||||
void HandleSOEvent( const CSteamID & steamIDOwner, const CSharedObject *pObject, ETrackerHandling_t eHandling );
|
||||
void CommitAllChanges();
|
||||
void CreateAndAddTracker( const CSharedObject* pItem, CSteamID steamIDOwner );
|
||||
void RemoveAndDeleteTrackerAtIndex( SOTrackerMap_t::IndexType_t idx );
|
||||
void RemoveTrackersForSteamID( const CSteamID & steamIDOwner );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void CommitRecord( CommitRecord_t* pRecord ) const;
|
||||
virtual void SendMessageForCommit( const ::google::protobuf::Message* pProtoMessage ) const = 0;
|
||||
#endif
|
||||
|
||||
double m_flLastUnacknowledgeCommitTime;
|
||||
CommitsMap_t m_mapUnacknowledgedCommits;
|
||||
SOTrackerMap_t m_mapItemTrackers;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // SHARED_OBJECT_MANAGER_H
|
||||
@@ -0,0 +1,281 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "tf_classdata.h"
|
||||
|
||||
extern bool UseHWMorphModels();
|
||||
|
||||
// Player class files
|
||||
#define TF_CLASS_UNDEFINED_FILE ""
|
||||
#define TF_CLASS_SCOUT_FILE "scripts/playerclasses/scout"
|
||||
#define TF_CLASS_SNIPER_FILE "scripts/playerclasses/sniper"
|
||||
#define TF_CLASS_SOLDIER_FILE "scripts/playerclasses/soldier"
|
||||
#define TF_CLASS_DEMOMAN_FILE "scripts/playerclasses/demoman"
|
||||
#define TF_CLASS_MEDIC_FILE "scripts/playerclasses/medic"
|
||||
#define TF_CLASS_HEAVYWEAPONS_FILE "scripts/playerclasses/heavyweapons"
|
||||
#define TF_CLASS_PYRO_FILE "scripts/playerclasses/pyro"
|
||||
#define TF_CLASS_SPY_FILE "scripts/playerclasses/spy"
|
||||
#define TF_CLASS_ENGINEER_FILE "scripts/playerclasses/engineer"
|
||||
#define TF_CLASS_CIVILIAN_FILE "scripts/playerclasses/civilian"
|
||||
|
||||
const char *s_aPlayerClassFiles[] =
|
||||
{
|
||||
TF_CLASS_UNDEFINED_FILE,
|
||||
TF_CLASS_SCOUT_FILE,
|
||||
TF_CLASS_SNIPER_FILE,
|
||||
TF_CLASS_SOLDIER_FILE,
|
||||
TF_CLASS_DEMOMAN_FILE,
|
||||
TF_CLASS_MEDIC_FILE,
|
||||
TF_CLASS_HEAVYWEAPONS_FILE,
|
||||
TF_CLASS_PYRO_FILE,
|
||||
TF_CLASS_SPY_FILE,
|
||||
TF_CLASS_ENGINEER_FILE,
|
||||
TF_CLASS_CIVILIAN_FILE
|
||||
};
|
||||
|
||||
|
||||
CTFPlayerClassDataMgr s_TFPlayerClassDataMgr;
|
||||
CTFPlayerClassDataMgr *g_pTFPlayerClassDataMgr = &s_TFPlayerClassDataMgr;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
TFPlayerClassData_t::TFPlayerClassData_t()
|
||||
{
|
||||
m_szClassName[0] = '\0';
|
||||
m_szModelName[0] = '\0';
|
||||
m_szHWMModelName[0] = '\0';
|
||||
m_szHandModelName[0] = '\0';
|
||||
m_szLocalizableName[0] = '\0';
|
||||
m_flMaxSpeed = 0.0f;
|
||||
m_nMaxHealth = 0;
|
||||
m_nMaxArmor = 0;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
for ( int i = 0; i < ARRAYSIZE( m_szDeathSound ); ++i )
|
||||
{
|
||||
m_szDeathSound[ i ][ 0 ] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
for ( int iWeapon = 0; iWeapon < TF_PLAYER_WEAPON_COUNT; ++iWeapon )
|
||||
{
|
||||
m_aWeapons[iWeapon] = TF_WEAPON_NONE;
|
||||
}
|
||||
|
||||
for ( int iGrenade = 0; iGrenade < TF_PLAYER_GRENADE_COUNT; ++iGrenade )
|
||||
{
|
||||
m_aGrenades[iGrenade] = TF_WEAPON_NONE;
|
||||
}
|
||||
|
||||
for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
|
||||
{
|
||||
m_aAmmoMax[iAmmo] = TF_AMMO_DUMMY;
|
||||
}
|
||||
|
||||
for ( int iBuildable = 0; iBuildable < TF_PLAYER_BLUEPRINT_COUNT; ++iBuildable )
|
||||
{
|
||||
m_aBuildable[iBuildable] = OBJ_LAST;
|
||||
}
|
||||
|
||||
m_bParsed = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *TFPlayerClassData_t::GetModelName() const
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( UseHWMorphModels() )
|
||||
{
|
||||
if ( m_szHWMModelName[0] != '\0' )
|
||||
{
|
||||
return m_szHWMModelName;
|
||||
}
|
||||
}
|
||||
|
||||
return m_szModelName;
|
||||
#else
|
||||
return m_szModelName;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
const char *TFPlayerClassData_t::GetDeathSound( int nType )
|
||||
{
|
||||
return m_szDeathSound[ nType ];
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void TFPlayerClassData_t::Parse( const char *szName )
|
||||
{
|
||||
// Have we parsed this file already?
|
||||
if ( m_bParsed )
|
||||
return;
|
||||
|
||||
// Parse class file.
|
||||
const unsigned char *pKey = GetTFEncryptionKey();
|
||||
KeyValues *pKV = ReadEncryptedKVFile( filesystem, szName, pKey );
|
||||
if ( pKV )
|
||||
{
|
||||
ParseData( pKV );
|
||||
pKV->deleteThis();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void TFPlayerClassData_t::ParseData( KeyValues *pKeyValuesData )
|
||||
{
|
||||
// Attributes.
|
||||
Q_strncpy( m_szClassName, pKeyValuesData->GetString( "name" ), TF_NAME_LENGTH );
|
||||
|
||||
// Load the high res model or the lower res model.
|
||||
if ( !IsX360() )
|
||||
{
|
||||
Q_strncpy( m_szHWMModelName, pKeyValuesData->GetString( "model_hwm" ), TF_NAME_LENGTH );
|
||||
}
|
||||
Q_strncpy( m_szModelName, pKeyValuesData->GetString( "model" ), TF_NAME_LENGTH );
|
||||
Q_strncpy( m_szHandModelName, pKeyValuesData->GetString( "model_hands" ), TF_NAME_LENGTH );
|
||||
Q_strncpy( m_szLocalizableName, pKeyValuesData->GetString( "localize_name" ), TF_NAME_LENGTH );
|
||||
|
||||
m_flMaxSpeed = pKeyValuesData->GetFloat( "speed_max" );
|
||||
m_nMaxHealth = pKeyValuesData->GetInt( "health_max" );
|
||||
m_nMaxArmor = pKeyValuesData->GetInt( "armor_max" );
|
||||
|
||||
// Weapons.
|
||||
int i;
|
||||
char buf[32];
|
||||
for ( i=0;i<TF_PLAYER_WEAPON_COUNT;i++ )
|
||||
{
|
||||
Q_snprintf( buf, sizeof(buf), "weapon%d", i+1 );
|
||||
m_aWeapons[i] = GetWeaponId( pKeyValuesData->GetString( buf ) );
|
||||
}
|
||||
|
||||
// Grenades.
|
||||
m_aGrenades[0] = GetWeaponId( pKeyValuesData->GetString( "grenade1" ) );
|
||||
m_aGrenades[1] = GetWeaponId( pKeyValuesData->GetString( "grenade2" ) );
|
||||
|
||||
// Ammo Max.
|
||||
KeyValues *pAmmoKeyValuesData = pKeyValuesData->FindKey( "AmmoMax" );
|
||||
if ( pAmmoKeyValuesData )
|
||||
{
|
||||
for ( int iAmmo = 1; iAmmo < TF_AMMO_COUNT; ++iAmmo )
|
||||
{
|
||||
m_aAmmoMax[iAmmo] = pAmmoKeyValuesData->GetInt( GetAmmoName( iAmmo ), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
// Buildables
|
||||
for ( i=0;i<TF_PLAYER_BLUEPRINT_COUNT;i++ )
|
||||
{
|
||||
Q_snprintf( buf, sizeof(buf), "buildable%d", i+1 );
|
||||
m_aBuildable[i] = GetBuildableId( pKeyValuesData->GetString( buf ) );
|
||||
}
|
||||
|
||||
// Temp animation flags
|
||||
m_bDontDoAirwalk = ( pKeyValuesData->GetInt( "DontDoAirwalk", 0 ) > 0 );
|
||||
m_bDontDoNewJump = ( pKeyValuesData->GetInt( "DontDoNewJump", 0 ) > 0 );
|
||||
|
||||
m_vecThirdPersonOffset.x = pKeyValuesData->GetFloat( "cameraoffset_forward" );
|
||||
m_vecThirdPersonOffset.y = pKeyValuesData->GetFloat( "cameraoffset_right" );
|
||||
m_vecThirdPersonOffset.z = pKeyValuesData->GetFloat( "cameraoffset_up" );
|
||||
|
||||
#ifdef GAME_DLL // right now we only emit these sounds from server. if that changes we can do this in both dlls
|
||||
|
||||
// Death Sounds
|
||||
Q_strncpy( m_szDeathSound[ DEATH_SOUND_GENERIC ], pKeyValuesData->GetString( "sound_death", "Player.Death" ), MAX_PLAYERCLASS_SOUND_LENGTH );
|
||||
Q_strncpy( m_szDeathSound[ DEATH_SOUND_CRIT ], pKeyValuesData->GetString( "sound_crit_death", "TFPlayer.CritDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH );
|
||||
Q_strncpy( m_szDeathSound[ DEATH_SOUND_MELEE ], pKeyValuesData->GetString( "sound_melee_death", "Player.MeleeDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH );
|
||||
Q_strncpy( m_szDeathSound[ DEATH_SOUND_EXPLOSION ], pKeyValuesData->GetString( "sound_explosion_death", "Player.ExplosionDeath" ), MAX_PLAYERCLASS_SOUND_LENGTH );
|
||||
#endif
|
||||
|
||||
// The file has been parsed.
|
||||
m_bParsed = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void TFPlayerClassData_t::AddAdditionalPlayerDeathSounds( void )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
for ( int i = DEATH_SOUND_FIRST; i <= DEATH_SOUND_LAST; ++i )
|
||||
{
|
||||
CopySoundNameWithModifierToken( m_szDeathSound[ i + DEATH_SOUND_MVM_FIRST ], m_szDeathSound[ i ], ARRAYSIZE( m_szDeathSound[0] ), "MVM_" );
|
||||
CopySoundNameWithModifierToken( m_szDeathSound[ i + DEATH_SOUND_GIANT_MVM_FIRST ], m_szDeathSound[ i ], ARRAYSIZE( m_szDeathSound[0] ), "M_MVM_" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFPlayerClassDataMgr::CTFPlayerClassDataMgr()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFPlayerClassDataMgr::Init( void )
|
||||
{
|
||||
// Special case the undefined class.
|
||||
TFPlayerClassData_t *pClassData = &m_aTFPlayerClassData[TF_CLASS_UNDEFINED];
|
||||
Assert( pClassData );
|
||||
Q_strncpy( pClassData->m_szClassName, "undefined", TF_NAME_LENGTH );
|
||||
Q_strncpy( pClassData->m_szModelName, "models/player/scout.mdl", TF_NAME_LENGTH ); // Undefined players still need a model
|
||||
Q_strncpy( pClassData->m_szLocalizableName, "undefined", TF_NAME_LENGTH );
|
||||
|
||||
// Initialize the classes.
|
||||
for ( int iClass = 1; iClass < TF_CLASS_COUNT_ALL; ++iClass )
|
||||
{
|
||||
COMPILE_TIME_ASSERT( ARRAYSIZE( s_aPlayerClassFiles ) == TF_CLASS_COUNT_ALL );
|
||||
pClassData = &m_aTFPlayerClassData[iClass];
|
||||
Assert( pClassData );
|
||||
pClassData->Parse( s_aPlayerClassFiles[iClass] );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Helper function to get player class data.
|
||||
//-----------------------------------------------------------------------------
|
||||
TFPlayerClassData_t *CTFPlayerClassDataMgr::Get( unsigned int iClass )
|
||||
{
|
||||
Assert ( iClass < TF_CLASS_COUNT_ALL );
|
||||
return &m_aTFPlayerClassData[iClass];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
TFPlayerClassData_t *GetPlayerClassData( unsigned int iClass )
|
||||
{
|
||||
return g_pTFPlayerClassDataMgr->Get( iClass );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerClassDataMgr::AddAdditionalPlayerDeathSounds( void )
|
||||
{
|
||||
for ( int iClass = 1; iClass < TF_CLASS_COUNT_ALL; ++iClass )
|
||||
{
|
||||
TFPlayerClassData_t *pClassData = &m_aTFPlayerClassData[iClass];
|
||||
Assert( pClassData );
|
||||
pClassData->AddAdditionalPlayerDeathSounds();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_CLASSDATA_H
|
||||
#define TF_CLASSDATA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cache structure for the TF player class data (includes citizen).
|
||||
//-----------------------------------------------------------------------------
|
||||
#define MAX_PLAYERCLASS_SOUND_LENGTH 128
|
||||
#define TF_NAME_LENGTH 128
|
||||
|
||||
|
||||
#define DEATH_SOUND_FIRST ( DEATH_SOUND_GENERIC )
|
||||
#define DEATH_SOUND_LAST ( DEATH_SOUND_EXPLOSION )
|
||||
#define DEATH_SOUND_MVM_FIRST ( DEATH_SOUND_GENERIC_MVM )
|
||||
#define DEATH_SOUND_MVM_LAST ( DEATH_SOUND_EXPLOSION_MVM )
|
||||
#define DEATH_SOUND_GIANT_MVM_FIRST ( DEATH_SOUND_GENERIC_GIANT_MVM )
|
||||
#define DEATH_SOUND_GIANT_MVM_LAST ( DEATH_SOUND_EXPLOSION_GIANT_MVM )
|
||||
|
||||
enum DeathSoundType_t
|
||||
{
|
||||
DEATH_SOUND_GENERIC = 0,
|
||||
DEATH_SOUND_CRIT,
|
||||
DEATH_SOUND_MELEE,
|
||||
DEATH_SOUND_EXPLOSION,
|
||||
|
||||
DEATH_SOUND_GENERIC_MVM,
|
||||
DEATH_SOUND_CRIT_MVM,
|
||||
DEATH_SOUND_MELEE_MVM,
|
||||
DEATH_SOUND_EXPLOSION_MVM,
|
||||
|
||||
DEATH_SOUND_GENERIC_GIANT_MVM,
|
||||
DEATH_SOUND_CRIT_GIANT_MVM,
|
||||
DEATH_SOUND_MELEE_GIANT_MVM,
|
||||
DEATH_SOUND_EXPLOSION_GIANT_MVM,
|
||||
|
||||
DEATH_SOUND_TOTAL
|
||||
};
|
||||
|
||||
struct TFPlayerClassData_t
|
||||
{
|
||||
char m_szClassName[TF_NAME_LENGTH];
|
||||
char m_szModelName[TF_NAME_LENGTH];
|
||||
char m_szHWMModelName[TF_NAME_LENGTH];
|
||||
char m_szHandModelName[TF_NAME_LENGTH];
|
||||
char m_szLocalizableName[TF_NAME_LENGTH];
|
||||
float m_flMaxSpeed;
|
||||
int m_nMaxHealth;
|
||||
int m_nMaxArmor;
|
||||
int m_aWeapons[TF_PLAYER_WEAPON_COUNT];
|
||||
int m_aGrenades[TF_PLAYER_GRENADE_COUNT];
|
||||
int m_aAmmoMax[TF_AMMO_COUNT];
|
||||
int m_aBuildable[TF_PLAYER_BLUEPRINT_COUNT];
|
||||
|
||||
bool m_bDontDoAirwalk;
|
||||
bool m_bDontDoNewJump;
|
||||
|
||||
bool m_bParsed;
|
||||
Vector m_vecThirdPersonOffset;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// sounds
|
||||
char m_szDeathSound[ DEATH_SOUND_TOTAL ][MAX_PLAYERCLASS_SOUND_LENGTH];
|
||||
#endif
|
||||
|
||||
TFPlayerClassData_t();
|
||||
const char *GetModelName() const;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
const char *GetDeathSound( int nType );
|
||||
#endif
|
||||
|
||||
void Parse( const char *pszClassName );
|
||||
void ParseData( KeyValues *pKeyValuesData );
|
||||
void AddAdditionalPlayerDeathSounds( void );
|
||||
};
|
||||
|
||||
|
||||
class CTFPlayerClassDataMgr : public CAutoGameSystem
|
||||
{
|
||||
public:
|
||||
|
||||
CTFPlayerClassDataMgr();
|
||||
virtual bool Init( void );
|
||||
TFPlayerClassData_t *Get( unsigned int iClass );
|
||||
void AddAdditionalPlayerDeathSounds( void );
|
||||
private:
|
||||
|
||||
TFPlayerClassData_t m_aTFPlayerClassData[TF_CLASS_COUNT_ALL];
|
||||
};
|
||||
|
||||
extern CTFPlayerClassDataMgr *g_pTFPlayerClassDataMgr;
|
||||
|
||||
// Legacy.
|
||||
TFPlayerClassData_t *GetPlayerClassData( unsigned int iClass );
|
||||
|
||||
#endif // TF_CLASSDATA_H
|
||||
@@ -0,0 +1,20 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Code for the CTFCoachRating object
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_coach_rating.h"
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
#ifdef GC_DLL
|
||||
IMPLEMENT_CLASS_MEMPOOL( CTFCoachRating, 100, UTLMEMORYPOOL_GROW_SLOW );
|
||||
|
||||
#endif
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Holds the CTFCoachRating object
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TF_COACH_RATING_H
|
||||
#define TF_COACH_RATING_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef GC
|
||||
#include "gcsdk/schemasharedobject.h"
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//---------------------------------------------------------------------------------
|
||||
class CTFCoachRating : public GCSDK::CSchemaSharedObject< CSchCoachRating, k_EEconTypeCoachRating >
|
||||
{
|
||||
#ifdef GC_DLL
|
||||
DECLARE_CLASS_MEMPOOL( CTFCoachRating );
|
||||
#endif
|
||||
|
||||
public:
|
||||
CTFCoachRating() {}
|
||||
CTFCoachRating( uint32 unAccountID )
|
||||
{
|
||||
Obj().m_unAccountIDCoach = unAccountID;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // GC
|
||||
|
||||
#endif // TF_COACH_RATING_H
|
||||
@@ -0,0 +1,397 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Condition Objects
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_condition.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#include "achievementmgr.h"
|
||||
#include "baseachievement.h"
|
||||
#include "achievements_tf.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
BEGIN_PREDICTION_DATA_NO_BASE( CTFConditionList )
|
||||
DEFINE_PRED_FIELD( _condition_bits, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
|
||||
END_PREDICTION_DATA()
|
||||
|
||||
BEGIN_RECV_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive )
|
||||
RecvPropInt( RECVINFO( _condition_bits ) ),
|
||||
END_RECV_TABLE()
|
||||
|
||||
#else
|
||||
|
||||
BEGIN_SEND_TABLE_NOBASE( CTFConditionList, DT_TFPlayerConditionListExclusive )
|
||||
SendPropInt( SENDINFO( _condition_bits ), MIN( TF_COND_LAST, 32 ), SPROP_UNSIGNED ),
|
||||
END_SEND_TABLE()
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Ctor
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFConditionList::CTFConditionList()
|
||||
{
|
||||
_condition_bits = _old_condition_bits = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Condition factory.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFConditionList::Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
return _Add( type, duration, outer, provider );
|
||||
#else
|
||||
return type == TF_COND_CRITBOOSTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CTFConditionList::_Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
|
||||
{
|
||||
// If we already have a condition of this type, ask it to handle the addition of another.
|
||||
for ( int i = 0; i < _conditions.Count(); ++i )
|
||||
{
|
||||
if ( _conditions[i]->GetType() == type )
|
||||
{
|
||||
_conditions[i]->Add( duration );
|
||||
_conditions[i]->SetProvider( provider );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new condition.
|
||||
CTFCondition* newCond = NULL;
|
||||
switch ( type )
|
||||
{
|
||||
// TODO: Register new conditions anonymously instead of switching.
|
||||
case TF_COND_CRITBOOSTED:
|
||||
newCond = new CTFCondition_CritBoost( type, duration, outer, provider );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( newCond )
|
||||
{
|
||||
_condition_bits |= (1<<type);
|
||||
_old_condition_bits |= (1<<type);
|
||||
|
||||
_conditions.AddToTail( newCond );
|
||||
newCond->OnAdded();
|
||||
newCond->SetProvider( provider );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Remove a condition from the player.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFConditionList::Remove( ETFCond type, bool ignore_duration )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
bool bConditionListHandledRemoval = _Remove( type, ignore_duration );
|
||||
|
||||
// The condition list only handles one type of condition. Written slightly weird
|
||||
// to avoid unused variable warnings with asserts disabled.
|
||||
if ( bConditionListHandledRemoval )
|
||||
{
|
||||
Assert( type == TF_COND_CRITBOOSTED );
|
||||
}
|
||||
#endif
|
||||
return type == TF_COND_CRITBOOSTED;
|
||||
}
|
||||
|
||||
bool CTFConditionList::_Remove( ETFCond type, bool ignore_duration )
|
||||
{
|
||||
for ( int i=_conditions.Count()-1; i>=0; --i )
|
||||
{
|
||||
CTFCondition* cond = _conditions[i];
|
||||
if ( !cond || cond->GetType() != type )
|
||||
continue;
|
||||
|
||||
if ( cond->UsesMinDuration() && !ignore_duration && cond->GetMinDuration() > 0 )
|
||||
{
|
||||
cond->SetMaxDuration( cond->GetMinDuration() );
|
||||
continue; // Can't remove conditions that haven't expired.
|
||||
}
|
||||
|
||||
_conditions.Remove( i );
|
||||
|
||||
_condition_bits &= ~(1<<type);
|
||||
_old_condition_bits &= ~(1<<type);
|
||||
|
||||
cond->OnRemoved();
|
||||
delete cond;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clear all conditions from the player.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::RemoveAll()
|
||||
{
|
||||
_condition_bits = 0;
|
||||
_old_condition_bits = 0;
|
||||
|
||||
for ( int i=0; i<_conditions.Count(); ++i )
|
||||
{
|
||||
_conditions[i]->OnRemoved();
|
||||
}
|
||||
_conditions.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Checks if we have at least one of a given condition applied.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFConditionList::InCond( ETFCond type ) const
|
||||
{
|
||||
return ( ( _condition_bits & (1<<type) ) != 0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
CBaseEntity *CTFConditionList::GetProvider( ETFCond type ) const
|
||||
{
|
||||
CBaseEntity *pProvider = NULL;
|
||||
for ( int i = 0; i < _conditions.Count(); ++i )
|
||||
{
|
||||
if ( _conditions[i]->GetType() == type )
|
||||
{
|
||||
pProvider = _conditions[i]->GetProvider();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pProvider;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Client/Server periodic condition think.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::Think()
|
||||
{
|
||||
for ( int i=0; i<_conditions.Count(); ++i )
|
||||
{
|
||||
_conditions[i]->OnThink();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Server only per-frame think.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::ServerThink()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
for ( int i=0; i<_conditions.Count(); ++i )
|
||||
{
|
||||
CTFCondition* cond = _conditions[i];
|
||||
if ( cond->GetMaxDuration() > PERMANENT_CONDITION ||
|
||||
cond->GetMinDuration() > PERMANENT_CONDITION )
|
||||
{
|
||||
// Reduce the duration over time.
|
||||
float reduction = gpGlobals->frametime;
|
||||
|
||||
// Healable conditions expire faster when we have healers.
|
||||
int numHealers = cond->GetOuter()->m_Shared.GetNumHealers();
|
||||
if ( cond->IsHealable() && numHealers > 0 )
|
||||
{
|
||||
reduction += numHealers * reduction * 4;
|
||||
}
|
||||
|
||||
// Decrement min duration.
|
||||
if ( cond->GetMinDuration() > PERMANENT_CONDITION )
|
||||
{
|
||||
cond->SetMinDuration( MAX( cond->GetMinDuration() - reduction, 0 ) );
|
||||
}
|
||||
|
||||
// Decrement max duration.
|
||||
if ( cond->GetMaxDuration() > PERMANENT_CONDITION )
|
||||
{
|
||||
cond->SetMaxDuration( MAX( cond->GetMaxDuration() - reduction, 0 ) );
|
||||
|
||||
if ( cond->GetMaxDuration() < cond->GetMinDuration() )
|
||||
{
|
||||
cond->SetMaxDuration( cond->GetMinDuration() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( cond->GetMaxDuration() == 0 )
|
||||
{
|
||||
Remove( cond->GetType() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_conditions[i]->OnServerThink();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::OnPreDataChanged( void )
|
||||
{
|
||||
// _old_condition_bits = _condition_bits;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::OnDataChanged( CTFPlayer* outer )
|
||||
{
|
||||
// Is there a way to improve this by hooking directly into network state changed?
|
||||
if ( _old_condition_bits != _condition_bits )
|
||||
{
|
||||
UpdateClientConditions( outer );
|
||||
|
||||
_old_condition_bits = _condition_bits;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates or destroys conditions to make sure our state matches the server.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFConditionList::UpdateClientConditions( CTFPlayer* outer )
|
||||
{
|
||||
int nCondChanged = _condition_bits ^ _old_condition_bits;
|
||||
int nCondAdded = nCondChanged & _condition_bits;
|
||||
int nCondRemoved = nCondChanged & _old_condition_bits;
|
||||
|
||||
int i;
|
||||
for ( i=0;i<TF_COND_LAST;i++ )
|
||||
{
|
||||
if ( nCondAdded & (1<<i) )
|
||||
{
|
||||
_Add( (ETFCond)i, PERMANENT_CONDITION, outer );
|
||||
}
|
||||
else if ( nCondRemoved & (1<<i) )
|
||||
{
|
||||
_Remove( (ETFCond)i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Ctor
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFCondition::CTFCondition( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
|
||||
: _type( type ),
|
||||
_min_duration( 0 ),
|
||||
_max_duration( duration ),
|
||||
_outer( outer ),
|
||||
_provider( provider )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dtor
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFCondition::~CTFCondition()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Called if we try to add another condition of a type we already have.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFCondition::Add( float duration )
|
||||
{
|
||||
if ( duration != PERMANENT_CONDITION )
|
||||
{
|
||||
// If our new duration is not permanent, is shorter than
|
||||
// our current duration, and is longer than our min duration
|
||||
// make it our new min duration.
|
||||
if ( GetMaxDuration() == PERMANENT_CONDITION ||
|
||||
duration < GetMaxDuration() )
|
||||
{
|
||||
if ( duration > GetMinDuration() )
|
||||
SetMinDuration( duration );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( GetMaxDuration() != PERMANENT_CONDITION )
|
||||
{
|
||||
// If our current duration is not permanent and we are adding a
|
||||
// permanent duration, make our old finite duration the new min duration.
|
||||
// This ensures we last at least that long.
|
||||
SetMinDuration( GetMaxDuration() );
|
||||
}
|
||||
|
||||
SetMaxDuration( duration );
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Crit Boost
|
||||
//=============================================================================
|
||||
CTFCondition_CritBoost::CTFCondition_CritBoost( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider /*= NULL*/ )
|
||||
: CTFCondition( type, duration, outer, provider )
|
||||
{
|
||||
Assert( type == TF_COND_CRITBOOSTED );
|
||||
}
|
||||
|
||||
void CTFCondition_CritBoost::OnAdded()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
GetOuter()->m_Shared.UpdateCritBoostEffect();
|
||||
|
||||
if ( GetOuter()->IsLocalPlayer() && GetOuter()->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
|
||||
{
|
||||
g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_RECEIVE_UBER_GRIND );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTFCondition_CritBoost::OnRemoved()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
GetOuter()->m_Shared.UpdateCritBoostEffect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTFCondition_CritBoost::OnThink()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( GetOuter()->m_pCritBoostEffect )
|
||||
{
|
||||
CBaseEntity *pWeapon = NULL;
|
||||
// Use GetRenderedWeaponModel() instead?
|
||||
if ( GetOuter()->IsLocalPlayer() )
|
||||
{
|
||||
pWeapon = GetOuter()->GetViewModel(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
pWeapon = GetOuter()->GetActiveWeapon();
|
||||
}
|
||||
|
||||
// Transfer the crit boosted effect if we've switched weapons
|
||||
if ( GetOuter()->m_pCritBoostEffect->GetOwner() != pWeapon )
|
||||
{
|
||||
GetOuter()->m_Shared.UpdateCritBoostEffect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTFCondition_CritBoost::OnServerThink()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Condition Objects
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_CONDITION_H
|
||||
#define TF_CONDITION_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "utlstack.h"
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// Avoid redef warnings
|
||||
#undef CTFPlayer
|
||||
#define CTFPlayer C_TFPlayer
|
||||
class C_TFPlayer;
|
||||
#endif
|
||||
|
||||
class CTFPlayer;
|
||||
class CTFCondition;
|
||||
|
||||
class CTFConditionList
|
||||
{
|
||||
public:
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
DECLARE_CLASS_NOBASE( CTFConditionList );
|
||||
DECLARE_PREDICTABLE();
|
||||
|
||||
CTFConditionList();
|
||||
|
||||
bool Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider = NULL );
|
||||
bool _Add( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider = NULL );
|
||||
bool Remove( ETFCond type, bool ignore_duration=false );
|
||||
bool _Remove( ETFCond type, bool ignore_duration=false );
|
||||
void RemoveAll();
|
||||
|
||||
bool InCond( ETFCond type ) const;
|
||||
CBaseEntity *GetProvider( ETFCond type ) const;
|
||||
|
||||
void Think();
|
||||
void ServerThink();
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// Forwarded from player shared.
|
||||
virtual void OnPreDataChanged( void );
|
||||
virtual void OnDataChanged( CTFPlayer* outer );
|
||||
void UpdateClientConditions( CTFPlayer* outer );
|
||||
#endif
|
||||
|
||||
private:
|
||||
CUtlVector< CTFCondition* > _conditions;
|
||||
|
||||
CNetworkVar( int, _condition_bits ); // Bitfield of set conditions for fast checking.
|
||||
int _old_condition_bits;
|
||||
};
|
||||
|
||||
class CTFCondition
|
||||
{
|
||||
public:
|
||||
CTFCondition( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider = NULL );
|
||||
virtual ~CTFCondition();
|
||||
|
||||
virtual void Add( float duration );
|
||||
|
||||
virtual void OnAdded() = 0;
|
||||
virtual void OnRemoved() = 0;
|
||||
virtual void OnThink() = 0;
|
||||
virtual void OnServerThink() = 0;
|
||||
|
||||
// Condition Traits
|
||||
virtual bool IsHealable() { return false; }
|
||||
virtual bool UsesMinDuration() { return false; }
|
||||
|
||||
ETFCond GetType() { return _type; }
|
||||
float GetMaxDuration() { return _max_duration; }
|
||||
void SetMaxDuration( float val ) { _max_duration = val; }
|
||||
float GetMinDuration() { return _min_duration; }
|
||||
void SetMinDuration( float val ) { if ( UsesMinDuration() ) { _min_duration = val; } }
|
||||
CTFPlayer* GetOuter() { return _outer; }
|
||||
void SetProvider( CBaseEntity *provider ) { _provider = provider; }
|
||||
CBaseEntity* GetProvider() { return _provider; }
|
||||
|
||||
private:
|
||||
float _min_duration;
|
||||
float _max_duration;
|
||||
const ETFCond _type;
|
||||
CTFPlayer* _outer;
|
||||
CHandle< CBaseEntity > _provider;
|
||||
};
|
||||
|
||||
class CTFCondition_CritBoost : public CTFCondition
|
||||
{
|
||||
public:
|
||||
CTFCondition_CritBoost( ETFCond type, float duration, CTFPlayer* outer, CBaseEntity* provider = NULL );
|
||||
|
||||
virtual void OnAdded();
|
||||
virtual void OnRemoved();
|
||||
virtual void OnThink();
|
||||
virtual void OnServerThink();
|
||||
|
||||
// Condition Traits
|
||||
virtual bool IsHealable() { return false; }
|
||||
virtual bool UsesMinDuration() { return true; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,649 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_dropped_weapon.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_tf_player.h"
|
||||
#include "model_types.h"
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#include "tf_weaponbase.h"
|
||||
#include "tf_weapon_medigun.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weapon_bottle.h"
|
||||
#endif // GAME_DLL
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
ConVar tf_dropped_weapon_lifetime( "tf_dropped_weapon_lifetime", "30", FCVAR_CHEAT );
|
||||
|
||||
EXTERN_SEND_TABLE( DT_ScriptCreatedItem );
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_dropped_weapon, CTFDroppedWeapon );
|
||||
|
||||
PRECACHE_REGISTER( tf_dropped_weapon );
|
||||
#else
|
||||
EXTERN_RECV_TABLE( DT_ScriptCreatedItem );
|
||||
#endif
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFDroppedWeapon, DT_TFDroppedWeapon );
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTFDroppedWeapon, DT_TFDroppedWeapon )
|
||||
#if !defined( CLIENT_DLL )
|
||||
SendPropDataTable( SENDINFO_DT(m_Item), &REFERENCE_SEND_TABLE(DT_ScriptCreatedItem) ),
|
||||
SendPropFloat( SENDINFO( m_flChargeLevel ) ),
|
||||
#else
|
||||
RecvPropDataTable( RECVINFO_DT(m_Item), 0, &REFERENCE_RECV_TABLE(DT_ScriptCreatedItem) ),
|
||||
RecvPropFloat( RECVINFO( m_flChargeLevel ) ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
IMPLEMENT_AUTO_LIST( IDroppedWeaponAutoList );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFDroppedWeapon::CTFDroppedWeapon()
|
||||
#ifdef GAME_DLL
|
||||
: m_nClip( 0 )
|
||||
, m_nAmmo( 0 )
|
||||
, m_nDetonated( 0 )
|
||||
, m_flEnergy( 0.f )
|
||||
, m_flEffectBarRegenTime( 0.f )
|
||||
#endif
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
m_pGlowEffect = NULL;
|
||||
m_bShouldGlowForLocalPlayer = false;
|
||||
m_flOldChargeLevel = 0.f;
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
m_flChargeLevel.Set( 0.f );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFDroppedWeapon::~CTFDroppedWeapon()
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( m_worldmodelStatTrakAddon )
|
||||
{
|
||||
m_worldmodelStatTrakAddon->Remove();
|
||||
}
|
||||
|
||||
if ( m_effect )
|
||||
{
|
||||
ParticleProp()->StopEmission( m_effect );
|
||||
m_effect = NULL;
|
||||
}
|
||||
|
||||
DestroyGlowEffect();
|
||||
#endif // CLIENT_DLL
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::Spawn()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetBlocksLOS( false );
|
||||
AddEFlags( EFL_NO_ROTORWASH_PUSH );
|
||||
|
||||
// This will make them not collide with the player, but will collide
|
||||
// against other items + weapons
|
||||
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
||||
CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT );
|
||||
|
||||
// Create the object in the physics system
|
||||
int nSolidFlags = GetSolidFlags() | FSOLID_NOT_STANDABLE;
|
||||
|
||||
if ( VPhysicsInitNormal( SOLID_VPHYSICS, nSolidFlags, false ) == NULL )
|
||||
{
|
||||
SetSolid( SOLID_BBOX );
|
||||
AddSolidFlags( nSolidFlags );
|
||||
|
||||
// If it's not physical, drop it to the floor
|
||||
if ( UTIL_DropToFloor( this, MASK_SOLID ) == 0 )
|
||||
{
|
||||
Warning( "Item %s fell out of level at %f,%f,%f\n", GetClassname(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z);
|
||||
UTIL_Remove( this );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GAME_DLL
|
||||
BaseClass::Spawn();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
SetContextThink( &CTFDroppedWeapon::SUB_Remove, gpGlobals->curtime + tf_dropped_weapon_lifetime.GetFloat(), "RemoveThink" );
|
||||
#endif // GAME_DLL
|
||||
}
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::OnPreDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
BaseClass::OnPreDataChanged( updateType );
|
||||
|
||||
m_flOldChargeLevel = m_flChargeLevel;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
BaseClass::OnDataChanged( updateType );
|
||||
|
||||
if ( updateType == DATA_UPDATE_CREATED )
|
||||
{
|
||||
// if its startrak attach a model to it
|
||||
if ( m_Item.GetItemID() != INVALID_ITEM_ID )
|
||||
{
|
||||
int iStrangeType = -1;
|
||||
for ( int i = 0; i < GetKillEaterAttrCount(); i++ )
|
||||
{
|
||||
if ( m_Item.FindAttribute( GetKillEaterAttr_Score( i ) ) )
|
||||
{
|
||||
iStrangeType = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// It's strange, does it have module as well?
|
||||
if ( iStrangeType != -1 )
|
||||
{
|
||||
CAttribute_String attrModule;
|
||||
static CSchemaAttributeDefHandle pAttr_module( "weapon_uses_stattrak_module" );
|
||||
if ( m_Item.FindAttribute( pAttr_module, &attrModule ) && attrModule.has_value() )
|
||||
{
|
||||
static CSchemaAttributeDefHandle pAttr_moduleScale( "weapon_stattrak_module_scale" );
|
||||
// Does it have a stat track module
|
||||
float flScale = 1.0f;
|
||||
uint32 unFloatAsUint32 = 1;
|
||||
if ( m_Item.FindAttribute( pAttr_moduleScale, &unFloatAsUint32 ) )
|
||||
{
|
||||
flScale = (float&)unFloatAsUint32;
|
||||
}
|
||||
|
||||
C_BaseAnimating *pStatTrakEnt = new class C_BaseAnimating;
|
||||
if ( pStatTrakEnt && pStatTrakEnt->InitializeAsClientEntity( "models/weapons/c_models/stattrack.mdl", RENDER_GROUP_OPAQUE_ENTITY ) )
|
||||
{
|
||||
pStatTrakEnt->AddEffects( EF_BONEMERGE );
|
||||
pStatTrakEnt->AddEffects( EF_BONEMERGE_FASTCULL );
|
||||
|
||||
m_worldmodelStatTrakAddon = pStatTrakEnt;
|
||||
pStatTrakEnt->SetParent( this );
|
||||
pStatTrakEnt->SetLocalOrigin( vec3_origin );
|
||||
pStatTrakEnt->UpdatePartitionListEntry();
|
||||
pStatTrakEnt->CollisionProp()->MarkPartitionHandleDirty();
|
||||
pStatTrakEnt->SetModelScale( flScale );
|
||||
pStatTrakEnt->UpdateVisibility();
|
||||
|
||||
pStatTrakEnt->SetBodygroup( 1, 1 );
|
||||
|
||||
pStatTrakEnt->m_nSkin = m_Item.GetTeamNumber(); // Use the "Sad" skin
|
||||
|
||||
//pStatTrakEnt->SetModelScale( 2.0f );
|
||||
// //if ( !cl_righthand.GetBool() )
|
||||
// //{
|
||||
// // pStatTrakEnt->SetBodygroup( 0, 1 ); // use a special mirror-image stattrak module that appears correct for lefties
|
||||
// //}
|
||||
|
||||
RemoveEffects( EF_NODRAW );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal Attached models (ie festive lights)
|
||||
const CEconItemDefinition *pItemDef = m_Item.GetItemDefinition();
|
||||
if ( pItemDef )
|
||||
{
|
||||
// Update the state of additional model attachments
|
||||
m_vecAttachedModels.Purge();
|
||||
int iTeamNumber = m_Item.GetTeamNumber();
|
||||
int iAttachedModels = pItemDef->GetNumAttachedModels( iTeamNumber );
|
||||
for ( int i = 0; i < iAttachedModels; i++ )
|
||||
{
|
||||
attachedmodel_t *pModel = pItemDef->GetAttachedModelData( iTeamNumber, i );
|
||||
|
||||
int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
|
||||
if ( iModelIndex >= 0 )
|
||||
{
|
||||
AttachedModelData_t attachedModelData;
|
||||
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
|
||||
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
|
||||
m_vecAttachedModels.AddToTail( attachedModelData );
|
||||
}
|
||||
}
|
||||
|
||||
// Festive
|
||||
{
|
||||
static CSchemaAttributeDefHandle pAttr_is_festivized( "is_festivized" );
|
||||
if ( pAttr_is_festivized && m_Item.FindAttribute( pAttr_is_festivized ) )
|
||||
{
|
||||
int iAttachedFestiveModels = pItemDef->GetNumAttachedModelsFestivized( iTeamNumber );
|
||||
if ( iAttachedFestiveModels )
|
||||
{
|
||||
|
||||
for ( int i = 0; i < iAttachedFestiveModels; i++ )
|
||||
{
|
||||
attachedmodel_t *pModel = pItemDef->GetAttachedModelDataFestivized( iTeamNumber, i );
|
||||
|
||||
int iModelIndex = modelinfo->GetModelIndex( pModel->m_pszModelName );
|
||||
if ( iModelIndex >= 0 )
|
||||
{
|
||||
AttachedModelData_t attachedModelData;
|
||||
attachedModelData.m_pModel = modelinfo->GetModel( iModelIndex );
|
||||
attachedModelData.m_iModelDisplayFlags = pModel->m_iModelDisplayFlags;
|
||||
m_vecAttachedModels.AddToTail( attachedModelData );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetupParticleEffect();
|
||||
}
|
||||
|
||||
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||||
}
|
||||
|
||||
if ( m_flOldChargeLevel != m_flChargeLevel )
|
||||
{
|
||||
float flRem = fmod( m_flChargeLevel, 0.1f );
|
||||
if ( flRem < 0.01f )
|
||||
{
|
||||
ParticleProp()->Create( "drain_effect", PATTACH_POINT_FOLLOW, LookupAttachment( "muzzle" ) );
|
||||
EmitSound( "Medigun.DrainCharge" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::ModifyEmitSoundParams( EmitSound_t ¶ms )
|
||||
{
|
||||
params.m_nPitch = RemapVal( m_flChargeLevel, 0.f, 1.f, 90, 180 );
|
||||
params.m_nFlags |= SND_CHANGE_PITCH;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFDroppedWeapon::OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
|
||||
{
|
||||
if ( !BaseClass::OnInternalDrawModel( pInfo ) )
|
||||
return false;
|
||||
|
||||
// Draw Attached Models
|
||||
// Draw our attached models as well
|
||||
for ( int i = 0; i < m_vecAttachedModels.Size(); i++ )
|
||||
{
|
||||
const AttachedModelData_t& attachedModel = m_vecAttachedModels[i];
|
||||
|
||||
if ( attachedModel.m_pModel && ( attachedModel.m_iModelDisplayFlags & kAttachedModelDisplayFlag_WorldModel ) )
|
||||
{
|
||||
ClientModelRenderInfo_t infoAttached = *pInfo;
|
||||
|
||||
infoAttached.pRenderable = this;
|
||||
infoAttached.instance = MODEL_INSTANCE_INVALID;
|
||||
infoAttached.entity_index = this->index;
|
||||
infoAttached.pModel = attachedModel.m_pModel;
|
||||
|
||||
infoAttached.pModelToWorld = &infoAttached.modelToWorld;
|
||||
|
||||
// Turns the origin + angles into a matrix
|
||||
AngleMatrix( infoAttached.angles, infoAttached.origin, infoAttached.modelToWorld );
|
||||
|
||||
DrawModelState_t state;
|
||||
matrix3x4_t *pBoneToWorld;
|
||||
bool bMarkAsDrawn = modelrender->DrawModelSetup( infoAttached, &state, NULL, &pBoneToWorld );
|
||||
DoInternalDrawModel( &infoAttached, ( bMarkAsDrawn && ( infoAttached.flags & STUDIO_RENDER ) ) ? &state : NULL, pBoneToWorld );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Get an econ material override for the given team.
|
||||
// Returns: NULL if there is no override.
|
||||
//-----------------------------------------------------------------------------
|
||||
IMaterial *CTFDroppedWeapon::GetEconWeaponMaterialOverride( int iTeam )
|
||||
{
|
||||
CEconItemView *pItemView = GetItem();
|
||||
if ( !pItemView )
|
||||
return NULL;
|
||||
|
||||
return pItemView->GetMaterialOverride( iTeam );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::SetupParticleEffect()
|
||||
{
|
||||
attachedparticlesystem_t *pParticleSystem = NULL;
|
||||
|
||||
// do community_sparkle effect if this is a community item?
|
||||
const int iQualityParticleType = m_Item.GetQualityParticleType();
|
||||
if ( iQualityParticleType > 0 )
|
||||
{
|
||||
pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( iQualityParticleType );
|
||||
}
|
||||
|
||||
if ( !pParticleSystem )
|
||||
{
|
||||
// does this hat even have a particle effect
|
||||
static CSchemaAttributeDefHandle pAttrDef_AttachParticleEffect( "attach particle effect" );
|
||||
uint32 iValue = 0;
|
||||
if ( !m_Item.FindAttribute( pAttrDef_AttachParticleEffect, &iValue ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const float& value_as_float = (float&)iValue;
|
||||
pParticleSystem = GetItemSchema()->GetAttributeControlledParticleSystem( value_as_float );
|
||||
}
|
||||
|
||||
// failed to find any particle effect
|
||||
if ( !pParticleSystem )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Team Color
|
||||
if ( GetTeamNumber() == TF_TEAM_BLUE && V_stristr( pParticleSystem->pszSystemName, "_teamcolor_red" ))
|
||||
{
|
||||
static char pBlue[256];
|
||||
V_StrSubst( pParticleSystem->pszSystemName, "_teamcolor_red", "_teamcolor_blue", pBlue, 256 );
|
||||
pParticleSystem = GetItemSchema()->FindAttributeControlledParticleSystem( pBlue );
|
||||
if ( !pParticleSystem )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// World model effect
|
||||
// Stop it on both the viewmodel & the world model, because it may be removed due to first/thirdperson switch
|
||||
static char pszTempName[256];
|
||||
const char* pszSystemName = pParticleSystem->pszSystemName;
|
||||
|
||||
// Weapon Remap for a Base Effect to be used on a specific weapon
|
||||
if ( pParticleSystem->bUseSuffixName && m_Item.GetItemDefinition()->GetParticleSuffix() )
|
||||
{
|
||||
V_strcpy_safe( pszTempName, pszSystemName );
|
||||
V_strcat_safe( pszTempName, "_" );
|
||||
V_strcat_safe( pszTempName, m_Item.GetItemDefinition()->GetParticleSuffix() );
|
||||
pszSystemName = pszTempName;
|
||||
}
|
||||
|
||||
m_effect = ParticleProp()->Create( pszSystemName, PATTACH_ABSORIGIN_FOLLOW );
|
||||
if ( m_effect )
|
||||
{
|
||||
for ( int i=0; i<ARRAYSIZE( pParticleSystem->pszControlPoints ); ++i )
|
||||
{
|
||||
const char *pszAttachmentName = pParticleSystem->pszControlPoints[i];
|
||||
if ( pszAttachmentName && pszAttachmentName[0] != '\0' )
|
||||
{
|
||||
ParticleProp()->AddControlPoint( m_effect, i, this, PATTACH_POINT_FOLLOW, pParticleSystem->pszControlPoints[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::ClientThink()
|
||||
{
|
||||
C_TFPlayer *pTFPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
bool bShouldGlowForLocalPlayer = pTFPlayer && pTFPlayer->IsAlive() && pTFPlayer->CanPickupDroppedWeapon( this ) && pTFPlayer->IsLineOfSightClear( this );
|
||||
if ( bShouldGlowForLocalPlayer )
|
||||
{
|
||||
// ignore the item that the player's equipped
|
||||
int iLoadoutSlot = 0;
|
||||
CTFItemDefinition *pItemDef = m_Item.GetStaticData();
|
||||
if ( pItemDef )
|
||||
{
|
||||
int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
|
||||
iLoadoutSlot = pItemDef->GetLoadoutSlot( iClass );
|
||||
CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase* >( pTFPlayer->GetEntityForLoadoutSlot( iLoadoutSlot ) );
|
||||
if ( pWeapon && *pWeapon->GetAttributeContainer()->GetItem() == m_Item )
|
||||
{
|
||||
bShouldGlowForLocalPlayer = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_bShouldGlowForLocalPlayer != bShouldGlowForLocalPlayer )
|
||||
{
|
||||
m_bShouldGlowForLocalPlayer = bShouldGlowForLocalPlayer;
|
||||
UpdateGlowEffect();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::UpdateGlowEffect( void )
|
||||
{
|
||||
// destroy the existing effect
|
||||
if ( m_pGlowEffect )
|
||||
{
|
||||
DestroyGlowEffect();
|
||||
}
|
||||
|
||||
// create a new effect if we have a cart
|
||||
if ( m_bShouldGlowForLocalPlayer )
|
||||
{
|
||||
Vector color = Vector( 0.745f, 0.773f, 0.157f );
|
||||
m_pGlowEffect = new CGlowObject( this, color, 1.0, true );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::DestroyGlowEffect( void )
|
||||
{
|
||||
if ( m_pGlowEffect )
|
||||
{
|
||||
delete m_pGlowEffect;
|
||||
m_pGlowEffect = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFDroppedWeapon::IsVisibleToTargetID( void ) const
|
||||
{
|
||||
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( !pLocalPlayer )
|
||||
return false;
|
||||
return pLocalPlayer->CanPickupDroppedWeapon( this );
|
||||
}
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#define MAX_DROPPED_WEAPON_COUNT 32
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFDroppedWeapon *CTFDroppedWeapon::Create( CTFPlayer *pLastOwner, const Vector &vecOrigin, const QAngle &vecAngles, const char *pszModelName, const CEconItemView *pItem )
|
||||
{
|
||||
// don't drop weapon in MVM
|
||||
if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
return NULL;
|
||||
|
||||
int nNumRemoved = 0;
|
||||
|
||||
// make sure we clean up the same item that was dropped before dropping a new one
|
||||
for ( int i=0; i<CTFDroppedWeapon::AutoList().Count(); ++i )
|
||||
{
|
||||
CTFDroppedWeapon *pDroppedWeapon = static_cast< CTFDroppedWeapon* >( CTFDroppedWeapon::AutoList()[i] );
|
||||
if ( pDroppedWeapon->m_Item.GetItemID() == pItem->GetItemID() && pDroppedWeapon->m_Item.GetItemDefIndex() == pItem->GetItemDefIndex() && pDroppedWeapon->m_hPlayer.Get() == pLastOwner )
|
||||
{
|
||||
UTIL_Remove( pDroppedWeapon );
|
||||
nNumRemoved++;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're still going over max dropped weapon count, remove more items
|
||||
int nNumToRemove = CTFDroppedWeapon::AutoList().Count() - nNumRemoved - MAX_DROPPED_WEAPON_COUNT;
|
||||
for ( int i=0; i<CTFDroppedWeapon::AutoList().Count() && nNumToRemove > 0; ++i )
|
||||
{
|
||||
CTFDroppedWeapon *pDroppedWeapon = static_cast< CTFDroppedWeapon* >( CTFDroppedWeapon::AutoList()[i] );
|
||||
|
||||
// skip item that we already marked for deletion
|
||||
if ( pDroppedWeapon->IsMarkedForDeletion() )
|
||||
continue;
|
||||
|
||||
UTIL_Remove( pDroppedWeapon );
|
||||
nNumToRemove--;
|
||||
}
|
||||
|
||||
CTFDroppedWeapon *pDroppedWeapon = static_cast<CTFDroppedWeapon*>( CBaseAnimating::CreateNoSpawn( "tf_dropped_weapon", vecOrigin, vecAngles ) );
|
||||
if ( pDroppedWeapon )
|
||||
{
|
||||
pDroppedWeapon->SetModelName( AllocPooledString( pszModelName ) );
|
||||
pDroppedWeapon->SetItem( pItem );
|
||||
DispatchSpawn( pDroppedWeapon );
|
||||
}
|
||||
|
||||
return pDroppedWeapon;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::InitDroppedWeapon( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon, bool bSwap, bool bIsSuicide /*= false*/ )
|
||||
{
|
||||
m_hPlayer = pPlayer;
|
||||
|
||||
// Calculate the initial impulse on the weapon.
|
||||
Vector vecImpulse( 0.0f, 0.0f, 0.0f );
|
||||
float flImpulseScale = 0.f;
|
||||
if ( bSwap && pPlayer )
|
||||
{
|
||||
Vector vecForward, vecUp;
|
||||
AngleVectors( pPlayer->EyeAngles(), &vecForward, NULL, &vecUp );
|
||||
vecImpulse += Vector(0,0,1.5); //vecUp * 0.5f;
|
||||
vecImpulse += vecForward * 1.0f;
|
||||
flImpulseScale = 250.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vecRight, vecUp;
|
||||
AngleVectors( EyeAngles(), NULL, &vecRight, &vecUp );
|
||||
vecImpulse += vecUp * random->RandomFloat( -0.25, 0.25 );
|
||||
vecImpulse += vecRight * random->RandomFloat( -0.25, 0.25 );
|
||||
flImpulseScale = random->RandomFloat( 100.f, 150.f );
|
||||
}
|
||||
VectorNormalize( vecImpulse );
|
||||
vecImpulse *= flImpulseScale;
|
||||
vecImpulse += GetAbsVelocity();
|
||||
|
||||
if ( VPhysicsGetObject() )
|
||||
{
|
||||
// We can probably remove this when the mass on the weapons is correct!
|
||||
VPhysicsGetObject()->SetMass( 25.0f );
|
||||
AngularImpulse angImpulse( 0, random->RandomFloat( 0, 100 ), 0 );
|
||||
VPhysicsGetObject()->SetVelocityInstantaneous( &vecImpulse, &angImpulse );
|
||||
}
|
||||
|
||||
m_nSkin = pWeapon->GetSkin();
|
||||
|
||||
m_nClip = pWeapon->IsEnergyWeapon() ? pWeapon->GetMaxClip1() : pWeapon->Clip1();
|
||||
m_nAmmo = pPlayer->GetAmmoCount( pWeapon->GetPrimaryAmmoType() );
|
||||
m_flEnergy = pWeapon->Energy_GetEnergy();
|
||||
m_flNextPrimaryAttack = pWeapon->m_flNextPrimaryAttack;
|
||||
m_flNextSecondaryAttack = pWeapon->m_flNextSecondaryAttack;
|
||||
|
||||
if ( bIsSuicide )
|
||||
{
|
||||
m_flChargeLevel = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pWeapon );
|
||||
if ( pMedigun )
|
||||
{
|
||||
m_flChargeLevel.Set( pMedigun->GetChargeLevel() );
|
||||
if ( m_flChargeLevel > 0.f )
|
||||
{
|
||||
SetContextThink( &CTFDroppedWeapon::ChargeLevelDegradeThink, gpGlobals->curtime + 0.1f, "ChargeLevelDegradeThink" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CTFStickBomb *pStickBomb = dynamic_cast< CTFStickBomb* >( pWeapon );
|
||||
if ( pStickBomb )
|
||||
{
|
||||
m_nDetonated = pStickBomb->GetDetonated();
|
||||
}
|
||||
|
||||
// Capture bar regen (Jarate, base ball)
|
||||
m_flEffectBarRegenTime = pWeapon->m_flEffectBarRegenTime;
|
||||
|
||||
//DevMsg( "Dropped weapon with: clip[%d] ammo[%d] energy[%f]\n", m_nClip, m_nAmmo, m_flEnergy );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::InitPickedUpWeapon( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon )
|
||||
{
|
||||
// clear the context think
|
||||
SetContextThink( NULL, 0, "ChargeLevelDegradeThink" );
|
||||
|
||||
// preserve the ammo
|
||||
int nCurrentMetal = pPlayer->GetAmmoCount( TF_AMMO_METAL );
|
||||
pWeapon->m_iClip1 = m_nClip;
|
||||
if ( pWeapon->GetPrimaryAmmoType() != -1 )
|
||||
{
|
||||
pPlayer->SetAmmoCount( m_nAmmo, pWeapon->GetPrimaryAmmoType() );
|
||||
}
|
||||
// SetAmmoCount can override metal for some weapon
|
||||
// Make sure engineer don't gain metal by picking up weapon
|
||||
pPlayer->SetAmmoCount( nCurrentMetal, TF_AMMO_METAL );
|
||||
pWeapon->Energy_SetEnergy( m_flEnergy );
|
||||
|
||||
CWeaponMedigun *pMedigun = dynamic_cast< CWeaponMedigun* >( pWeapon );
|
||||
if ( pMedigun )
|
||||
{
|
||||
pMedigun->SetChargeLevel( m_flChargeLevel );
|
||||
}
|
||||
|
||||
CTFStickBomb *pStickBomb = dynamic_cast< CTFStickBomb* >( pWeapon );
|
||||
if ( pStickBomb )
|
||||
{
|
||||
pStickBomb->SetDetonated( m_nDetonated );
|
||||
}
|
||||
|
||||
// stomp the team color
|
||||
if ( pWeapon->GetAttributeContainer() && pWeapon->GetAttributeContainer()->GetItem() )
|
||||
{
|
||||
pWeapon->GetAttributeContainer()->GetItem()->SetTeamNumber( GetItem()->GetTeamNumber() );
|
||||
}
|
||||
|
||||
pWeapon->m_flEffectBarRegenTime = m_flEffectBarRegenTime;
|
||||
pWeapon->m_flNextPrimaryAttack = m_flNextPrimaryAttack;
|
||||
pWeapon->m_flNextSecondaryAttack = m_flNextSecondaryAttack;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::ChargeLevelDegradeThink()
|
||||
{
|
||||
m_flChargeLevel.Set( m_flChargeLevel - 0.01f );
|
||||
|
||||
if ( m_flChargeLevel < 0.f )
|
||||
{
|
||||
m_flChargeLevel.Set( 0.f );
|
||||
SetContextThink( NULL, 0, "ChargeLevelDegradeThink" );
|
||||
return;
|
||||
}
|
||||
|
||||
SetContextThink( &CTFDroppedWeapon::ChargeLevelDegradeThink, gpGlobals->curtime + 0.1f, "ChargeLevelDegradeThink" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFDroppedWeapon::SetItem( const CEconItemView *pItem )
|
||||
{
|
||||
if ( pItem )
|
||||
m_Item.CopyFrom( *pItem );
|
||||
}
|
||||
#endif // GAME_DLL
|
||||
@@ -0,0 +1,102 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TF_DROPPED_WEAPON_H
|
||||
#define TF_DROPPED_WEAPON_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFDroppedWeapon C_TFDroppedWeapon
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
class CTFPlayer;
|
||||
#endif // GAME_DLL
|
||||
|
||||
DECLARE_AUTO_LIST( IDroppedWeaponAutoList );
|
||||
|
||||
class CTFDroppedWeapon : public CBaseAnimating, public IDroppedWeaponAutoList
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFDroppedWeapon, CBaseAnimating );
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CTFDroppedWeapon();
|
||||
~CTFDroppedWeapon();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual void OnPreDataChanged( DataUpdateType_t updateType ) OVERRIDE;
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType ) OVERRIDE;
|
||||
virtual void ClientThink() OVERRIDE;
|
||||
|
||||
// target id
|
||||
virtual bool IsVisibleToTargetID( void ) const OVERRIDE;
|
||||
|
||||
// Draw Attachment models
|
||||
virtual bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo );
|
||||
|
||||
virtual IMaterial *GetEconWeaponMaterialOverride( int iTeam ) OVERRIDE;
|
||||
virtual void ModifyEmitSoundParams( EmitSound_t ¶ms ) OVERRIDE;
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
static CTFDroppedWeapon *Create( CTFPlayer *pLastOwner, const Vector &vecOrigin, const QAngle &vecAngles, const char *pszModelName, const CEconItemView *pItem );
|
||||
void InitDroppedWeapon( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon, bool bSwap, bool bIsSuicide = false );
|
||||
void InitPickedUpWeapon( CTFPlayer *pPlayer, CTFWeaponBase *pWeapon );
|
||||
|
||||
void ChargeLevelDegradeThink();
|
||||
#endif // GAME_DLL
|
||||
|
||||
CEconItemView *GetItem() { return &m_Item; }
|
||||
const CEconItemView *GetItem() const { return &m_Item; }
|
||||
|
||||
float GetChargeLevel( void ){ return m_flChargeLevel; }
|
||||
|
||||
private:
|
||||
|
||||
CNetworkVarEmbedded( CEconItemView, m_Item );
|
||||
CNetworkVar( float, m_flChargeLevel );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
CHandle< CTFPlayer > m_hPlayer;
|
||||
|
||||
// preserve weapon ammo in the clip
|
||||
void SetItem( const CEconItemView *pItem );
|
||||
|
||||
// preserve ammo count
|
||||
int m_nClip;
|
||||
int m_nAmmo;
|
||||
int m_nDetonated;
|
||||
float m_flEnergy;
|
||||
float m_flEffectBarRegenTime;
|
||||
float m_flNextPrimaryAttack;
|
||||
float m_flNextSecondaryAttack;
|
||||
#endif // GAME_DLL
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void SetupParticleEffect();
|
||||
HPARTICLEFFECT m_effect;
|
||||
|
||||
CHandle< C_BaseAnimating > m_worldmodelStatTrakAddon;
|
||||
|
||||
void UpdateGlowEffect( void );
|
||||
void DestroyGlowEffect( void );
|
||||
CGlowObject *m_pGlowEffect;
|
||||
bool m_bShouldGlowForLocalPlayer;
|
||||
|
||||
CUtlVector<AttachedModelData_t> m_vecAttachedModels;
|
||||
|
||||
float m_flOldChargeLevel;
|
||||
#endif // CLIENT_DLL
|
||||
};
|
||||
|
||||
#endif // TF_DROPPED_WEAPON_H
|
||||
@@ -0,0 +1,343 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "tf_duckleaderboard.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "vgui_avatarimage.h"
|
||||
#include "tf_item_inventory.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include <tier0/memdbgon.h>
|
||||
|
||||
//-------------------------------
|
||||
const char *g_szDuckLeaderboardNames[] =
|
||||
{
|
||||
"TF_DUCK_SCORING_OVERALL_RATING", // TF_DUCK_SCORING_OVERALL_RATING
|
||||
"TF_DUCK_SCORING_PERSONAL_GENERATION", // TF_DUCK_SCORING_PERSONAL_GENERATION
|
||||
"TF_DUCK_SCORING_PERSONAL_PICKUP_OFFENSE", // TF_DUCK_SCORING_PERSONAL_PICKUP_OFFENSE
|
||||
"TF_DUCK_SCORING_PERSONAL_PICKUP_DEFENDED", // TF_DUCK_SCORING_PERSONAL_PICKUP_DEFENDED
|
||||
"TF_DUCK_SCORING_PERSONAL_PICKUP_OBJECTIVE", // TF_DUCK_SCORING_PERSONAL_PICKUP_OBJECTIVE
|
||||
"TF_DUCK_SCORING_TEAM_PICKUP_MY_DUCKS", // TF_DUCK_SCORING_TEAM_PICKUP_MY_DUCKS
|
||||
"TF_DUCK_SCORING_PERSONAL_BONUS_PICKUP", // TF_DUCK_SCORING_PERSONAL_BONUS_PICKUP
|
||||
};
|
||||
COMPILE_TIME_ASSERT( ARRAYSIZE( g_szDuckLeaderboardNames ) == DUCK_NUM_LEADERBOARDS );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CDucksLeaderboard::CDucksLeaderboard( Panel *parent, const char *panelName, const char *pszDuckLeaderboardname )
|
||||
: CTFLeaderboardPanel( parent, panelName )
|
||||
, m_pszDuckLeaderboardName( pszDuckLeaderboardname )
|
||||
, m_pToolTip( NULL )
|
||||
{
|
||||
m_pToolTip = new CTFTextToolTip( this );
|
||||
m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" );
|
||||
m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false );
|
||||
m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false );
|
||||
m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel );
|
||||
m_pToolTip->SetTooltipDelay( 0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CDucksLeaderboard::~CDucksLeaderboard()
|
||||
{}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboard::ApplySchemeSettings( IScheme *pScheme )
|
||||
{
|
||||
BaseClass::ApplySchemeSettings( pScheme );
|
||||
|
||||
LoadControlSettings( "Resource/UI/econ/DucksLeaderboardPanel.res" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CDucksLeaderboard::GetLeaderboardData( CUtlVector< LeaderboardEntry_t* > &scores )
|
||||
{
|
||||
return Leaderboards_GetDuckLeaderboard( scores, m_pszDuckLeaderboardName );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CDucksLeaderboard::UpdateLeaderboards()
|
||||
{
|
||||
CUtlVector< LeaderboardEntry_t* > scores;
|
||||
if ( !GetLeaderboardData( scores ) )
|
||||
return false;
|
||||
|
||||
CSteamID localSteamID;
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
localSteamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
// Scores were empty but the leaderboard query was OK? This will happen while the user
|
||||
// and their friends has no scores. For now, insert a dummy value for the local player.
|
||||
LeaderboardEntry_t dummyentry;
|
||||
if ( scores.IsEmpty() )
|
||||
{
|
||||
dummyentry.m_nScore = 0;
|
||||
dummyentry.m_steamIDUser = localSteamID;
|
||||
scores.AddToTail( &dummyentry );
|
||||
}
|
||||
|
||||
int nStartingIndex = 0;
|
||||
FOR_EACH_VEC( scores, i )
|
||||
{
|
||||
if ( scores[ i ]->m_steamIDUser == localSteamID )
|
||||
{
|
||||
// Try to go 3 past where the player is if we can, then go back 6
|
||||
nStartingIndex = Max( Min( i + 3, scores.Count() ) - 6, 0 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int x=0,y=0;
|
||||
FOR_EACH_VEC( m_vecLeaderboardEntries, i )
|
||||
{
|
||||
Color colorToUse = i % 2 == 1 ? m_OddTextColor : m_EvenTextColor;
|
||||
EditablePanel *pContainer = dynamic_cast< EditablePanel* >( m_vecLeaderboardEntries[i] );
|
||||
int nScoreIndex = nStartingIndex + i;
|
||||
if ( pContainer )
|
||||
{
|
||||
bool bIsEntryVisible = nScoreIndex < scores.Count();
|
||||
pContainer->SetVisible( bIsEntryVisible );
|
||||
pContainer->SetPos( x, y );
|
||||
y += m_yEntryStep;
|
||||
if ( bIsEntryVisible )
|
||||
{
|
||||
const LeaderboardEntry_t *leaderboardEntry = scores[nScoreIndex];
|
||||
const CSteamID &steamID = leaderboardEntry->m_steamIDUser;
|
||||
bool bIsLocalPlayer = steamapicontext && steamapicontext->SteamUser() && steamapicontext->SteamUser()->GetSteamID() == steamID;
|
||||
pContainer->SetDialogVariable( "username", InventoryManager()->PersonaName_Get( steamID.GetAccountID() ) );
|
||||
float flXPToLevel = DUCK_XP_SCALE;
|
||||
const float flPreciseLevel = leaderboardEntry->m_nScore / flXPToLevel;
|
||||
const int nCurrentLevel = floor( flPreciseLevel );
|
||||
const float flPercentToNextLevel = flPreciseLevel - nCurrentLevel;
|
||||
pContainer->SetDialogVariable( "score", nCurrentLevel );
|
||||
ProgressBar* pProgressBar = pContainer->FindControl<ProgressBar>( "ProgressToNextLevel", true );
|
||||
if ( pProgressBar )
|
||||
{
|
||||
pProgressBar->SetProgress( 1.f - flPercentToNextLevel );
|
||||
pProgressBar->SetProgressDirection( ProgressBar::PROGRESS_WEST );
|
||||
//const int nNextLevelXP = ( nCurrentLevel + 1 ) * DUCK_XP_SCALE;
|
||||
pProgressBar->SetTooltip( m_pToolTip, CFmtStr( "%d / %d", leaderboardEntry->m_nScore % DUCK_XP_SCALE, DUCK_XP_SCALE ) );
|
||||
}
|
||||
|
||||
CExLabel *pText = pContainer->FindControl< CExLabel >( "UserName" );
|
||||
if ( pText )
|
||||
{
|
||||
pText->SetColorStr( bIsLocalPlayer ? m_LocalPlayerTextColor : colorToUse );
|
||||
}
|
||||
|
||||
pText = pContainer->FindControl< CExLabel >( "Score" );
|
||||
if ( pText )
|
||||
{
|
||||
pText->SetColorStr( bIsLocalPlayer ? m_LocalPlayerTextColor : colorToUse );
|
||||
}
|
||||
|
||||
CAvatarImagePanel *pAvatar = dynamic_cast< CAvatarImagePanel* >( pContainer->FindChildByName( "AvatarImage" ) );
|
||||
if ( pAvatar )
|
||||
{
|
||||
pAvatar->SetShouldDrawFriendIcon( false );
|
||||
pAvatar->SetPlayer( steamID, k_EAvatarSize32x32 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CDucksLeaderboardManager::CDucksLeaderboardManager( Panel *parent, const char *panelName )
|
||||
: EditablePanel( parent, panelName )
|
||||
, m_nCurrentPage( 0 )
|
||||
, m_flFadeStartTime( Plat_FloatTime() )
|
||||
, m_pDimmer( NULL )
|
||||
{
|
||||
ListenForGameEvent( "gameui_hidden" );
|
||||
|
||||
m_pToolTip = new CTFTextToolTip( this );
|
||||
m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" );
|
||||
m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false );
|
||||
m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false );
|
||||
m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel );
|
||||
m_pToolTip->SetTooltipDelay( 0 );
|
||||
|
||||
static CSchemaItemDefHandle pDuckBadgeDef( "Duck Badge" );
|
||||
// Prevent users who don't own the badge from opening the duck leaderboards
|
||||
if( CTFPlayerInventory::GetFirstItemOfItemDef( pDuckBadgeDef->GetDefinitionIndex() ) == NULL )
|
||||
{
|
||||
SetVisible( false );
|
||||
MarkForDeletion();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::ApplySchemeSettings( IScheme *pScheme )
|
||||
{
|
||||
BaseClass::ApplySchemeSettings( pScheme );
|
||||
|
||||
LoadControlSettings( "Resource/UI/econ/DucksLeaderboards.res" );
|
||||
|
||||
m_pDimmer = FindControl<EditablePanel>( "Dimmer" );
|
||||
|
||||
ShowPage( 0 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::ApplySettings( KeyValues *inResourceData )
|
||||
{
|
||||
BaseClass::ApplySettings( inResourceData );
|
||||
|
||||
m_vecLeaderboards.PurgeAndDeleteElements();
|
||||
|
||||
EditablePanel* pBackgroundPanel = FindControl< EditablePanel >( "Background", true );
|
||||
if ( pBackgroundPanel )
|
||||
{
|
||||
EDuckLeaderboardTypes eShowLeaderboard = TF_DUCK_SCORING_OVERALL_RATING;
|
||||
CDucksLeaderboard *pLeaderboard = new CDucksLeaderboard( pBackgroundPanel, "DuckLeaderboard", g_szDuckLeaderboardNames[eShowLeaderboard] );
|
||||
pLeaderboard->SetDialogVariable( "title", g_pVGuiLocalize->Find( CFmtStr( "#%s", g_szDuckLeaderboardNames[eShowLeaderboard] ) ) );
|
||||
pLeaderboard->SetDialogVariable( "description", g_pVGuiLocalize->Find( CFmtStr( "#%s_Desc", g_szDuckLeaderboardNames[eShowLeaderboard] ) ) );
|
||||
pLeaderboard->InvalidateLayout( true, true );
|
||||
m_vecLeaderboards.AddToTail( pLeaderboard );
|
||||
|
||||
EditablePanel *pStatsPanel = pBackgroundPanel->FindControl<EditablePanel>( "SecondaryStatsContainer", true );
|
||||
if ( pStatsPanel )
|
||||
{
|
||||
m_vecLeaderboards.AddToTail( pStatsPanel );
|
||||
|
||||
Panel* pScoresContainer = pStatsPanel->FindChildByName( "ScoresContainer", true );
|
||||
|
||||
if ( pScoresContainer )
|
||||
{
|
||||
CSteamID localSteamID;
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
localSteamID= steamapicontext->SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
KeyValues* pScoreEntryKVs = inResourceData->FindKey( "ScoreEntryKVs" );
|
||||
if ( pScoreEntryKVs )
|
||||
{
|
||||
for( int i = 1; i < DUCK_NUM_LEADERBOARDS; ++i )
|
||||
{
|
||||
EditablePanel *pNewEntry = new EditablePanel( pScoresContainer , "Score%d" );
|
||||
|
||||
pNewEntry->ApplySettings( pScoreEntryKVs );
|
||||
pNewEntry->SetDialogVariable( "name", g_pVGuiLocalize->Find( CFmtStr( "#%s", g_szDuckLeaderboardNames[i] ) ) );
|
||||
pNewEntry->SetTooltip( m_pToolTip, CFmtStr( "#%s_Desc", g_szDuckLeaderboardNames[i] ) );
|
||||
pNewEntry->SetVisible( true );
|
||||
pNewEntry->SetPos( 0, m_iScoreStep * i ); // This is off by 1, but that's what we want. It starts at 1, but we want it to start lower
|
||||
// so it matches the leaderboard entries
|
||||
|
||||
CUtlVector< LeaderboardEntry_t* > scores;
|
||||
Leaderboards_GetDuckLeaderboard( scores, g_szDuckLeaderboardNames[i] );
|
||||
|
||||
int nScore = 0;
|
||||
FOR_EACH_VEC( scores, j )
|
||||
{
|
||||
if ( scores[j]->m_steamIDUser == localSteamID )
|
||||
{
|
||||
nScore = scores[j]->m_nScore;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pNewEntry->SetDialogVariable( "score", nScore );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::OnCommand( const char *command )
|
||||
{
|
||||
if ( FStrEq( command, "close" ) )
|
||||
{
|
||||
SetVisible( false );
|
||||
MarkForDeletion();
|
||||
return;
|
||||
}
|
||||
else if ( FStrEq( command, "nextpage" ) )
|
||||
{
|
||||
NextPage();
|
||||
}
|
||||
else if ( FStrEq( command, "prevpage" ) )
|
||||
{
|
||||
PrevPage();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::FireGameEvent( IGameEvent *event )
|
||||
{
|
||||
if ( FStrEq( event->GetName(), "gameui_hidden" ) )
|
||||
{
|
||||
SetVisible( false );
|
||||
MarkForDeletion();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDucksLeaderboardManager::OnThink()
|
||||
{
|
||||
if ( m_pDimmer )
|
||||
{
|
||||
float flDelta = Plat_FloatTime() - m_flFadeStartTime;
|
||||
float flAlpha = RemapValClamped( flDelta, 0.f, 0.2f, 0.f, 253.f );
|
||||
m_pDimmer->SetAlpha( flAlpha );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::NextPage()
|
||||
{
|
||||
++m_nCurrentPage;
|
||||
if ( m_nCurrentPage == m_vecLeaderboards.Count() )
|
||||
{
|
||||
m_nCurrentPage = 0;
|
||||
}
|
||||
|
||||
ShowPage( m_nCurrentPage );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::PrevPage()
|
||||
{
|
||||
--m_nCurrentPage;
|
||||
if ( m_nCurrentPage < 0 )
|
||||
{
|
||||
m_nCurrentPage = m_vecLeaderboards.Count() - 1;
|
||||
}
|
||||
|
||||
ShowPage( m_nCurrentPage );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CDucksLeaderboardManager::ShowPage( int nPage )
|
||||
{
|
||||
for( int i=0; i < m_vecLeaderboards.Count(); ++i )
|
||||
{
|
||||
m_vecLeaderboards[i]->SetVisible( i == nPage );
|
||||
}
|
||||
|
||||
EditablePanel* pBackgroundPanel = dynamic_cast< EditablePanel* >( FindChildByName( "Background", true ) );
|
||||
if ( pBackgroundPanel )
|
||||
{
|
||||
pBackgroundPanel->SetDialogVariable( "pagenumber", CFmtStr( "%d/%d", nPage + 1, m_vecLeaderboards.Count() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_DUCKLEADERBOARD_H
|
||||
#define TF_DUCKLEADERBOARD_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "tf_mapinfo.h"
|
||||
#include "tf_leaderboardpanel.h"
|
||||
#include "tf_controls.h"
|
||||
#endif
|
||||
|
||||
extern const char* g_szDuckLeaderboardNames[];
|
||||
|
||||
#define TF_DUCK_ID "DUCK_ID"
|
||||
#define DUCK_XP_SCALE 5000
|
||||
#define DUCK_XP_WEIGHT_GENERATION 3
|
||||
#define DUCK_XP_WEIGHT_OFFENSE 3
|
||||
#define DUCK_XP_WEIGHT_DEFENSE 1
|
||||
#define DUCK_XP_WEIGHT_OBJECTIVE 3
|
||||
#define DUCK_XP_WEIGHT_TEAMMATE 3
|
||||
#define DUCK_XP_WEIGHT_BONUS 50
|
||||
|
||||
|
||||
enum EDuckLeaderboardTypes
|
||||
{
|
||||
TF_DUCK_SCORING_OVERALL_RATING = 0,
|
||||
TF_DUCK_SCORING_PERSONAL_GENERATION,
|
||||
TF_DUCK_SCORING_PERSONAL_PICKUP_OFFENSE,
|
||||
TF_DUCK_SCORING_PERSONAL_PICKUP_DEFENDED,
|
||||
TF_DUCK_SCORING_PERSONAL_PICKUP_OBJECTIVE,
|
||||
TF_DUCK_SCORING_TEAM_PICKUP_MY_DUCKS,
|
||||
TF_DUCK_SCORING_PERSONAL_BONUS_PICKUP,
|
||||
DUCK_NUM_LEADERBOARDS
|
||||
};
|
||||
|
||||
enum EDuckEventTypes
|
||||
{
|
||||
DUCK_CREATED = 1,
|
||||
DUCK_COLLECTED,
|
||||
};
|
||||
|
||||
enum EDuckFlags
|
||||
{
|
||||
DUCK_FLAG_OBJECTIVE = 1 << 0,
|
||||
DUCK_FLAG_BONUS = 1 << 1
|
||||
};
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
class CDucksLeaderboard : public CTFLeaderboardPanel
|
||||
{
|
||||
DECLARE_CLASS_SIMPLE( CDucksLeaderboard, CTFLeaderboardPanel );
|
||||
public:
|
||||
CDucksLeaderboard( Panel *parent, const char *panelName, const char *pszDuckLeaderboardname );
|
||||
virtual ~CDucksLeaderboard();
|
||||
|
||||
virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE;
|
||||
private:
|
||||
virtual bool UpdateLeaderboards() OVERRIDE;
|
||||
virtual bool GetLeaderboardData( CUtlVector< LeaderboardEntry_t* >& scores ) OVERRIDE;
|
||||
|
||||
const char *m_pszDuckLeaderboardName;
|
||||
CTFTextToolTip *m_pToolTip;
|
||||
vgui::EditablePanel *m_pToolTipEmbeddedPanel;
|
||||
};
|
||||
|
||||
|
||||
class CDucksLeaderboardManager : public EditablePanel, CGameEventListener
|
||||
{
|
||||
DECLARE_CLASS_SIMPLE( CDucksLeaderboardManager, EditablePanel );
|
||||
public:
|
||||
CDucksLeaderboardManager( Panel *parent, const char *panelName );
|
||||
|
||||
virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE;
|
||||
virtual void ApplySettings( KeyValues *inResourceData ) OVERRIDE;
|
||||
virtual void OnCommand( const char *command ) OVERRIDE;
|
||||
virtual void FireGameEvent( IGameEvent *event ) OVERRIDE;
|
||||
|
||||
virtual void OnThink();
|
||||
private:
|
||||
|
||||
void NextPage();
|
||||
void PrevPage();
|
||||
void ShowPage( int nPage );
|
||||
|
||||
int m_nCurrentPage;
|
||||
CUtlVector< EditablePanel* > m_vecLeaderboards;
|
||||
|
||||
CTFTextToolTip *m_pToolTip;
|
||||
vgui::EditablePanel *m_pToolTipEmbeddedPanel;
|
||||
vgui::EditablePanel *m_pDimmer;
|
||||
float m_flFadeStartTime;
|
||||
|
||||
CPanelAnimationVarAliasType( int, m_iScoreStep, "score_step", "0", "proportional_int" );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // TF_DUCKLEADERBOARD_H
|
||||
@@ -0,0 +1,90 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_duel_summary.h"
|
||||
#include "gcsdk/enumutils.h"
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
#ifdef GC
|
||||
IMPLEMENT_CLASS_MEMPOOL( CTFDuelSummary, 1000, UTLMEMORYPOOL_GROW_SLOW );
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
bool CTFDuelSummary::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchDuelSummary schDuelSummary;
|
||||
WriteToRecord( &schDuelSummary );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schDuelSummary );
|
||||
}
|
||||
|
||||
bool CTFDuelSummary::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
|
||||
{
|
||||
CSchDuelSummary schDuelSummary;
|
||||
WriteToRecord( &schDuelSummary );
|
||||
CColumnSet csDatabaseDirty( schDuelSummary.GetPSchema()->GetRecordInfo() );
|
||||
csDatabaseDirty.MakeEmpty();
|
||||
FOR_EACH_VEC( fields, nField )
|
||||
{
|
||||
switch ( fields[nField] )
|
||||
{
|
||||
case CSOTFDuelSummary::kLastDuelAccountIdFieldNumber: csDatabaseDirty.BAddColumn( CSchDuelSummary::k_iField_unLastDuelAccountID ); break;
|
||||
case CSOTFDuelSummary::kLastDuelTimestampFieldNumber: csDatabaseDirty.BAddColumn( CSchDuelSummary::k_iField_rtLastDuelTimestamp ); break;
|
||||
case CSOTFDuelSummary::kLastDuelStatusFieldNumber: csDatabaseDirty.BAddColumn( CSchDuelSummary::k_iField_eLastDuelStatus ); break;
|
||||
case CSOTFDuelSummary::kDuelLossesFieldNumber: csDatabaseDirty.BAddColumn( CSchDuelSummary::k_iField_unDuelLosses ); break;
|
||||
case CSOTFDuelSummary::kDuelWinsFieldNumber: csDatabaseDirty.BAddColumn( CSchDuelSummary::k_iField_unDuelWins ); break;
|
||||
}
|
||||
}
|
||||
return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schDuelSummary, csDatabaseDirty );
|
||||
}
|
||||
|
||||
bool CTFDuelSummary::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchDuelSummary schDuelSummary;
|
||||
WriteToRecord( &schDuelSummary );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schDuelSummary );
|
||||
}
|
||||
|
||||
void CTFDuelSummary::WriteToRecord( CSchDuelSummary *pDuelSummary ) const
|
||||
{
|
||||
pDuelSummary->m_unAccountID = Obj().account_id();
|
||||
pDuelSummary->m_unDuelWins = Obj().duel_wins();
|
||||
pDuelSummary->m_unDuelLosses = Obj().duel_losses();
|
||||
pDuelSummary->m_unLastDuelAccountID = Obj().last_duel_account_id();
|
||||
pDuelSummary->m_rtLastDuelTimestamp = Obj().last_duel_timestamp();
|
||||
pDuelSummary->m_eLastDuelStatus = Obj().last_duel_status();
|
||||
}
|
||||
|
||||
|
||||
void CTFDuelSummary::ReadFromRecord( const CSchDuelSummary & duelSummary )
|
||||
{
|
||||
Obj().set_account_id( duelSummary.m_unAccountID );
|
||||
Obj().set_duel_wins( duelSummary.m_unDuelWins );
|
||||
Obj().set_duel_losses( duelSummary.m_unDuelLosses );
|
||||
Obj().set_last_duel_account_id( duelSummary.m_unLastDuelAccountID );
|
||||
Obj().set_last_duel_timestamp( duelSummary.m_rtLastDuelTimestamp );
|
||||
Obj().set_last_duel_status( duelSummary.m_eLastDuelStatus );
|
||||
|
||||
}
|
||||
|
||||
ENUMSTRINGS_START( eDuelEndReason )
|
||||
{ kDuelEndReason_DuelOver, "Complete" },
|
||||
{ kDuelEndReason_PlayerDisconnected, "Player Disconnected" },
|
||||
{ kDuelEndReason_PlayerSwappedTeams, "Player Swapped Teams" },
|
||||
{ kDuelEndReason_LevelShutdown, "Level Shutdown" },
|
||||
{ kDuelEndReason_ScoreTiedAtZero, "Tied" },
|
||||
{ kDuelEndReason_PlayerKicked, "Player Kicked" },
|
||||
{ kDuelEndReason_PlayerForceSwappedTeams, "Forced to Swap Teams" },
|
||||
{ kDuelEndReason_ScoreTied, "Tied" },
|
||||
ENUMSTRINGS_END( eDuelEndReason )
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Holds the CTFDualSummary
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TFDUELSUMMARY_H
|
||||
#define TFDUELSUMMARY_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/protobufsharedobject.h"
|
||||
#include "tf_gcmessages.h"
|
||||
|
||||
|
||||
// do not re-order, stored in DB
|
||||
enum eDuelStatus
|
||||
{
|
||||
kDuelStatus_Loss,
|
||||
kDuelStatus_Tie,
|
||||
kDuelStatus_Win,
|
||||
};
|
||||
|
||||
// do not re-order, stored in DB
|
||||
enum eDuelEndReason
|
||||
{
|
||||
kDuelEndReason_DuelOver,
|
||||
kDuelEndReason_PlayerDisconnected,
|
||||
kDuelEndReason_PlayerSwappedTeams,
|
||||
kDuelEndReason_LevelShutdown,
|
||||
kDuelEndReason_ScoreTiedAtZero,
|
||||
kDuelEndReason_PlayerKicked,
|
||||
kDuelEndReason_PlayerForceSwappedTeams,
|
||||
kDuelEndReason_ScoreTied,
|
||||
kDuelEndReason_Cancelled
|
||||
};
|
||||
|
||||
const char *PchNameFromeDuelEndReason( eDuelEndReason eReason );
|
||||
|
||||
const uint32 kWinsPerLevel = 10;
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//---------------------------------------------------------------------------------
|
||||
class CTFDuelSummary : public GCSDK::CProtoBufSharedObject< CSOTFDuelSummary, k_EEconTypeDuelSummary >
|
||||
{
|
||||
#ifdef GC
|
||||
DECLARE_CLASS_MEMPOOL( CTFDuelSummary );
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef GC
|
||||
virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
|
||||
virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
|
||||
virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess );
|
||||
|
||||
void WriteToRecord( CSchDuelSummary *pDuelSummary ) const;
|
||||
void ReadFromRecord( const CSchDuelSummary & duelSummary );
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //TFDUELSUMMARY_H
|
||||
@@ -0,0 +1,368 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "tf_fx_shared.h"
|
||||
#include "tf_weaponbase.h"
|
||||
#include "takedamageinfo.h"
|
||||
#include "tf_gamerules.h"
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
#include "fx_impact.h"
|
||||
// Server specific.
|
||||
#else
|
||||
#include "tf_fx.h"
|
||||
#include "ilagcompensationmanager.h"
|
||||
#include "tf_passtime_logic.h"
|
||||
#endif
|
||||
|
||||
ConVar tf_use_fixed_weaponspreads( "tf_use_fixed_weaponspreads", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "If set to 1, weapons that fire multiple pellets per shot will use a non-random pellet distribution." );
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
class CGroupedSound
|
||||
{
|
||||
public:
|
||||
string_t m_SoundName;
|
||||
Vector m_vecPos;
|
||||
};
|
||||
|
||||
CUtlVector<CGroupedSound> g_aGroupedSounds;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Called by the ImpactSound function.
|
||||
//-----------------------------------------------------------------------------
|
||||
void ImpactSoundGroup( const char *pSoundName, const Vector &vecEndPos )
|
||||
{
|
||||
int iSound = 0;
|
||||
|
||||
// Don't play the sound if it's too close to another impact sound.
|
||||
for ( iSound = 0; iSound < g_aGroupedSounds.Count(); ++iSound )
|
||||
{
|
||||
CGroupedSound *pSound = &g_aGroupedSounds[iSound];
|
||||
if ( pSound )
|
||||
{
|
||||
if ( vecEndPos.DistToSqr( pSound->m_vecPos ) < ( 300.0f * 300.0f ) )
|
||||
{
|
||||
if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 )
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, play the sound and add it to the list.
|
||||
CLocalPlayerFilter filter;
|
||||
C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vecEndPos );
|
||||
|
||||
iSound = g_aGroupedSounds.AddToTail();
|
||||
g_aGroupedSounds[iSound].m_SoundName = pSoundName;
|
||||
g_aGroupedSounds[iSound].m_vecPos = vecEndPos;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This is a cheap ripoff from CBaseCombatWeapon::WeaponSound().
|
||||
//-----------------------------------------------------------------------------
|
||||
void FX_WeaponSound( int iPlayer, WeaponSound_t soundType, const Vector &vecOrigin, CTFWeaponInfo *pWeaponInfo )
|
||||
{
|
||||
// If we have some sounds from the weapon classname.txt file, play a random one of them
|
||||
const char *pShootSound = pWeaponInfo->aShootSounds[soundType];
|
||||
if ( !pShootSound || !pShootSound[0] )
|
||||
return;
|
||||
|
||||
CBroadcastRecipientFilter filter;
|
||||
if ( !te->CanPredict() )
|
||||
return;
|
||||
|
||||
CBaseEntity::EmitSound( filter, iPlayer, pShootSound, &vecOrigin );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void StartGroupingSounds()
|
||||
{
|
||||
Assert( g_aGroupedSounds.Count() == 0 );
|
||||
SetImpactSoundRoute( ImpactSoundGroup );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void EndGroupingSounds()
|
||||
{
|
||||
g_aGroupedSounds.Purge();
|
||||
SetImpactSoundRoute( NULL );
|
||||
}
|
||||
|
||||
// Server specific.
|
||||
#else
|
||||
|
||||
// Server doesn't play sounds.
|
||||
void FX_WeaponSound ( int iPlayer, WeaponSound_t soundType, const Vector &vecOrigin, CTFWeaponInfo *pWeaponInfo ) {}
|
||||
void StartGroupingSounds() {}
|
||||
void EndGroupingSounds() {}
|
||||
|
||||
#endif
|
||||
|
||||
Vector g_vecFixedWpnSpreadPellets[] =
|
||||
{
|
||||
Vector( 0,0,0 ), // First pellet goes down the middle
|
||||
Vector( 1,0,0 ),
|
||||
Vector( -1,0,0 ),
|
||||
Vector( 0,-1,0 ),
|
||||
Vector( 0,1,0 ),
|
||||
Vector( 0.85,-0.85,0 ),
|
||||
Vector( 0.85,0.85,0 ),
|
||||
Vector( -0.85,-0.85,0 ),
|
||||
Vector( -0.85,0.85,0 ),
|
||||
Vector( 0,0,0 ), // last pellet goes down the middle as well to reward fine aim
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This runs on both the client and the server. On the server, it
|
||||
// only does the damage calculations. On the client, it does all the effects.
|
||||
//-----------------------------------------------------------------------------
|
||||
void FX_FireBullets( CTFWeaponBase *pWpn, int iPlayer, const Vector &vecOrigin, const QAngle &vecAngles,
|
||||
int iWeapon, int iMode, int iSeed, float flSpread, float flDamage /* = -1.0f */, bool bCritical /* = false*/ )
|
||||
{
|
||||
// Get the weapon information.
|
||||
const char *pszWeaponAlias = WeaponIdToAlias( iWeapon );
|
||||
if ( !pszWeaponAlias )
|
||||
{
|
||||
DevMsg( 1, "FX_FireBullets: weapon alias for ID %i not found\n", iWeapon );
|
||||
return;
|
||||
}
|
||||
|
||||
WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pszWeaponAlias );
|
||||
if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
|
||||
{
|
||||
DevMsg( 1, "FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", pszWeaponAlias );
|
||||
return;
|
||||
}
|
||||
|
||||
CTFWeaponInfo *pWeaponInfo = static_cast<CTFWeaponInfo*>( GetFileWeaponInfoFromHandle( hWpnInfo ) );
|
||||
if( !pWeaponInfo )
|
||||
return;
|
||||
|
||||
bool bDoEffects = false;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
C_TFPlayer *pPlayer = ToTFPlayer( ClientEntityList().GetBaseEntity( iPlayer ) );
|
||||
#else
|
||||
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( iPlayer ) );
|
||||
#endif
|
||||
if ( !pPlayer )
|
||||
return;
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
bDoEffects = true;
|
||||
|
||||
// The minigun has custom sound & animation code to deal with its windup/down.
|
||||
if ( !pPlayer->IsLocalPlayer()
|
||||
&& iWeapon != TF_WEAPON_MINIGUN )
|
||||
{
|
||||
// Fire the animation event.
|
||||
if ( pPlayer && !pPlayer->IsDormant() )
|
||||
{
|
||||
if ( iMode == TF_WEAPON_PRIMARY_MODE )
|
||||
{
|
||||
pPlayer->m_PlayerAnimState->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
||||
}
|
||||
else
|
||||
{
|
||||
pPlayer->m_PlayerAnimState->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_SECONDARY );
|
||||
}
|
||||
}
|
||||
|
||||
//FX_WeaponSound( pPlayer->entindex(), SINGLE, vecOrigin, pWeaponInfo );
|
||||
}
|
||||
|
||||
// Server specific.
|
||||
#else
|
||||
// If this is server code, send the effect over to client as temp entity and
|
||||
// dispatch one message for all the bullet impacts and sounds.
|
||||
TE_FireBullets( pPlayer->entindex(), vecOrigin, vecAngles, iWeapon, iMode, iSeed, flSpread, bCritical );
|
||||
|
||||
// Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation.
|
||||
pPlayer->NoteWeaponFired();
|
||||
|
||||
#endif
|
||||
|
||||
// Fire bullets, calculate impacts & effects.
|
||||
StartGroupingSounds();
|
||||
|
||||
#if !defined (CLIENT_DLL)
|
||||
// Move other players back to history positions based on local player's lag
|
||||
lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
|
||||
|
||||
// PASSTIME custom lag compensation for the ball; see also tf_weapon_flamethrower.cpp
|
||||
// it would be better if all entities could opt-in to this, or a way for lagcompensation to handle non-players automatically
|
||||
if ( g_pPasstimeLogic && g_pPasstimeLogic->GetBall() )
|
||||
{
|
||||
g_pPasstimeLogic->GetBall()->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get the shooting angles.
|
||||
Vector vecShootForward, vecShootRight, vecShootUp;
|
||||
AngleVectors( vecAngles, &vecShootForward, &vecShootRight, &vecShootUp );
|
||||
|
||||
// Initialize the static firing information.
|
||||
FireBulletsInfo_t fireInfo;
|
||||
fireInfo.m_vecSrc = vecOrigin;
|
||||
if ( flDamage < 0.0f )
|
||||
{
|
||||
fireInfo.m_flDamage = pWeaponInfo->GetWeaponData( iMode ).m_nDamage;
|
||||
}
|
||||
else
|
||||
{
|
||||
fireInfo.m_flDamage = flDamage;
|
||||
}
|
||||
fireInfo.m_flDistance = pWeaponInfo->GetWeaponData( iMode ).m_flRange;
|
||||
fireInfo.m_iShots = 1;
|
||||
fireInfo.m_vecSpread.Init( flSpread, flSpread, 0.0f );
|
||||
fireInfo.m_iAmmoType = pWeaponInfo->iAmmoType;
|
||||
|
||||
// Ammo override
|
||||
int iModUseMetalOverride = 0;
|
||||
CALL_ATTRIB_HOOK_INT_ON_OTHER( pWpn, iModUseMetalOverride, mod_use_metal_ammo_type );
|
||||
if ( iModUseMetalOverride )
|
||||
{
|
||||
fireInfo.m_iAmmoType = TF_AMMO_METAL;
|
||||
}
|
||||
|
||||
// Setup the bullet damage type & roll for crit.
|
||||
int nDamageType = DMG_GENERIC;
|
||||
int nCustomDamageType = TF_DMG_CUSTOM_NONE;
|
||||
CTFWeaponBase *pWeapon = pPlayer->GetActiveTFWeapon(); // FIXME: Should this be pWpn?
|
||||
if ( pWeapon )
|
||||
{
|
||||
nDamageType = pWeapon->GetDamageType();
|
||||
if ( pWeapon->IsCurrentAttackACrit() || bCritical )
|
||||
{
|
||||
nDamageType |= DMG_CRITICAL;
|
||||
}
|
||||
|
||||
nCustomDamageType = pWeapon->GetCustomDamageType();
|
||||
}
|
||||
|
||||
if ( iWeapon != TF_WEAPON_MINIGUN )
|
||||
{
|
||||
fireInfo.m_iTracerFreq = 2;
|
||||
}
|
||||
|
||||
// Reset multi-damage structures.
|
||||
ClearMultiDamage();
|
||||
|
||||
#if !defined (CLIENT_DLL)
|
||||
// If this weapon fires multiple projectiles per shot, and can penetrate multiple
|
||||
// targets, aggregate CTakeDamageInfo events and send them off as one event
|
||||
CDmgAccumulator *pDmgAccumulator = pWpn ? pWpn->GetDmgAccumulator() : NULL;
|
||||
if ( pDmgAccumulator )
|
||||
{
|
||||
pDmgAccumulator->Start();
|
||||
}
|
||||
#endif // !CLIENT
|
||||
|
||||
int nBulletsPerShot = pWeaponInfo->GetWeaponData( iMode ).m_nBulletsPerShot;
|
||||
bool bFixedSpread = ( nDamageType & DMG_BUCKSHOT ) && ( nBulletsPerShot > 1 ) && IsFixedWeaponSpreadEnabled();
|
||||
if ( pWeapon )
|
||||
{
|
||||
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, nBulletsPerShot, mult_bullets_per_shot );
|
||||
}
|
||||
for ( int iBullet = 0; iBullet < nBulletsPerShot; ++iBullet )
|
||||
{
|
||||
// Initialize random system with this seed.
|
||||
RandomSeed( iSeed );
|
||||
|
||||
// Get circular gaussian spread. Under some cases we fire a bullet right down the crosshair:
|
||||
// - The first bullet of a spread weapon (except for rapid fire spread weapons like the minigun)
|
||||
// - The first bullet of a non-spread weapon if it's been >1.25 second since firing
|
||||
bool bFirePerfect = false;
|
||||
if ( iBullet == 0 && pWpn )
|
||||
{
|
||||
float flTimeSinceLastShot = (gpGlobals->curtime - pWpn->m_flLastFireTime );
|
||||
if ( nBulletsPerShot > 1 && flTimeSinceLastShot > 0.25 )
|
||||
{
|
||||
bFirePerfect = true;
|
||||
}
|
||||
else if ( nBulletsPerShot == 1 && flTimeSinceLastShot > 1.25 )
|
||||
{
|
||||
bFirePerfect = true;
|
||||
}
|
||||
}
|
||||
|
||||
float x,y;
|
||||
if ( bFixedSpread )
|
||||
{
|
||||
int iSpread = iBullet;
|
||||
while ( iSpread >= ARRAYSIZE(g_vecFixedWpnSpreadPellets) )
|
||||
{
|
||||
iSpread -= ARRAYSIZE(g_vecFixedWpnSpreadPellets);
|
||||
}
|
||||
float flScalar = 0.5;
|
||||
x = g_vecFixedWpnSpreadPellets[iSpread].x * flScalar;
|
||||
y = g_vecFixedWpnSpreadPellets[iSpread].y * flScalar;
|
||||
}
|
||||
else if ( bFirePerfect )
|
||||
{
|
||||
x = y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = RandomFloat( -0.5, 0.5 ) + RandomFloat( -0.5, 0.5 );
|
||||
y = RandomFloat( -0.5, 0.5 ) + RandomFloat( -0.5, 0.5 );
|
||||
}
|
||||
|
||||
// Initialize the varialbe firing information.
|
||||
fireInfo.m_vecDirShooting = vecShootForward + ( x * flSpread * vecShootRight ) + ( y * flSpread * vecShootUp );
|
||||
fireInfo.m_vecDirShooting.NormalizeInPlace();
|
||||
fireInfo.m_bUseServerRandomSeed = pWpn && pWpn->UseServerRandomSeed();
|
||||
|
||||
// Fire a bullet.
|
||||
pPlayer->FireBullet( pWpn, fireInfo, bDoEffects, nDamageType, nCustomDamageType );
|
||||
|
||||
// Use new seed for next bullet.
|
||||
++iSeed;
|
||||
}
|
||||
|
||||
#if !defined (CLIENT_DLL)
|
||||
if ( pDmgAccumulator )
|
||||
{
|
||||
pDmgAccumulator->Process();
|
||||
}
|
||||
#endif // !CLIENT
|
||||
|
||||
// Apply damage if any.
|
||||
ApplyMultiDamage();
|
||||
|
||||
#if !defined (CLIENT_DLL)
|
||||
lagcompensation->FinishLagCompensation( pPlayer );
|
||||
|
||||
// PASSTIME custom lag compensation for the ball; see also tf_weapon_flamethrower.cpp
|
||||
// it would be better if all entities could opt-in to this, or a way for lagcompensation to handle non-players automatically
|
||||
if ( g_pPasstimeLogic && g_pPasstimeLogic->GetBall() )
|
||||
{
|
||||
g_pPasstimeLogic->GetBall()->FinishLagCompensation( pPlayer );
|
||||
}
|
||||
#endif
|
||||
|
||||
EndGroupingSounds();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Should we make this a per-weapon property?
|
||||
//-----------------------------------------------------------------------------
|
||||
bool IsFixedWeaponSpreadEnabled( void )
|
||||
{
|
||||
const IMatchGroupDescription *pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
|
||||
if ( pMatchDesc )
|
||||
return pMatchDesc->m_params.m_bFixedWeaponSpread;
|
||||
|
||||
return tf_use_fixed_weaponspreads.GetBool();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_FX_SHARED_H
|
||||
#define TF_FX_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// Client specific.
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_tf_player.h"
|
||||
// Server specific.
|
||||
#else
|
||||
#include "tf_player.h"
|
||||
#endif
|
||||
|
||||
void FX_WeaponSound ( int iPlayer, WeaponSound_t soundType, const Vector &vecOrigin, CTFWeaponInfo *pWeaponInfo );
|
||||
void StartGroupingSounds( void );
|
||||
void EndGroupingSounds( void );
|
||||
bool IsFixedWeaponSpreadEnabled( void );
|
||||
|
||||
// This runs on both the client and the server.
|
||||
// On the server, it only does the damage calculations.
|
||||
// On the client, it does all the effects.
|
||||
void FX_FireBullets( CTFWeaponBase *pWpn, int iPlayer, const Vector &vecOrigin, const QAngle &vecAngles,
|
||||
int iWeapon, int iMode, int iSeed, float flSpread, float flDamage = -1.0f, bool bCritical = false );
|
||||
|
||||
#endif // TF_FX_SHARED_H
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,637 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#ifdef GAME_DLL
|
||||
#include "gamestats.h"
|
||||
#else
|
||||
#include "tf_hud_statpanel.h"
|
||||
#endif
|
||||
#include "tf_gamestats_shared.h"
|
||||
|
||||
#ifndef NO_STEAM
|
||||
#include "steamworks_gamestats.h"
|
||||
#endif
|
||||
|
||||
int TF_Gamestats_RoundStats_t::m_iNumRounds = 0;
|
||||
time_t TF_Gamestats_RoundStats_t::m_iRoundStartTime = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const char *s_pStatStrings[ TFSTAT_TOTAL ] =
|
||||
{
|
||||
"TFSTAT_UNDEFINED",
|
||||
"TFSTAT_SHOTS_HIT",
|
||||
"TFSTAT_SHOTS_FIRED",
|
||||
"TFSTAT_KILLS",
|
||||
"TFSTAT_DEATHS",
|
||||
"TFSTAT_DAMAGE",
|
||||
"TFSTAT_CAPTURES",
|
||||
"TFSTAT_DEFENSES",
|
||||
"TFSTAT_DOMINATIONS",
|
||||
"TFSTAT_REVENGE",
|
||||
"TFSTAT_POINTSSCORED",
|
||||
"TFSTAT_BUILDINGSDESTROYED",
|
||||
"TFSTAT_HEADSHOTS",
|
||||
"TFSTAT_PLAYTIME",
|
||||
"TFSTAT_HEALING",
|
||||
"TFSTAT_INVULNS",
|
||||
"TFSTAT_KILLASSISTS",
|
||||
"TFSTAT_BACKSTABS",
|
||||
"TFSTAT_HEALTHLEACHED",
|
||||
"TFSTAT_BUILDINGSBUILT",
|
||||
"TFSTAT_MAXSENTRYKILLS",
|
||||
"TFSTAT_TELEPORTS",
|
||||
"TFSTAT_FIREDAMAGE",
|
||||
"TFSTAT_BONUS_POINTS",
|
||||
"TFSTAT_BLASTDAMAGE",
|
||||
"TFSTAT_DAMAGETAKEN",
|
||||
"TFSTAT_HEALTHKITS",
|
||||
"TFSTAT_AMMOKITS",
|
||||
"TFSTAT_CLASSCHANGES",
|
||||
"TFSTAT_CRITS",
|
||||
"TFSTAT_SUICIDES",
|
||||
"TFSTAT_CURRENCY_COLLECTED",
|
||||
"TFSTAT_DAMAGE_ASSIST",
|
||||
"TFSTAT_HEALING_ASSIST",
|
||||
"TFSTAT_DAMAGE_BOSS",
|
||||
"TFSTAT_DAMAGE_BLOCKED",
|
||||
"TFSTAT_DAMAGE_RANGED",
|
||||
"TFSTAT_DAMAGE_RANGED_CRIT_RANDOM",
|
||||
"TFSTAT_DAMAGE_RANGED_CRIT_BOOSTED",
|
||||
"TFSTAT_REVIVED",
|
||||
};
|
||||
|
||||
const char *s_pMapStatStrings[ TFMAPSTAT_TOTAL ] =
|
||||
{
|
||||
"TFSTAT_UNDEFINED",
|
||||
"TFSTAT_PLAYTIME",
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_LevelStats_t::TF_Gamestats_LevelStats_t()
|
||||
{
|
||||
m_bInitialized = false;
|
||||
m_iRoundStartTime = 0;
|
||||
m_flRoundStartTime = 0;
|
||||
m_Header.m_iRoundsPlayed = 0;
|
||||
m_Header.m_iTotalTime = 0;
|
||||
m_Header.m_iBlueWins = 0;
|
||||
m_Header.m_iRedWins = 0;
|
||||
m_Header.m_iStalemates = 0;
|
||||
m_Header.m_iBlueSuddenDeathWins = 0;
|
||||
m_Header.m_iRedSuddenDeathWins = 0;
|
||||
Q_memset( m_aClassStats, 0, sizeof( m_aClassStats ) );
|
||||
Q_memset( m_aWeaponStats, 0, sizeof( m_aWeaponStats ) );
|
||||
Q_memset( m_iPeakPlayerCount, 0, sizeof( m_iPeakPlayerCount ) );
|
||||
|
||||
for ( int i = 0; i <= MAX_CONTROL_POINTS; i++ )
|
||||
{
|
||||
m_Header.m_iLastCapChangedInRound[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_LevelStats_t::~TF_Gamestats_LevelStats_t()
|
||||
{
|
||||
//m_aPlayerDeaths.Purge();
|
||||
//m_aPlayerDamage.Purge();
|
||||
m_bIsRealServer = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Copy constructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_LevelStats_t::TF_Gamestats_LevelStats_t( const TF_Gamestats_LevelStats_t &stats )
|
||||
{
|
||||
m_bInitialized = stats.m_bInitialized;
|
||||
m_iRoundStartTime = stats.m_iRoundStartTime;
|
||||
m_flRoundStartTime = stats.m_flRoundStartTime;
|
||||
m_iMapStartTime = stats.m_iMapStartTime;
|
||||
m_Header = stats.m_Header;
|
||||
m_bIsRealServer = stats.m_bIsRealServer;
|
||||
//m_aPlayerDeaths = stats.m_aPlayerDeaths;
|
||||
//m_aPlayerDamage = stats.m_aPlayerDamage;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pszMapName -
|
||||
// nIPAddr -
|
||||
// nPort -
|
||||
// flStartTime -
|
||||
//-----------------------------------------------------------------------------
|
||||
void TF_Gamestats_LevelStats_t::Init( const char *pszMapName, int nMapRevision, int nIPAddr, short nPort, float flStartTime )
|
||||
{
|
||||
Q_memset( &m_Header, 0, sizeof( m_Header ) ); // TODO: This is correct for steamworks stats, but probably breaks old stats!!!
|
||||
|
||||
V_FileBase( pszMapName, m_Header.m_szMapName, sizeof( m_Header.m_szMapName ) );
|
||||
|
||||
m_Header.m_nMapRevision = nMapRevision;
|
||||
m_Header.m_nIPAddr = nIPAddr;
|
||||
m_Header.m_nPort = nPort;
|
||||
|
||||
#ifndef NO_STEAM
|
||||
// Start the level timer.
|
||||
m_iMapStartTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
|
||||
m_iRoundStartTime = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
|
||||
m_flRoundStartTime = gpGlobals->curtime;
|
||||
#endif
|
||||
|
||||
m_bIsRealServer = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : flEndTime -
|
||||
//-----------------------------------------------------------------------------
|
||||
void TF_Gamestats_LevelStats_t::Shutdown( float flEndTime )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_RoundStats_t::TF_Gamestats_RoundStats_t()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_RoundStats_t::~TF_Gamestats_RoundStats_t()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: resets the state of stat tracking
|
||||
//-----------------------------------------------------------------------------
|
||||
void TF_Gamestats_RoundStats_t::Reset()
|
||||
{
|
||||
ResetSummary();
|
||||
m_iRoundStartTime = 0.f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void TF_Gamestats_RoundStats_t::ResetSummary()
|
||||
{
|
||||
Q_memset( &m_Summary, 0, sizeof( m_Summary ) );
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_KillStats_t::TF_Gamestats_KillStats_t()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
// Input : -
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_KillStats_t::~TF_Gamestats_KillStats_t()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: resets the state of stat tracking
|
||||
//-----------------------------------------------------------------------------
|
||||
void TF_Gamestats_KillStats_t::Reset()
|
||||
{
|
||||
// Q_memset( &m_Summary, 0, sizeof( m_Summary ) );
|
||||
// m_flRoundStartTime = 0.f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
TFReportedStats_t::TFReportedStats_t()
|
||||
{
|
||||
Clear();
|
||||
m_bValidData = false;
|
||||
m_pCurrentGame = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
TFReportedStats_t::~TFReportedStats_t()
|
||||
{
|
||||
if ( m_pCurrentGame )
|
||||
{
|
||||
delete m_pCurrentGame;
|
||||
m_pCurrentGame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Clears data
|
||||
//-----------------------------------------------------------------------------
|
||||
void TFReportedStats_t::Clear()
|
||||
{
|
||||
m_pCurrentGame = NULL;
|
||||
m_dictMapStats.Purge();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *szMapName -
|
||||
// Output : TF_Gamestats_LevelStats_t
|
||||
//-----------------------------------------------------------------------------
|
||||
TF_Gamestats_LevelStats_t *TFReportedStats_t::FindOrAddMapStats( const char *szMapName )
|
||||
{
|
||||
int iMap = m_dictMapStats.Find( szMapName );
|
||||
if( iMap == m_dictMapStats.InvalidIndex() )
|
||||
{
|
||||
iMap = m_dictMapStats.Insert( szMapName );
|
||||
}
|
||||
|
||||
return &m_dictMapStats[iMap];
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Saves data to buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
void TFReportedStats_t::AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer )
|
||||
{
|
||||
// save a version lump at beginning of file
|
||||
TF_Gamestats_Version_t versionLump;
|
||||
versionLump.m_iMagic = TF_GAMESTATS_MAGIC;
|
||||
versionLump.m_iVersion = TF_GAMESTATS_FILE_VERSION;
|
||||
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_VERSION, 1, sizeof( versionLump ), &versionLump );
|
||||
|
||||
// Save data per map.
|
||||
for ( int iMap = m_dictMapStats.First(); iMap != m_dictMapStats.InvalidIndex(); iMap = m_dictMapStats.Next( iMap ) )
|
||||
{
|
||||
// Get the current map.
|
||||
TF_Gamestats_LevelStats_t *pCurrentMap = &m_dictMapStats[iMap];
|
||||
Assert( pCurrentMap );
|
||||
|
||||
// Write out the lumps.
|
||||
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPHEADER, 1, sizeof( TF_Gamestats_LevelStats_t::LevelHeader_t ), static_cast<void*>( &pCurrentMap->m_Header ) );
|
||||
//CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPDEATH, pCurrentMap->m_aPlayerDeaths.Count(), sizeof( TF_Gamestats_LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( pCurrentMap->m_aPlayerDeaths.Base() ) );
|
||||
//CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_MAPDAMAGE, pCurrentMap->m_aPlayerDamage.Count(), sizeof( TF_Gamestats_LevelStats_t::PlayerDamageLump_t ), static_cast<void*>( pCurrentMap->m_aPlayerDamage.Base() ) );
|
||||
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_CLASS, ARRAYSIZE( pCurrentMap->m_aClassStats ), sizeof( pCurrentMap->m_aClassStats[0] ),
|
||||
static_cast<void*>( pCurrentMap->m_aClassStats ) );
|
||||
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_WEAPON, ARRAYSIZE( pCurrentMap->m_aWeaponStats ), sizeof( pCurrentMap->m_aWeaponStats[0] ),
|
||||
static_cast<void*>( pCurrentMap->m_aWeaponStats ) );
|
||||
}
|
||||
|
||||
// Append an end tag to verify we've reached end of file and data was sane. (Sometimes we receive stat files that start sane but become filled
|
||||
// with garbage partway through.)
|
||||
CBaseGameStats::AppendLump( MAX_LUMP_COUNT, SaveBuffer, TFSTATS_LUMP_ENDTAG, 1, sizeof( versionLump ), &versionLump );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads data from buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
bool TFReportedStats_t::LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer )
|
||||
{
|
||||
// read the version lump of beginning of file and verify version
|
||||
bool bGotEndTag = false;
|
||||
unsigned short iLump = 0;
|
||||
unsigned short iLumpCount = 0;
|
||||
if ( !CBaseGameStats::GetLumpHeader( MAX_LUMP_COUNT, LoadBuffer, iLump, iLumpCount ) )
|
||||
return false;
|
||||
if ( iLump != TFSTATS_LUMP_VERSION )
|
||||
{
|
||||
Msg( "Didn't find version header. Expected lump type TFSTATS_LUMP_VERSION, got lump type %d. Skipping file.\n", iLump );
|
||||
return false;
|
||||
}
|
||||
TF_Gamestats_Version_t versionLump;
|
||||
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( versionLump ), &versionLump );
|
||||
if ( versionLump.m_iMagic != TF_GAMESTATS_MAGIC )
|
||||
{
|
||||
Msg( "Incorrect magic # in version header. Expected %x, got %x. Skipping file.\n", TF_GAMESTATS_MAGIC, versionLump.m_iMagic );
|
||||
return false;
|
||||
}
|
||||
if ( versionLump.m_iVersion != TF_GAMESTATS_FILE_VERSION )
|
||||
{
|
||||
Msg( "Mismatched file version. Expected file version %d, got %d. Skipping file.\n", TF_GAMESTATS_FILE_VERSION, versionLump.m_iVersion );
|
||||
return false;
|
||||
}
|
||||
|
||||
TF_Gamestats_LevelStats_t *pCurrentGame = NULL;
|
||||
|
||||
// read all the lumps in the file
|
||||
while( CBaseGameStats::GetLumpHeader( MAX_LUMP_COUNT, LoadBuffer, iLump, iLumpCount ) )
|
||||
{
|
||||
switch ( iLump )
|
||||
{
|
||||
case TFSTATS_LUMP_MAPHEADER:
|
||||
{
|
||||
TF_Gamestats_LevelStats_t::LevelHeader_t header;
|
||||
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::LevelHeader_t ), &header );
|
||||
|
||||
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||||
if ( ( header.m_iRoundsPlayed < 0 ) || ( header.m_iTotalTime < 0 ) || ( header.m_iRoundsPlayed > 1000 ) )
|
||||
return false;
|
||||
|
||||
// if there's no interesting data, skip this file. (Need to have server not send it in this case.)
|
||||
if ( header.m_iTotalTime == 0 )
|
||||
return false;
|
||||
|
||||
pCurrentGame = FindOrAddMapStats( header.m_szMapName );
|
||||
if ( pCurrentGame )
|
||||
{
|
||||
pCurrentGame->m_Header = header;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TFSTATS_LUMP_MAPDEATH:
|
||||
{
|
||||
//CUtlVector<TF_Gamestats_LevelStats_t::PlayerDeathsLump_t> playerDeaths;
|
||||
|
||||
//playerDeaths.SetCount( iLumpCount );
|
||||
//CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::PlayerDeathsLump_t ), static_cast<void*>( playerDeaths.Base() ) );
|
||||
//if ( pCurrentGame )
|
||||
//{
|
||||
// pCurrentGame->m_aPlayerDeaths = playerDeaths;
|
||||
//}
|
||||
break;
|
||||
}
|
||||
case TFSTATS_LUMP_MAPDAMAGE:
|
||||
{
|
||||
//CUtlVector<TF_Gamestats_LevelStats_t::PlayerDamageLump_t> playerDamage;
|
||||
|
||||
//playerDamage.SetCount( iLumpCount );
|
||||
//CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( TF_Gamestats_LevelStats_t::PlayerDamageLump_t ), static_cast<void*>( playerDamage.Base() ) );
|
||||
//if ( pCurrentGame )
|
||||
//{
|
||||
// pCurrentGame->m_aPlayerDamage = playerDamage;
|
||||
//}
|
||||
break;
|
||||
}
|
||||
case TFSTATS_LUMP_CLASS:
|
||||
{
|
||||
Assert( pCurrentGame );
|
||||
if ( !pCurrentGame )
|
||||
return false;
|
||||
Assert ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aClassStats ) );
|
||||
if ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aClassStats ) )
|
||||
{
|
||||
CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( pCurrentGame->m_aClassStats ), sizeof( pCurrentGame->m_aClassStats[0] ),
|
||||
pCurrentGame->m_aClassStats );
|
||||
|
||||
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||||
for ( int i = 0; i < ARRAYSIZE( pCurrentGame->m_aClassStats ); i++ )
|
||||
{
|
||||
TF_Gamestats_ClassStats_t &classStats = pCurrentGame->m_aClassStats[i];
|
||||
if ( ( classStats.iSpawns < 0 ) || ( classStats.iSpawns > 10000 ) || ( classStats.iTotalTime < 0 ) || ( classStats.iTotalTime > 36000 * 20 ) ||
|
||||
( classStats.iKills < 0 ) || ( classStats.iKills > 10000 ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// mismatched lump size, possibly from different build, don't know how it interpret it, just skip over it
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TFSTATS_LUMP_WEAPON:
|
||||
{
|
||||
Assert( pCurrentGame );
|
||||
if ( !pCurrentGame )
|
||||
return false;
|
||||
Assert ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aWeaponStats ) );
|
||||
if ( iLumpCount == ARRAYSIZE( pCurrentGame->m_aWeaponStats ) )
|
||||
{
|
||||
CBaseGameStats::LoadLump( LoadBuffer, ARRAYSIZE( pCurrentGame->m_aWeaponStats ), sizeof( pCurrentGame->m_aWeaponStats[0] ),
|
||||
pCurrentGame->m_aWeaponStats );
|
||||
|
||||
// quick sanity check on some data -- we get some stat files that start out OK but are corrupted later in the file
|
||||
if ( ( pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired < 0 ) || ( pCurrentGame->m_aWeaponStats[TF_WEAPON_MEDIGUN].iShotsFired > 100000 )
|
||||
|| ( pCurrentGame->m_aWeaponStats[TF_WEAPON_FLAMETHROWER_ROCKET].iShotsFired != 0 ) ) // check that unused weapon has 0 shots
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// mismatched lump size, possibly from different build, don't know how it interpret it, just skip over it
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TFSTATS_LUMP_ENDTAG:
|
||||
{
|
||||
// check that end tag is valid -- should be version lump again
|
||||
TF_Gamestats_Version_t versionLump;
|
||||
CBaseGameStats::LoadLump( LoadBuffer, iLumpCount, sizeof( versionLump ), &versionLump );
|
||||
if ( versionLump.m_iMagic != TF_GAMESTATS_MAGIC )
|
||||
{
|
||||
Msg( "Incorrect magic # in version header. Expected %x, got %x. Skipping file.\n", TF_GAMESTATS_MAGIC, versionLump.m_iMagic );
|
||||
return false;
|
||||
}
|
||||
if ( versionLump.m_iVersion != TF_GAMESTATS_FILE_VERSION )
|
||||
{
|
||||
Msg( "Mismatched file version. Expected file version %d, got %d. Skipping file.\n", TF_GAMESTATS_FILE_VERSION, versionLump.m_iVersion );
|
||||
return false;
|
||||
}
|
||||
bGotEndTag = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return bGotEndTag;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TF2 Beta Maps
|
||||
// Robot Destruction
|
||||
//-----------------------------------------------------------------------------
|
||||
RobotDestructionStats_t::RobotDestructionStats_t()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void RobotDestructionStats_t::Clear()
|
||||
{
|
||||
V_memset( &iRobotInteraction, 0, sizeof( iRobotInteraction ) );
|
||||
V_memset( &iRobotCoreInteraction, 0, sizeof( iRobotCoreInteraction ) );
|
||||
V_memset( &iFlagInteraction, 0, sizeof( iFlagInteraction ) );
|
||||
|
||||
V_memset( &iCoresCollectedByTeam, 0, sizeof( iCoresCollectedByTeam ) );
|
||||
V_memset( &iCoreCollectedByClass, 0, sizeof( iCoreCollectedByClass ) );
|
||||
|
||||
V_memset( &iBlueRobotsKilledByType, 0, sizeof( iBlueRobotsKilledByType ) );
|
||||
V_memset( &iRedRobotsKilledByType, 0, sizeof( iRedRobotsKilledByType ) );
|
||||
V_memset( &iRobotsDamageFromClass, 0, sizeof( iRobotsDamageFromClass ) );
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int RobotDestructionStats_t::GetRobotInteractionCount()
|
||||
{
|
||||
int iCount = 0;
|
||||
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||||
{
|
||||
if ( iRobotInteraction[i] )
|
||||
{
|
||||
iCount++;
|
||||
}
|
||||
}
|
||||
return iCount;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
int RobotDestructionStats_t::GetRobotCoreInteractionCount()
|
||||
{
|
||||
int iCount = 0;
|
||||
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||||
{
|
||||
if ( iRobotCoreInteraction[i] )
|
||||
{
|
||||
iCount++;
|
||||
}
|
||||
}
|
||||
return iCount;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
int RobotDestructionStats_t::GetFlagInteractionCount()
|
||||
{
|
||||
int iCount = 0;
|
||||
for ( int i = 1; i < MAX_PLAYERS; ++i )
|
||||
{
|
||||
if ( iFlagInteraction[i] )
|
||||
{
|
||||
iCount++;
|
||||
}
|
||||
}
|
||||
return iCount;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const char* g_aRoundEndReasons[] =
|
||||
{
|
||||
"round_end",
|
||||
"client_disconnect",
|
||||
"client_quit",
|
||||
"server_map_change",
|
||||
"server_shutdown",
|
||||
"time_limit_reached",
|
||||
"win_limit_reached",
|
||||
"win_diff_limit_reached",
|
||||
"round_limit_reached",
|
||||
"next_level_cvar",
|
||||
};
|
||||
|
||||
// Get a string describing the current game type.
|
||||
const char* GetGameTypeID()
|
||||
{
|
||||
ConVarRef tf_gamemode_arena( "tf_gamemode_arena" );
|
||||
ConVarRef tf_gamemode_cp( "tf_gamemode_cp" );
|
||||
ConVarRef tf_gamemode_ctf( "tf_gamemode_ctf" );
|
||||
ConVarRef tf_gamemode_sd( "tf_gamemode_sd" );
|
||||
ConVarRef tf_gamemode_payload( "tf_gamemode_payload" );
|
||||
ConVarRef tf_gamemode_mvm( "tf_gamemode_mvm" );
|
||||
ConVarRef tf_powerup_mode( "tf_powerup_mode" );
|
||||
ConVarRef tf_gamemode_passtime( "tf_gamemode_passtime" );
|
||||
|
||||
const char* pszGameTypeID = NULL;
|
||||
if ( tf_gamemode_arena.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "arena";
|
||||
}
|
||||
else if ( tf_gamemode_cp.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "cp";
|
||||
}
|
||||
else if ( tf_gamemode_ctf.GetBool() )
|
||||
{
|
||||
if ( tf_powerup_mode.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "ctf_mannpower";
|
||||
}
|
||||
else
|
||||
{
|
||||
pszGameTypeID = "ctf";
|
||||
}
|
||||
}
|
||||
else if ( tf_gamemode_sd.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "sd";
|
||||
}
|
||||
else if ( tf_gamemode_payload.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "payload";
|
||||
}
|
||||
else if ( tf_gamemode_mvm.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "mvm";
|
||||
}
|
||||
else if ( tf_gamemode_passtime.GetBool() )
|
||||
{
|
||||
pszGameTypeID = "pass"; // intentionally not "passtime"
|
||||
}
|
||||
else
|
||||
{
|
||||
pszGameTypeID = "custom";
|
||||
}
|
||||
|
||||
return pszGameTypeID;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TF2 Beta Maps
|
||||
// Passtime
|
||||
//-----------------------------------------------------------------------------
|
||||
void PasstimeStats_t::Clear()
|
||||
{
|
||||
memset( &summary, 0, sizeof(summary) );
|
||||
memset( &classes, 0, sizeof(classes) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PasstimeStats_t::AddBallFracSample( float f )
|
||||
{
|
||||
Assert( f >= 0 && f <= 1.0f );
|
||||
int iBin = (uint8) Floor2Int( f * 255 );
|
||||
summary.nBallFracHistSum += iBin;
|
||||
++summary.arrBallFracHist[ iBin ];
|
||||
++summary.nBallFracSampleCount;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PasstimeStats_t::AddPassTravelDistSample( float f )
|
||||
{
|
||||
if ( summary.nPassTravelDistSampleCount >= summary.k_nMaxPassTravelDistSamples )
|
||||
return;
|
||||
Assert( f >= 0 );
|
||||
summary.arrPassTravelDistSamples[ summary.nPassTravelDistSampleCount ] = (uint16) Float2Int( f );
|
||||
++summary.nPassTravelDistSampleCount;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
MapStats_t &GetMapStats( map_identifier_t iMapID )
|
||||
{
|
||||
return CTFStatPanel::GetMapStats( iMapID );
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,706 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_GAMESTATS_SHARED_H
|
||||
#define TF_GAMESTATS_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
#include "cbase.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "tier1/utldict.h"
|
||||
#include "shareddefs.h"
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// TF Game Stats Enums
|
||||
//
|
||||
// NOTE: You may add to the end, but do not insert to this list!
|
||||
//
|
||||
enum TFStatType_t
|
||||
{
|
||||
TFSTAT_UNDEFINED = 0,
|
||||
TFSTAT_SHOTS_HIT,
|
||||
TFSTAT_SHOTS_FIRED,
|
||||
TFSTAT_KILLS,
|
||||
TFSTAT_DEATHS,
|
||||
TFSTAT_DAMAGE,
|
||||
TFSTAT_CAPTURES,
|
||||
TFSTAT_DEFENSES,
|
||||
TFSTAT_DOMINATIONS,
|
||||
TFSTAT_REVENGE,
|
||||
TFSTAT_POINTSSCORED,
|
||||
TFSTAT_BUILDINGSDESTROYED,
|
||||
TFSTAT_HEADSHOTS,
|
||||
TFSTAT_PLAYTIME,
|
||||
TFSTAT_HEALING,
|
||||
TFSTAT_INVULNS,
|
||||
TFSTAT_KILLASSISTS,
|
||||
TFSTAT_BACKSTABS,
|
||||
TFSTAT_HEALTHLEACHED,
|
||||
TFSTAT_BUILDINGSBUILT,
|
||||
TFSTAT_MAXSENTRYKILLS,
|
||||
TFSTAT_TELEPORTS,
|
||||
TFSTAT_FIREDAMAGE,
|
||||
TFSTAT_BONUS_POINTS,
|
||||
TFSTAT_BLASTDAMAGE,
|
||||
TFSTAT_DAMAGETAKEN,
|
||||
TFSTAT_HEALTHKITS,
|
||||
TFSTAT_AMMOKITS,
|
||||
TFSTAT_CLASSCHANGES,
|
||||
TFSTAT_CRITS,
|
||||
TFSTAT_SUICIDES,
|
||||
TFSTAT_CURRENCY_COLLECTED,
|
||||
TFSTAT_DAMAGE_ASSIST,
|
||||
TFSTAT_HEALING_ASSIST,
|
||||
TFSTAT_DAMAGE_BOSS,
|
||||
TFSTAT_DAMAGE_BLOCKED,
|
||||
TFSTAT_DAMAGE_RANGED,
|
||||
TFSTAT_DAMAGE_RANGED_CRIT_RANDOM,
|
||||
TFSTAT_DAMAGE_RANGED_CRIT_BOOSTED,
|
||||
TFSTAT_REVIVED,
|
||||
TFSTAT_THROWABLEHIT,
|
||||
TFSTAT_THROWABLEKILL,
|
||||
TFSTAT_KILLSTREAK_MAX,
|
||||
TFSTAT_KILLS_RUNECARRIER,
|
||||
TFSTAT_FLAGRETURNS,
|
||||
TFSTAT_TOTAL
|
||||
};
|
||||
|
||||
#define TFSTAT_FIRST (TFSTAT_UNDEFINED+1)
|
||||
#define TFSTAT_LAST (TFSTAT_TOTAL-1)
|
||||
|
||||
extern const char *s_pStatStrings[ TFSTAT_TOTAL ];
|
||||
|
||||
enum TFMapStatType_t
|
||||
{
|
||||
TFMAPSTAT_UNDEFINED = 0,
|
||||
TFMAPSTAT_PLAYTIME,
|
||||
TFMAPSTAT_TOTAL
|
||||
};
|
||||
|
||||
#define TFMAPSTAT_FIRST (TFMAPSTAT_UNDEFINED+1)
|
||||
#define TFMAPSTAT_LAST (TFMAPSTAT_TOTAL-1)
|
||||
|
||||
extern const char *s_pMapStatStrings[ TFMAPSTAT_TOTAL ];
|
||||
|
||||
enum TFRoundEndReason_t
|
||||
{
|
||||
RE_ROUND_END,
|
||||
RE_CLIENT_DISCONNECT,
|
||||
RE_CLIENT_QUIT,
|
||||
RE_SERVER_MAP_CHANGE,
|
||||
RE_SERVER_SHUTDOWN,
|
||||
RE_TIME_LIMIT,
|
||||
RE_WIN_LIMIT,
|
||||
RE_WIN_DIFF_LIMIT,
|
||||
RE_ROUND_LIMIT,
|
||||
RE_NEXT_LEVEL_CVAR,
|
||||
MAX_ROUND_END_REASON
|
||||
};
|
||||
|
||||
extern const char *g_aRoundEndReasons[MAX_ROUND_END_REASON];
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// TF Player Round Stats
|
||||
//
|
||||
struct RoundStats_t
|
||||
{
|
||||
int m_iStat[TFSTAT_TOTAL];
|
||||
|
||||
RoundStats_t() { Reset(); };
|
||||
|
||||
inline int Get( int i ) const
|
||||
{
|
||||
AssertMsg( i >= TFSTAT_UNDEFINED && i < TFSTAT_TOTAL, "Stat index out of range!" );
|
||||
return m_iStat[ i ];
|
||||
}
|
||||
|
||||
inline void Set( int i, int nValue )
|
||||
{
|
||||
AssertMsg( i >= TFSTAT_UNDEFINED && i < TFSTAT_TOTAL, "Stat index out of range!" );
|
||||
m_iStat[ i ] = nValue;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
|
||||
{
|
||||
m_iStat[i] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void AccumulateRound( const RoundStats_t &other )
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
|
||||
{
|
||||
m_iStat[i] += other.m_iStat[i];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct RoundMapStats_t
|
||||
{
|
||||
int m_iStat[ TFMAPSTAT_TOTAL ];
|
||||
|
||||
RoundMapStats_t() { Reset(); };
|
||||
|
||||
inline int Get( int i ) const
|
||||
{
|
||||
AssertMsg( i >= TFMAPSTAT_UNDEFINED && i < TFMAPSTAT_TOTAL, "Map stat index out of range!" );
|
||||
return m_iStat[ i ];
|
||||
}
|
||||
|
||||
inline void Set( int i, int nValue )
|
||||
{
|
||||
AssertMsg( i >= TFMAPSTAT_UNDEFINED && i < TFMAPSTAT_TOTAL, "Map stat index out of range!" );
|
||||
m_iStat[ i ] = nValue;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
|
||||
{
|
||||
m_iStat[i] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void AccumulateRound( const RoundMapStats_t &other )
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
|
||||
{
|
||||
m_iStat[i] += other.m_iStat[i];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
enum TFGameStatsVersions_t
|
||||
{
|
||||
TF_GAMESTATS_FILE_VERSION = 006,
|
||||
TF_GAMESTATS_MAGIC = 0xDEADBEEF
|
||||
};
|
||||
|
||||
enum TFGameStatsLumpIds_t
|
||||
{
|
||||
TFSTATS_LUMP_VERSION = 1,
|
||||
TFSTATS_LUMP_MAPHEADER,
|
||||
TFSTATS_LUMP_MAPDEATH,
|
||||
TFSTATS_LUMP_MAPDAMAGE,
|
||||
TFSTATS_LUMP_CLASS,
|
||||
TFSTATS_LUMP_WEAPON,
|
||||
TFSTATS_LUMP_ENDTAG,
|
||||
MAX_LUMP_COUNT
|
||||
};
|
||||
|
||||
struct TF_Gamestats_Version_t
|
||||
{
|
||||
int m_iMagic; // always TF_GAMESTATS_MAGIC
|
||||
int m_iVersion;
|
||||
};
|
||||
|
||||
struct TF_Gamestats_ClassStats_t
|
||||
{
|
||||
static const unsigned short LumpId = TFSTATS_LUMP_CLASS; // Lump ids.
|
||||
int iSpawns; // total # of spawns of this class
|
||||
int iTotalTime; // aggregate player time in seconds in this class
|
||||
int iScore; // total # of points scored by this class
|
||||
int iKills; // total # of kills by this class
|
||||
int iDeaths; // total # of deaths by this class
|
||||
int iAssists; // total # of assists by this class
|
||||
int iCaptures; // total # of captures by this class
|
||||
int iClassChanges; // total # of times someone changed to this class
|
||||
|
||||
void Accumulate( TF_Gamestats_ClassStats_t &other )
|
||||
{
|
||||
iSpawns += other.iSpawns;
|
||||
iTotalTime += other.iTotalTime;
|
||||
iScore += other.iScore;
|
||||
iKills += other.iKills;
|
||||
iDeaths += other.iDeaths;
|
||||
iAssists += other.iAssists;
|
||||
iCaptures += other.iCaptures;
|
||||
iClassChanges += other.iClassChanges;
|
||||
}
|
||||
};
|
||||
|
||||
struct TF_Gamestats_WeaponStats_t
|
||||
{
|
||||
static const unsigned short LumpId = TFSTATS_LUMP_WEAPON; // Lump ids.
|
||||
int iShotsFired;
|
||||
int iCritShotsFired;
|
||||
int iHits;
|
||||
int iTotalDamage;
|
||||
int iHitsWithKnownDistance;
|
||||
int64 iTotalDistance;
|
||||
|
||||
void Accumulate( TF_Gamestats_WeaponStats_t &other )
|
||||
{
|
||||
iShotsFired += other.iShotsFired;
|
||||
iCritShotsFired += other.iCritShotsFired;
|
||||
iHits += other.iHits;
|
||||
iTotalDamage += other.iTotalDamage;
|
||||
iHitsWithKnownDistance += other.iHitsWithKnownDistance;
|
||||
iTotalDistance += other.iTotalDistance;
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// TF Game Level Stats Data
|
||||
//
|
||||
struct TF_Gamestats_LevelStats_t
|
||||
{
|
||||
public:
|
||||
|
||||
TF_Gamestats_LevelStats_t();
|
||||
~TF_Gamestats_LevelStats_t();
|
||||
TF_Gamestats_LevelStats_t( const TF_Gamestats_LevelStats_t &stats );
|
||||
|
||||
// Level start and end
|
||||
void Init( const char *pszMapName, int nMapVersion, int nIPAddr, short nPort, float flStartTime );
|
||||
void Shutdown( float flEndTime );
|
||||
|
||||
void Accumulate( TF_Gamestats_LevelStats_t *pOther )
|
||||
{
|
||||
m_Header.Accumulate( pOther->m_Header );
|
||||
//m_aPlayerDeaths.AddVectorToTail( pOther->m_aPlayerDeaths );
|
||||
//m_aPlayerDamage.AddVectorToTail( pOther->m_aPlayerDamage );
|
||||
int i;
|
||||
for ( i = 0; i < ARRAYSIZE( m_aClassStats ); i++ )
|
||||
{
|
||||
m_aClassStats[i].Accumulate( pOther->m_aClassStats[i] );
|
||||
}
|
||||
for ( i = 0; i < ARRAYSIZE( m_aWeaponStats ); i++ )
|
||||
{
|
||||
m_aWeaponStats[i].Accumulate( pOther->m_aWeaponStats[i] );
|
||||
}
|
||||
|
||||
}
|
||||
public:
|
||||
|
||||
// Level header data.
|
||||
struct LevelHeader_t
|
||||
{
|
||||
static const unsigned short LumpId = TFSTATS_LUMP_MAPHEADER; // Lump ids.
|
||||
char m_szMapName[64]; // Name of the map.
|
||||
int m_nMapRevision; // Version number for the map.
|
||||
unsigned int m_nIPAddr; // IP Address of the server - 4 bytes stored as an int.
|
||||
unsigned short m_nPort; // Port the server is using.
|
||||
int m_iRoundsPlayed; // # of rounds played
|
||||
int m_iTotalTime; // total # of seconds of all rounds
|
||||
int m_iBlueWins; // # of blue team wins
|
||||
int m_iRedWins; // # of red team wins
|
||||
int m_iStalemates; // # of stalemates
|
||||
int m_iBlueSuddenDeathWins; // # of blue team wins during sudden death
|
||||
int m_iRedSuddenDeathWins; // # of red team wins during sudden death
|
||||
int m_iLastCapChangedInRound[MAX_CONTROL_POINTS+1]; // # of times a round ended on each control point
|
||||
|
||||
void Accumulate( LevelHeader_t &other )
|
||||
{
|
||||
m_iRoundsPlayed += other.m_iRoundsPlayed;
|
||||
m_iTotalTime += other.m_iTotalTime;
|
||||
m_iBlueWins += other.m_iBlueWins;
|
||||
m_iRedWins += other.m_iRedWins;
|
||||
m_iStalemates += other.m_iStalemates;
|
||||
m_iBlueSuddenDeathWins += other.m_iBlueSuddenDeathWins;
|
||||
m_iRedSuddenDeathWins += other.m_iRedSuddenDeathWins;
|
||||
for ( int i = 0; i <= MAX_CONTROL_POINTS; i++ )
|
||||
{
|
||||
m_iLastCapChangedInRound[i] += other.m_iLastCapChangedInRound[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Player deaths.
|
||||
struct PlayerDeathsLump_t
|
||||
{
|
||||
static const unsigned short LumpId = TFSTATS_LUMP_MAPDEATH; // Lump ids.
|
||||
short nPosition[3]; // Position of death.
|
||||
short iWeapon; // Weapon that killed the player.
|
||||
unsigned short iDistance; // Distance the attacker was from the player.
|
||||
byte iAttackClass; // Class that killed the player.
|
||||
byte iTargetClass; // Class of the player killed.
|
||||
};
|
||||
|
||||
// Player damage.
|
||||
struct PlayerDamageLump_t
|
||||
{
|
||||
static const unsigned short LumpId = TFSTATS_LUMP_MAPDAMAGE; // Lump ids.
|
||||
float fTime; // Time of the damage event
|
||||
short nTargetPosition[3]; // Position of target.
|
||||
short nAttackerPosition[3]; // Position of attacker.
|
||||
short iDamage; // Total damage.
|
||||
short iWeapon; // Weapon used.
|
||||
byte iAttackClass; // Class of the attacker
|
||||
byte iTargetClass; // Class of the target
|
||||
byte iCrit; // was the shot a crit?
|
||||
byte iKill; // did the shot kill the target?
|
||||
};
|
||||
|
||||
// Data.
|
||||
LevelHeader_t m_Header; // Level header.
|
||||
// Disabling These Fields
|
||||
//CUtlVector<PlayerDeathsLump_t> m_aPlayerDeaths; // Vector of player deaths.
|
||||
//CUtlVector<PlayerDamageLump_t> m_aPlayerDamage; // Vector of player damage.
|
||||
bool m_bIsRealServer;
|
||||
TF_Gamestats_ClassStats_t m_aClassStats[TF_CLASS_COUNT_ALL]; // Vector of class data
|
||||
TF_Gamestats_WeaponStats_t m_aWeaponStats[TF_WEAPON_COUNT]; // Vector of weapon data
|
||||
// Temporary data.
|
||||
bool m_bInitialized; // Has the map Map Stat Data been initialized.
|
||||
time_t m_iMapStartTime;
|
||||
time_t m_iRoundStartTime; // time_t version for steamworks stats
|
||||
float m_flRoundStartTime;
|
||||
int m_iPeakPlayerCount[TF_TEAM_COUNT];
|
||||
};
|
||||
|
||||
struct TF_Gamestats_RoundStats_t
|
||||
{
|
||||
public:
|
||||
|
||||
TF_Gamestats_RoundStats_t();
|
||||
~TF_Gamestats_RoundStats_t();
|
||||
|
||||
private:
|
||||
TF_Gamestats_RoundStats_t( const TF_Gamestats_RoundStats_t &stats ) {}
|
||||
|
||||
public:
|
||||
void Reset();
|
||||
void ResetSummary();
|
||||
|
||||
struct RoundSummary_t
|
||||
{
|
||||
int iTeamQuit;
|
||||
int iPoints;
|
||||
int iBonusPoints;
|
||||
int iKills;
|
||||
int iDeaths;
|
||||
int iSuicides;
|
||||
int iAssists;
|
||||
int iBuildingsBuilt;
|
||||
int iBuildingsDestroyed;
|
||||
int iHeadshots;
|
||||
int iDominations;
|
||||
int iRevenges;
|
||||
int iInvulns;
|
||||
int iTeleports;
|
||||
int iDamageDone;
|
||||
int iHealingDone;
|
||||
int iCrits;
|
||||
int iBackstabs;
|
||||
int iThrowableHits;
|
||||
int iThrowableKills;
|
||||
};
|
||||
|
||||
RoundSummary_t m_Summary;
|
||||
|
||||
static time_t m_iRoundStartTime;
|
||||
static int m_iNumRounds;
|
||||
};
|
||||
|
||||
struct TF_Gamestats_KillStats_t
|
||||
{
|
||||
public:
|
||||
TF_Gamestats_KillStats_t();
|
||||
~TF_Gamestats_KillStats_t();
|
||||
|
||||
private:
|
||||
TF_Gamestats_KillStats_t( const TF_Gamestats_KillStats_t &stats ) {}
|
||||
|
||||
public:
|
||||
void Reset();
|
||||
};
|
||||
|
||||
// Old style killstats matrix.
|
||||
struct KillStats_t
|
||||
{
|
||||
KillStats_t() { Reset(); }
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Q_memset( iNumKilled, 0, sizeof( iNumKilled ) );
|
||||
Q_memset( iNumKilledBy, 0, sizeof( iNumKilledBy ) );
|
||||
Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
|
||||
}
|
||||
|
||||
int iNumKilled[MAX_PLAYERS+1]; // how many times this player has killed every other player
|
||||
int iNumKilledBy[MAX_PLAYERS+1]; // how many times this player has been killed by every other player
|
||||
int iNumKilledByUnanswered[MAX_PLAYERS+1]; // how many unanswered kills this player has been dealt by every other player
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// LoadoutStats
|
||||
struct LoadoutStats_t
|
||||
{
|
||||
LoadoutStats_t() { Reset(); }
|
||||
|
||||
void Reset()
|
||||
{
|
||||
V_memset( iLoadoutItemDefIndices, INVALID_ITEM_DEF_INDEX, sizeof( iLoadoutItemDefIndices ) );
|
||||
V_memset( iLoadoutItemQualities, AE_UNDEFINED, sizeof( iLoadoutItemQualities ) );
|
||||
V_memset( iLoadoutItemStyles, 0, sizeof( iLoadoutItemStyles ) );
|
||||
|
||||
flStartTime = 0;
|
||||
iClass = TF_CLASS_UNDEFINED;
|
||||
}
|
||||
|
||||
void Set ( int iPlayerClass )
|
||||
{
|
||||
iClass = iPlayerClass;
|
||||
flStartTime = gpGlobals->curtime;
|
||||
}
|
||||
|
||||
void SetItemDef ( int iSlot, itemid_t iItemDef, entityquality_t iItemQuality, style_index_t iStyle )
|
||||
{
|
||||
iLoadoutItemDefIndices[iSlot] = iItemDef;
|
||||
iLoadoutItemQualities[iSlot] = iItemQuality;
|
||||
iLoadoutItemStyles[iSlot] = iStyle;
|
||||
}
|
||||
|
||||
item_definition_index_t iLoadoutItemDefIndices[CLASS_LOADOUT_POSITION_COUNT];
|
||||
entityquality_t iLoadoutItemQualities[CLASS_LOADOUT_POSITION_COUNT];
|
||||
style_index_t iLoadoutItemStyles[CLASS_LOADOUT_POSITION_COUNT];
|
||||
float flStartTime;
|
||||
int iClass;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// TF Player Stats
|
||||
//
|
||||
struct PlayerStats_t
|
||||
{
|
||||
PlayerStats_t()
|
||||
{
|
||||
Reset();
|
||||
};
|
||||
|
||||
void Reset()
|
||||
{
|
||||
statsCurrentLife.Reset();
|
||||
statsCurrentRound.Reset();
|
||||
statsAccumulated.Reset();
|
||||
mapStatsCurrentLife.Reset();
|
||||
mapStatsCurrentRound.Reset();
|
||||
mapStatsAccumulated.Reset();
|
||||
statsKills.Reset();
|
||||
loadoutStats.Reset();
|
||||
iConnectTime = 0;
|
||||
iDisconnectTime = 0;
|
||||
}
|
||||
|
||||
PlayerStats_t( const PlayerStats_t &other )
|
||||
{
|
||||
statsCurrentLife = other.statsCurrentLife;
|
||||
statsCurrentRound = other.statsCurrentRound;
|
||||
statsAccumulated = other.statsAccumulated;
|
||||
mapStatsCurrentLife = other.mapStatsCurrentLife;
|
||||
mapStatsCurrentRound = other.mapStatsCurrentRound;
|
||||
mapStatsAccumulated = other.mapStatsAccumulated;
|
||||
loadoutStats = other.loadoutStats;
|
||||
iConnectTime = other.iConnectTime;
|
||||
iDisconnectTime = other.iDisconnectTime;
|
||||
}
|
||||
|
||||
RoundStats_t statsCurrentLife;
|
||||
RoundStats_t statsCurrentRound;
|
||||
RoundStats_t statsAccumulated;
|
||||
RoundMapStats_t mapStatsCurrentLife;
|
||||
RoundMapStats_t mapStatsCurrentRound;
|
||||
RoundMapStats_t mapStatsAccumulated;
|
||||
KillStats_t statsKills;
|
||||
LoadoutStats_t loadoutStats;
|
||||
int iConnectTime;
|
||||
int iDisconnectTime;
|
||||
};
|
||||
|
||||
// reported stats structure that contains all stats data uploaded from TF server to Steam. Note that this
|
||||
// code is shared between TF server and processgamestats, which cracks the data file on the back end
|
||||
struct TFReportedStats_t
|
||||
{
|
||||
TFReportedStats_t();
|
||||
~TFReportedStats_t();
|
||||
void Clear();
|
||||
TF_Gamestats_LevelStats_t *FindOrAddMapStats( const char *szMapName );
|
||||
#ifdef GAME_DLL
|
||||
void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer );
|
||||
bool LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer );
|
||||
#endif
|
||||
|
||||
bool m_bValidData;
|
||||
TF_Gamestats_LevelStats_t *m_pCurrentGame;
|
||||
CUtlDict<TF_Gamestats_LevelStats_t, unsigned short> m_dictMapStats;
|
||||
};
|
||||
|
||||
struct ClassStats_t
|
||||
{
|
||||
int iPlayerClass; // which class these stats refer to
|
||||
int iNumberOfRounds; // how many times player has played this class
|
||||
RoundStats_t accumulated;
|
||||
RoundStats_t max;
|
||||
RoundStats_t currentRound;
|
||||
|
||||
RoundStats_t accumulatedMVM;
|
||||
RoundStats_t maxMVM;
|
||||
|
||||
ClassStats_t()
|
||||
{
|
||||
iPlayerClass = TF_CLASS_UNDEFINED;
|
||||
iNumberOfRounds = 0;
|
||||
}
|
||||
|
||||
void AccumulateRound( const RoundStats_t &other )
|
||||
{
|
||||
iNumberOfRounds++;
|
||||
accumulated.AccumulateRound( other );
|
||||
currentRound = other;
|
||||
}
|
||||
|
||||
void AccumulateMVMRound( const RoundStats_t &other )
|
||||
{
|
||||
iNumberOfRounds++;
|
||||
accumulatedMVM.AccumulateRound( other );
|
||||
currentRound = other;
|
||||
}
|
||||
};
|
||||
|
||||
struct MapStats_t
|
||||
{
|
||||
map_identifier_t iMapID; // which map these stats refer to
|
||||
int iNumberOfRounds; // how many times player has played this map
|
||||
RoundMapStats_t accumulated;
|
||||
RoundMapStats_t currentRound;
|
||||
|
||||
MapStats_t()
|
||||
{
|
||||
iMapID = 0xFFFFFFFF;
|
||||
iNumberOfRounds = 0;
|
||||
}
|
||||
|
||||
void AccumulateRound( const RoundMapStats_t &other )
|
||||
{
|
||||
iNumberOfRounds++;
|
||||
accumulated.AccumulateRound( other );
|
||||
currentRound = other;
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Beta Map Stats
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
// Robot Destruction
|
||||
struct RobotDestructionStats_t
|
||||
{
|
||||
RobotDestructionStats_t();
|
||||
|
||||
void Clear();
|
||||
int GetRobotInteractionCount();
|
||||
int GetRobotCoreInteractionCount();
|
||||
int GetFlagInteractionCount();
|
||||
|
||||
// Robot Cores Collected
|
||||
int iCoresCollectedByTeam[ TF_TEAM_COUNT ];
|
||||
|
||||
// Collected By What Class
|
||||
int iCoreCollectedByClass[ TF_CLASS_COUNT ];
|
||||
|
||||
// Robots Killed By Type
|
||||
// eRobotType::NUM_ROBOT_TYPES
|
||||
int iBlueRobotsKilledByType[ 3 ];
|
||||
int iRedRobotsKilledByType[ 3 ];
|
||||
|
||||
int iRobotsDamageFromClass[ TF_CLASS_COUNT ];
|
||||
|
||||
// Player Interaction
|
||||
int iRobotInteraction[MAX_PLAYERS];
|
||||
int iRobotCoreInteraction[MAX_PLAYERS];
|
||||
int iFlagInteraction[MAX_PLAYERS];
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Cactus Canyon
|
||||
|
||||
//=============================================================================
|
||||
// Passtime
|
||||
struct PasstimeStats_t
|
||||
{
|
||||
PasstimeStats_t() { Clear(); }
|
||||
void Clear();
|
||||
void AddBallFracSample( float f );
|
||||
void AddPassTravelDistSample( float f );
|
||||
|
||||
// To get comprehensive class stats, we need an event log instead of a summary.
|
||||
// But for now this should cover what we need.
|
||||
// These class stats were specifically requested by Travis@br, in addition to
|
||||
// total kills by class. The total kills by class is tracked by TF already.
|
||||
struct Classes_t
|
||||
{
|
||||
int nTotalScores;
|
||||
int nTotalCarrySec;
|
||||
} classes[TF_CLASS_COUNT_ALL];
|
||||
|
||||
struct RoundSummary_t
|
||||
{
|
||||
int nTotalPassesStarted;
|
||||
int nTotalPassesFailed;
|
||||
int nTotalPassesShotDown;
|
||||
int nTotalPassesCompleted;
|
||||
int nTotalPassesCompletedNearGoal;
|
||||
int nTotalPassesIntercepted;
|
||||
int nTotalPassesInterceptedNearGoal;
|
||||
int nTotalPassRequests;
|
||||
int nTotalTosses;
|
||||
int nTotalTossesCompleted;
|
||||
int nTotalTossesIntercepted;
|
||||
int nTotalTossesInterceptedNearGoal;
|
||||
int nTotalSteals;
|
||||
int nTotalStealsNearGoal;
|
||||
int nTotalBallSpawnShots;
|
||||
int nTotalScores;
|
||||
int nTotalRecoveries;
|
||||
int nTotalCarrySec;
|
||||
int nTotalWinningTeamBallCarrySec;
|
||||
int nTotalLosingTeamBallCarrySec;
|
||||
int nTotalThrowCancels;
|
||||
int nTotalSpeedBoosts;
|
||||
int nTotalJumpPads;
|
||||
int nTotalCarrierSpeedBoosts;
|
||||
int nTotalCarrierJumpPads;
|
||||
int nTotalBallDeflects;
|
||||
int nBallNeutralSec;
|
||||
int nGoalType;
|
||||
int nRoundEndReason;
|
||||
int nRoundRemainingSec;
|
||||
int nRoundMaxSec;
|
||||
int nPlayersRedMax;
|
||||
int nPlayersBlueMax;
|
||||
int nScoreBlue;
|
||||
int nScoreRed;
|
||||
bool bStalemate;
|
||||
bool bSuddenDeath;
|
||||
bool bMeleeOnlySuddenDeath;
|
||||
|
||||
// histogram used to create min/max/mean/med/mode/stdev stats
|
||||
uint32 nBallFracSampleCount;
|
||||
uint32 arrBallFracHist[ 256 ];
|
||||
uint32 nBallFracHistSum;
|
||||
|
||||
// sample set used to create min/max/mean/med/stdev stats
|
||||
static const uint32 k_nMaxPassTravelDistSamples = 1024;
|
||||
uint32 nPassTravelDistSampleCount;
|
||||
uint16 arrPassTravelDistSamples[ k_nMaxPassTravelDistSamples ];
|
||||
} summary;
|
||||
};
|
||||
|
||||
const char* GetGameTypeID();
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
MapStats_t &GetMapStats( map_identifier_t iMapID );
|
||||
#endif
|
||||
|
||||
#endif // TF_GAMESTATS_SHARED_H
|
||||
@@ -0,0 +1,162 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
#ifndef _TF_GC_SHARED_H
|
||||
#define _TF_GC_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/msgprotobuf.h"
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
#define MMLog(...) do { Log( __VA_ARGS__ ); } while(false)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ReliableMessage - A message/job class that retry until confirmed, and be sent
|
||||
// In order with other such messages.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Check for pending messages
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool BPendingReliableMessages();
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
GCSDK::CGCClientJob *s_pCurrentConfirmJob = NULL;
|
||||
CUtlQueue< GCSDK::CGCClientJob * > s_queuePendingConfirmJobs;
|
||||
|
||||
template < typename RELIABLE_MSG_CLASS, typename MSG_TYPE, ETFGCMsg E_MSG_TYPE, typename REPLY_TYPE, ETFGCMsg E_REPLY_TYPE>
|
||||
class CJobReliableMessageBase : public GCSDK::CGCClientJob
|
||||
{
|
||||
public:
|
||||
typedef CProtoBufMsg< MSG_TYPE > Msg_t;
|
||||
typedef CProtoBufMsg< REPLY_TYPE > Reply_t;
|
||||
|
||||
CJobReliableMessageBase()
|
||||
: GCSDK::CGCClientJob( GCClientSystem()->GetGCClient() )
|
||||
, m_msg( E_MSG_TYPE )
|
||||
, m_msgReply()
|
||||
{}
|
||||
|
||||
Msg_t &Msg() { return m_msg; }
|
||||
void Enqueue()
|
||||
{
|
||||
static_cast<RELIABLE_MSG_CLASS *>(this)->InitDebugString( m_strDebug );
|
||||
MMLog( "[SendMsgUntilConfirmed] %s queued for %s\n", GetMsgName(), DebugString() );
|
||||
|
||||
if ( !s_pCurrentConfirmJob )
|
||||
{
|
||||
s_pCurrentConfirmJob = this;
|
||||
this->StartJobDelayed( NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue, confirm jobs will kick next in queue as necessary
|
||||
s_queuePendingConfirmJobs.Insert( this );
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool BYieldingRunJob( void *pvStartParam )
|
||||
{
|
||||
Assert( s_pCurrentConfirmJob == this );
|
||||
bool bRet = BYieldingRunJobInternal();
|
||||
|
||||
if ( s_queuePendingConfirmJobs.Count() )
|
||||
{
|
||||
// Kick off next job
|
||||
s_pCurrentConfirmJob = s_queuePendingConfirmJobs.RemoveAtHead();
|
||||
s_pCurrentConfirmJob->StartJob( NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_pCurrentConfirmJob = NULL;
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
bool BYieldingRunJobInternal()
|
||||
{
|
||||
MMLog( "[SendMsgUntilConfirmed] %s started for %s\n", GetMsgName(), DebugString() );
|
||||
|
||||
// Trigger OnPrepare
|
||||
static_cast<RELIABLE_MSG_CLASS *>(this)->OnPrepare();
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
BYieldingWaitOneFrame();
|
||||
|
||||
// Create and load the message
|
||||
// continuously attempt to send the message to the GC
|
||||
BYldSendMessageAndGetReply_t result = BYldSendMessageAndGetReplyEx( m_msg, 30, &m_msgReply, E_REPLY_TYPE );
|
||||
|
||||
switch ( result )
|
||||
{
|
||||
case BYLDREPLY_SUCCESS:
|
||||
MMLog( "[SendMsgUntilConfirmed] %s successfully sent for %s\n",
|
||||
GetMsgName(), DebugString() );
|
||||
// Trigger OnReply
|
||||
static_cast<RELIABLE_MSG_CLASS *>(this)->OnReply( m_msgReply );
|
||||
return true;
|
||||
case BYLDREPLY_SEND_FAILED:
|
||||
MMLog( "[SendMsgUntilConfirmed] %s send FAILED for %s -- retrying\n",
|
||||
GetMsgName(), DebugString() );
|
||||
break;
|
||||
case BYLDREPLY_TIMEOUT:
|
||||
MMLog( "[SendMsgUntilConfirmed] %s send TIMEOUT for %s -- retrying\n",
|
||||
GetMsgName(), DebugString() );
|
||||
break;
|
||||
case BYLDREPLY_MSG_TYPE_MISMATCH:
|
||||
MMLog( "[SendMsgUntilConfirmed] %s send TYPE MISMATCH for %s\n",
|
||||
GetMsgName(), DebugString() );
|
||||
Assert( !"Mismatched response type in reliable message" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// Overrides
|
||||
|
||||
// Must be overridden by reliable message implementers. Debug string is e.g. "Match 12345, Lobby 4"
|
||||
void InitDebugString( CUtlString &debugStr ) {}
|
||||
const char *MsgName() { return "<unknown>"; }
|
||||
|
||||
// Optionally overridden
|
||||
void OnReply( Reply_t &msgReply ) {}
|
||||
// Called before sending, after previous messages in queue have flushed
|
||||
void OnPrepare() {}
|
||||
|
||||
private:
|
||||
const char *DebugString() { return m_strDebug.Get(); }
|
||||
|
||||
// Forward to override
|
||||
const char *GetMsgName() { return static_cast<RELIABLE_MSG_CLASS *>(this)->MsgName(); }
|
||||
|
||||
Msg_t m_msg;
|
||||
Reply_t m_msgReply;
|
||||
CUtlString m_strDebug;
|
||||
|
||||
void _static_asserts() {
|
||||
// Ensure we passed an override and provided provided these
|
||||
#if __cplusplus >= 201103L && !defined ( OSX ) // (Don't have time to figure out what criteria the OS X toolchain has to not blow this)
|
||||
static_assert( std::is_base_of< decltype( *this ), RELIABLE_MSG_CLASS >::value,
|
||||
"RELIABLE_MSG_CLASS Must be an override of this base" );
|
||||
static_assert( !std::is_same< decltype( &(decltype( *this )::InitDebugString) ),
|
||||
decltype( &RELIABLE_MSG_CLASS::InitDebugString ) >::value && \
|
||||
!std::is_same< decltype( &(decltype( *this )::MsgName) ),
|
||||
decltype( &RELIABLE_MSG_CLASS::MsgName ) >::value,
|
||||
"RELIABLE_MSG_CLASS class must override DebugString and MsgName" );
|
||||
#endif // __cplusplus >= 201103L && !defined ( OSX )
|
||||
}
|
||||
};
|
||||
|
||||
static bool BPendingReliableMessages()
|
||||
{
|
||||
Assert( !s_queuePendingConfirmJobs.Count() || s_pCurrentConfirmJob );
|
||||
return !!s_pCurrentConfirmJob || s_queuePendingConfirmJobs.Count();
|
||||
}
|
||||
|
||||
|
||||
#endif // _TF_GC_SHARED_H
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Provides names for GC message types for TF
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gcsdk/gcsdk_auto.h"
|
||||
#include "tf_gcmessages.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
uint32 GetKickBanPlayerReason( const char *pReasonString )
|
||||
{
|
||||
if ( Q_strncmp( pReasonString, "other", 5 ) == 0 )
|
||||
{
|
||||
return kVoteKickBanPlayerReason_Other;
|
||||
}
|
||||
else if ( Q_strncmp( pReasonString, "cheating", 8 ) == 0 )
|
||||
{
|
||||
return kVoteKickBanPlayerReason_Cheating;
|
||||
}
|
||||
else if ( Q_strncmp( pReasonString, "idle", 4 ) == 0 )
|
||||
{
|
||||
return kVoteKickBanPlayerReason_Idle;
|
||||
}
|
||||
else if ( Q_strncmp( pReasonString, "scamming", 8 ) == 0 )
|
||||
{
|
||||
return kVoteKickBanPlayerReason_Scamming;
|
||||
}
|
||||
return kVoteKickBanPlayerReason_Other;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: This file defines all of our over-the-wire net protocols for the
|
||||
// Game Coordinator for Team Fortress. Note that we never use types
|
||||
// with undefined length (like int). Always use an explicit type
|
||||
// (like int32).
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_GCMESSAGES_H
|
||||
#define TF_GCMESSAGES_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "language.h"
|
||||
#include "gcsdk/gcsystemmsgs.h"
|
||||
|
||||
// Protobuf headers interfere with the valve min/max/malloc overrides. so we need to do all
|
||||
// this funky wrapping to make the include happy.
|
||||
#include <tier0/valve_minmax_off.h>
|
||||
|
||||
#include "tf_gcmessages.pb.h"
|
||||
#ifdef GC
|
||||
#include "tf_gcmessages_interserver.pb.h" // These should not be exposed to clients/servers
|
||||
#endif // #ifdef GC
|
||||
|
||||
#include <tier0/valve_minmax_on.h>
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type IDs for TF GC classes. These are part of the client-GC protocol and
|
||||
// should not change if it can be helped
|
||||
//-----------------------------------------------------------------------------
|
||||
enum EGCTFProtoObjectTypes
|
||||
{
|
||||
k_EProtoObjectTypesGameBase = 2000,
|
||||
|
||||
// k_EProtoObjectHeroStandings = k_EProtoObjectTypesGameBase + 1,
|
||||
// k_EProtoObjectGameAccountClient = k_EProtoObjectTypesGameBase + 2,
|
||||
k_EProtoObjectTFParty = k_EProtoObjectTypesGameBase + 3,
|
||||
k_EProtoObjectTFGameServerLobby = k_EProtoObjectTypesGameBase + 4,
|
||||
// k_EProtoObjectBetaParticipation = k_EProtoObjectTypesGameBase + 5,
|
||||
k_EProtoObjectTFPartyInvite = k_EProtoObjectTypesGameBase + 6,
|
||||
k_EProtoObjectTFRatingData = k_EProtoObjectTypesGameBase + 7,
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// Duel
|
||||
|
||||
// k_EMsgGC_Duel_Request
|
||||
struct MsgGC_Duel_Request_t
|
||||
{
|
||||
uint64 m_ulInitiatorSteamID;
|
||||
uint64 m_ulTargetSteamID;
|
||||
uint8 m_usAsPlayerClass;
|
||||
};
|
||||
|
||||
// k_EMsgGC_Duel_Response
|
||||
struct MsgGC_Duel_Response_t
|
||||
{
|
||||
uint64 m_ulInitiatorSteamID;
|
||||
uint64 m_ulTargetSteamID;
|
||||
bool m_bAccepted;
|
||||
uint8 m_usAsPlayerClass;
|
||||
};
|
||||
|
||||
// k_EMsgGC_Duel_Results
|
||||
struct MsgGC_Duel_Results_t
|
||||
{
|
||||
uint64 m_ulInitiatorSteamID;
|
||||
uint64 m_ulTargetSteamID;
|
||||
uint64 m_ulWinnerSteamID;
|
||||
uint16 m_usScoreInitiator;
|
||||
uint16 m_usScoreTarget;
|
||||
uint8 m_usEndReason;
|
||||
};
|
||||
|
||||
// k_EMsgGC_Duel_Status
|
||||
enum EGCDuelStatus
|
||||
{
|
||||
kDuel_Status_Invalid = -1,
|
||||
kDuel_Status_AlreadyInDuel_Inititator,
|
||||
kDuel_Status_AlreadyInDuel_Target,
|
||||
kDuel_Status_DuelBanned_Initiator,
|
||||
kDuel_Status_DuelBanned_Target,
|
||||
kDuel_Status_MissingSession, // could be gameserver session or target client session
|
||||
kDuel_Status_Cancelled,
|
||||
};
|
||||
struct MsgGC_Duel_Status_t
|
||||
{
|
||||
uint8 m_usStatus;
|
||||
uint64 m_ulInitiatorSteamID;
|
||||
uint64 m_ulTargetSteamID;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// k_EMsgGC_MM_RequestMatch
|
||||
struct MsgGC_MM_RequestMatch_t
|
||||
{
|
||||
uint32 m_unRequiredGameServerFlags;
|
||||
// string with map name
|
||||
};
|
||||
|
||||
// k_EMsgGC_MM_RequestMatchResponse
|
||||
struct MsgGC_MM_RequestMatchResponse_t
|
||||
{
|
||||
bool m_bServerFound;
|
||||
uint32 m_iServerAddress;
|
||||
uint16 m_iServerPort;
|
||||
};
|
||||
|
||||
// k_EMsgGC_MM_ReserveSpot
|
||||
struct MsgGC_MM_ReserveSpot_t
|
||||
{
|
||||
uint64 m_ulSteamID;
|
||||
};
|
||||
|
||||
// k_EMsgGC_MM_LoadMap
|
||||
struct MsgGC_MM_LoadMap_t
|
||||
{
|
||||
// string with map name
|
||||
};
|
||||
|
||||
struct MsgGCChatMessage_t
|
||||
{
|
||||
// string sChannelName
|
||||
// string sPersonaName
|
||||
int32 m_cMsgLen;
|
||||
// binary message
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// do not re-order, stored in DB
|
||||
enum
|
||||
{
|
||||
kVoteKickBanPlayerReason_Other,
|
||||
kVoteKickBanPlayerReason_Cheating,
|
||||
kVoteKickBanPlayerReason_Idle,
|
||||
kVoteKickBanPlayerReason_Scamming,
|
||||
};
|
||||
|
||||
uint32 GetKickBanPlayerReason( const char *pReasonString );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
// Normal:
|
||||
#define MATCHMAKING_SPEWLEVEL4 4
|
||||
#define MATCHMAKING_SPEWLEVEL3 4
|
||||
#define MATCHMAKING_SPEWLEVEL2 2
|
||||
|
||||
// Use these defines to crank up the spew level
|
||||
//#define MATCHMAKING_SPEWLEVEL4 1
|
||||
//#define MATCHMAKING_SPEWLEVEL3 1
|
||||
//#define MATCHMAKING_SPEWLEVEL2 1
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
$Project
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "$SRCDIR\game\shared\tf\tf_gcmessages.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\game\shared\tf\tf_gcmessages.h"
|
||||
$File "$SRCDIR\game\shared\tf\tf_matchmaking_scoring.h"
|
||||
}
|
||||
|
||||
$Folder "Protobuf Files"
|
||||
{
|
||||
$File "$SRCDIR\game\shared\tf\tf_gcmessages.proto"
|
||||
$DynamicFile "$GENERATED_PROTO_DIR\tf_gcmessages.pb.h"
|
||||
$DynamicFile "$GENERATED_PROTO_DIR\tf_gcmessages.pb.cc" [!$OSXALL]
|
||||
{
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$Create/UsePrecompiledHeader "Not Using Precompiled Headers"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// TF Generic Bomb
|
||||
//
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
#include "tf_generic_bomb.h"
|
||||
#include "takedamageinfo.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "props_shared.h"
|
||||
#ifdef GAME_DLL
|
||||
#include "te_effect_dispatch.h"
|
||||
#include "tf_fx.h"
|
||||
#include "tf_projectile_base.h"
|
||||
#include "basegrenade_shared.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_weaponbase_rocket.h"
|
||||
#endif
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_generic_bomb, CTFGenericBomb );
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFGenericBomb, DT_TFGenericBomb )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTFGenericBomb, DT_TFGenericBomb )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
IMPLEMENT_AUTO_LIST( ITFGenericBomb );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
BEGIN_DATADESC( CTFGenericBomb )
|
||||
// Keyfields
|
||||
DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "damage" ),
|
||||
DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
|
||||
DEFINE_KEYFIELD( m_nHealth, FIELD_INTEGER, "health" ),
|
||||
DEFINE_KEYFIELD( m_strExplodeParticleName, FIELD_STRING, "explode_particle" ),
|
||||
DEFINE_KEYFIELD( m_strExplodeSoundName, FIELD_STRING, "sound" ),
|
||||
DEFINE_KEYFIELD( m_eWhoToDamage, FIELD_INTEGER, "friendlyfire" ),
|
||||
|
||||
// Output
|
||||
DEFINE_OUTPUT( m_OnDetonate, "OnDetonate" ),
|
||||
|
||||
// Input
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "Detonate", Detonate ),
|
||||
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFGenericBomb::CTFGenericBomb()
|
||||
{
|
||||
m_bDead = false;
|
||||
m_bPrecached = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFGenericBomb::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
// always allow late precaching
|
||||
bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
|
||||
CBaseEntity::SetAllowPrecache( true );
|
||||
|
||||
int iModel = PrecacheModel( STRING( GetModelName() ) );
|
||||
PrecacheGibsForModel( iModel );
|
||||
PrecacheModel( STRING( GetModelName() ) );
|
||||
if ( STRING( m_strExplodeParticleName ) && STRING( m_strExplodeParticleName )[0] )
|
||||
{
|
||||
PrecacheParticleSystem( STRING( m_strExplodeParticleName ) );
|
||||
}
|
||||
|
||||
if ( STRING( m_strExplodeSoundName ) && STRING( m_strExplodeSoundName )[0] )
|
||||
{
|
||||
PrecacheScriptSound( STRING( m_strExplodeSoundName ) );
|
||||
}
|
||||
|
||||
CBaseEntity::SetAllowPrecache( bAllowPrecache );
|
||||
|
||||
m_bPrecached = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFGenericBomb::Spawn()
|
||||
{
|
||||
if ( !m_bPrecached )
|
||||
{
|
||||
Precache();
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
SetModel( STRING( GetModelName() ) );
|
||||
#endif
|
||||
SetMoveType( MOVETYPE_VPHYSICS );
|
||||
SetSolid( SOLID_VPHYSICS );
|
||||
|
||||
SetHealth( m_nHealth );
|
||||
|
||||
BaseClass::Spawn();
|
||||
|
||||
m_takedamage = DAMAGE_YES;
|
||||
m_bDead = false;
|
||||
|
||||
SetTouch( &CTFGenericBombShim::Touch );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFGenericBomb::GenericTouch( CBaseEntity *pOther )
|
||||
{
|
||||
if ( !pOther )
|
||||
return;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
if ( pOther->GetFlags() & FL_GRENADE )
|
||||
{
|
||||
// Only let my team destroy
|
||||
CBaseEntity *pAttacker = NULL;
|
||||
|
||||
CBaseGrenade *pGrenade = dynamic_cast<CBaseGrenade*>(pOther);
|
||||
if ( pGrenade )
|
||||
{
|
||||
pAttacker = pGrenade->GetThrower();
|
||||
// Do a proper explosion
|
||||
Vector velDir = pGrenade->GetAbsVelocity();
|
||||
VectorNormalize( velDir );
|
||||
Vector vecSpot = pGrenade->GetAbsOrigin() - velDir * 32;
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
// Boom
|
||||
pGrenade->Explode( &tr, DMG_BLAST );
|
||||
}
|
||||
else
|
||||
{
|
||||
CTFBaseRocket *pRocket = dynamic_cast<CTFBaseRocket*>(pOther);
|
||||
if ( pRocket )
|
||||
{
|
||||
Vector velDir = pRocket->GetAbsVelocity();
|
||||
VectorNormalize( velDir );
|
||||
Vector vecSpot = pRocket->GetAbsOrigin() - velDir * 32;
|
||||
trace_t tr;
|
||||
UTIL_TraceLine( vecSpot, vecSpot + velDir * 64, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
|
||||
pRocket->Explode( &tr, this );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !pAttacker )
|
||||
{
|
||||
CTFBaseProjectile *pProj = dynamic_cast<CTFBaseProjectile*>(pOther);
|
||||
if ( pProj )
|
||||
{
|
||||
pAttacker = pProj->GetScorer();
|
||||
}
|
||||
}
|
||||
|
||||
TakeDamage( CTakeDamageInfo( pOther, pAttacker, 10.f, DMG_CRUSH ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef GAME_DLL
|
||||
|
||||
void CTFGenericBomb::Detonate( inputdata_t& inputdata )
|
||||
{
|
||||
CTakeDamageInfo info;
|
||||
Event_Killed( info );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFGenericBomb::Event_Killed( const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( m_bDead )
|
||||
return;
|
||||
|
||||
m_bDead = true;
|
||||
|
||||
trace_t tr;
|
||||
Vector vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );
|
||||
UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, &tr );
|
||||
|
||||
// Explosion effect and gibs.
|
||||
Vector vecOrigin = GetAbsOrigin();
|
||||
QAngle vecAngles = GetAbsAngles();
|
||||
int iAttachment = LookupAttachment( "alt-origin" );
|
||||
if ( iAttachment > 0 )
|
||||
{
|
||||
GetAttachment( iAttachment, vecOrigin, vecAngles );
|
||||
}
|
||||
CPVSFilter pvsFilter( vecOrigin );
|
||||
if ( STRING( m_strExplodeParticleName ) && STRING( m_strExplodeParticleName )[0] )
|
||||
{
|
||||
TE_TFParticleEffect( pvsFilter, 0.0f, STRING( m_strExplodeParticleName ), vecOrigin, vecAngles );
|
||||
}
|
||||
|
||||
if ( STRING( m_strExplodeSoundName ) && STRING( m_strExplodeSoundName )[0] )
|
||||
{
|
||||
EmitSound( STRING( m_strExplodeSoundName ) );
|
||||
}
|
||||
|
||||
// Get the owner out of the attacker in case of arrows hitting the bomb.
|
||||
CBaseEntity* pAttacker = info.GetAttacker();
|
||||
if ( pAttacker && pAttacker->GetOwnerEntity() )
|
||||
{
|
||||
pAttacker = pAttacker->GetOwnerEntity();
|
||||
}
|
||||
|
||||
// Deal damage.
|
||||
SetSolid( SOLID_NONE );
|
||||
if ( pAttacker )
|
||||
{
|
||||
ChangeTeam( pAttacker->GetTeamNumber() );
|
||||
}
|
||||
|
||||
CTakeDamageInfo damage_info( this, pAttacker, NULL, m_flDamage, DMG_BLAST | DMG_HALF_FALLOFF | DMG_NOCLOSEDISTANCEMOD );
|
||||
damage_info.SetDamageCustom( TF_DMG_CUSTOM_NONE );
|
||||
|
||||
damage_info.SetForceFriendlyFire( m_eWhoToDamage == DAMAGE_EVERYONE );
|
||||
|
||||
if ( TFGameRules() )
|
||||
{
|
||||
CTFRadiusDamageInfo radiusinfo( &damage_info, vecOrigin, m_flRadius, this );
|
||||
TFGameRules()->RadiusDamage( radiusinfo );
|
||||
}
|
||||
|
||||
// Don't decal players with scorch.
|
||||
if ( tr.m_pEnt && !tr.m_pEnt->IsPlayer() )
|
||||
{
|
||||
UTIL_DecalTrace( &tr, "Scorch" );
|
||||
}
|
||||
|
||||
// Spawns gibs on the client
|
||||
UserMessageBegin( pvsFilter, "BreakModel" );
|
||||
WRITE_SHORT( GetModelIndex() );
|
||||
WRITE_VEC3COORD( vecOrigin );
|
||||
WRITE_ANGLES( vecAngles );
|
||||
WRITE_SHORT( m_nSkin );
|
||||
MessageEnd();
|
||||
|
||||
m_OnDetonate.FireOutput( this, this );
|
||||
|
||||
BaseClass::Event_Killed( info );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// TF Generic Bomb
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef TF_GENERIC_BOMB_H
|
||||
#define TF_GENERIC_BOMB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFGenericBomb C_TFGenericBomb
|
||||
#endif
|
||||
|
||||
class CTFGenericBombShim : public CBaseAnimating
|
||||
{
|
||||
virtual void GenericTouch( CBaseEntity *pOther ) = 0;
|
||||
public:
|
||||
void Touch( CBaseEntity *pOther ) { return GenericTouch( pOther ) ; }
|
||||
};
|
||||
|
||||
DECLARE_AUTO_LIST( ITFGenericBomb );
|
||||
|
||||
class CTFGenericBomb : public CTFGenericBombShim, public ITFGenericBomb
|
||||
{
|
||||
DECLARE_CLASS( CTFGenericBomb, CBaseAnimating );
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
enum EWhoToDamage
|
||||
{
|
||||
DAMAGE_ATTACKER_AND_ATTACKER_ENEMIES,
|
||||
DAMAGE_EVERYONE
|
||||
};
|
||||
|
||||
public:
|
||||
CTFGenericBomb();
|
||||
~CTFGenericBomb() {}
|
||||
|
||||
virtual void Precache( void );
|
||||
virtual void Spawn( void );
|
||||
virtual void GenericTouch( CBaseEntity *pOther ) OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef GAME_DLL
|
||||
void Detonate( inputdata_t& inputdata );
|
||||
COutputEvent m_OnDetonate;
|
||||
#endif
|
||||
|
||||
bool m_bDead;
|
||||
bool m_bPrecached;
|
||||
|
||||
int m_iTeam;
|
||||
float m_flDamage;
|
||||
int m_nHealth;
|
||||
float m_flRadius;
|
||||
string_t m_strExplodeParticleName;
|
||||
string_t m_strHitParticleName;
|
||||
string_t m_strExplodeSoundName;
|
||||
EWhoToDamage m_eWhoToDamage;
|
||||
};
|
||||
|
||||
#endif //TF_GENERIC_BOMB_H
|
||||
@@ -0,0 +1,229 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF AmmoPack.
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_halloween_souls_pickup.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "tf_shareddefs.h"
|
||||
#else
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_player.h"
|
||||
#include "particle_parse.h"
|
||||
#endif
|
||||
|
||||
#define TF_SOULS_TIMEOUT 10.0f // How long before dropped souls disappear
|
||||
|
||||
LINK_ENTITY_TO_CLASS( halloween_souls_pack, CHalloweenSoulPack );
|
||||
PRECACHE_REGISTER( halloween_souls_pack );
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( HalloweenSoulPack, DT_HalloweenSoulPack )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CHalloweenSoulPack, DT_HalloweenSoulPack )
|
||||
#ifdef GAME_DLL
|
||||
SendPropEHandle( SENDINFO( m_hTarget ) ),
|
||||
SendPropVector( SENDINFO( m_vecPreCurvePos ) ),
|
||||
SendPropVector( SENDINFO( m_vecStartCurvePos ) ),
|
||||
SendPropFloat( SENDINFO( m_flDuration ) ),
|
||||
#else
|
||||
RecvPropEHandle( RECVINFO( m_hTarget) ),
|
||||
RecvPropVector( RECVINFO( m_vecPreCurvePos) ),
|
||||
RecvPropVector( RECVINFO( m_vecStartCurvePos) ),
|
||||
RecvPropFloat( RECVINFO( m_flDuration ) ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
BEGIN_PREDICTION_DATA( CHalloweenSoulPack )
|
||||
END_PREDICTION_DATA()
|
||||
|
||||
#define SOUND_PLAYER_COLLECT_SOULS "Player.ReceiveSouls"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CHalloweenSoulPack::CHalloweenSoulPack()
|
||||
: m_flCreationTime( 0.f )
|
||||
#ifdef GAME_DLL
|
||||
, m_nAmount( 1 )
|
||||
#endif
|
||||
{
|
||||
m_hTarget = NULL;
|
||||
m_flDuration = 2.f;
|
||||
}
|
||||
|
||||
|
||||
CHalloweenSoulPack::~CHalloweenSoulPack()
|
||||
{}
|
||||
|
||||
|
||||
void CHalloweenSoulPack::Spawn()
|
||||
{
|
||||
BaseClass::Spawn();
|
||||
|
||||
SetMoveType( MOVETYPE_NOCLIP, MOVECOLLIDE_DEFAULT );
|
||||
SetSolid( SOLID_BBOX );
|
||||
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
|
||||
SetSolidFlags( FSOLID_TRIGGER );
|
||||
#ifdef GAME_DLL
|
||||
InitSplineData();
|
||||
SetTouch(&CHalloweenSoulPack::ItemTouch);
|
||||
#else
|
||||
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
||||
#endif
|
||||
}
|
||||
|
||||
void CHalloweenSoulPack::Precache()
|
||||
{
|
||||
PrecacheScriptSound( SOUND_PLAYER_COLLECT_SOULS );
|
||||
}
|
||||
|
||||
void CHalloweenSoulPack::FlyThink( void )
|
||||
{
|
||||
FlyTowardsTargetEntity();
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
void CHalloweenSoulPack::OnDataChanged( DataUpdateType_t type )
|
||||
{
|
||||
BaseClass::OnDataChanged( type );
|
||||
|
||||
if ( type == DATA_UPDATE_CREATED )
|
||||
{
|
||||
switch( GetTeamNumber() )
|
||||
{
|
||||
case TF_TEAM_RED:
|
||||
ParticleProp()->Create( "halloween_pickup_active_red", PATTACH_ABSORIGIN_FOLLOW );
|
||||
break;
|
||||
case TF_TEAM_BLUE:
|
||||
ParticleProp()->Create( "halloween_pickup_active", PATTACH_ABSORIGIN_FOLLOW );
|
||||
break;
|
||||
case TEAM_SPECTATOR:
|
||||
ParticleProp()->Create( "halloween_pickup_active_green", PATTACH_ABSORIGIN_FOLLOW );
|
||||
break;
|
||||
default:
|
||||
Assert( false );
|
||||
}
|
||||
ParticleProp()->Create( "eb_beam_angry_ring01", PATTACH_ABSORIGIN_FOLLOW );
|
||||
ParticleProp()->Create( "soul_trail", PATTACH_ABSORIGIN_FOLLOW );
|
||||
InitSplineData();
|
||||
}
|
||||
}
|
||||
|
||||
void CHalloweenSoulPack::ClientThink()
|
||||
{
|
||||
FlyTowardsTargetEntity();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
int CHalloweenSoulPack::UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_ALWAYS );
|
||||
}
|
||||
|
||||
|
||||
void CHalloweenSoulPack::ItemTouch( CBaseEntity *pOther )
|
||||
{
|
||||
// Only allow our target to pick us up, if we have one
|
||||
if ( pOther != m_hTarget && m_hTarget != NULL )
|
||||
return;
|
||||
|
||||
// Only allow to be picked up when done travelling
|
||||
float flT = ( gpGlobals->curtime - m_flCreationTime ) / m_flDuration;
|
||||
if ( flT < 1.f )
|
||||
return;
|
||||
|
||||
CTFPlayer * pPlayer = ToTFPlayer( pOther );
|
||||
if ( pPlayer )
|
||||
{
|
||||
CTFPlayer *pTargetPlayer = ToTFPlayer( m_hTarget );
|
||||
IGameEvent *pEvent = gameeventmanager->CreateEvent( "halloween_soul_collected" );
|
||||
if ( pEvent )
|
||||
{
|
||||
pEvent->SetInt( "intended_target", pTargetPlayer ? pTargetPlayer->GetUserID() : -1 );
|
||||
pEvent->SetInt( "collecting_player", pPlayer->GetUserID() );
|
||||
pEvent->SetInt( "soul_count", m_nAmount );
|
||||
gameeventmanager->FireEvent( pEvent, true );
|
||||
}
|
||||
|
||||
// Strange Tracking
|
||||
static CSchemaItemDefHandle hItemDef( "Activated Halloween Pass");
|
||||
kill_eater_event_t eEventType = kKillEaterEvent_HalloweenSouls;
|
||||
EconEntity_NonEquippedItemKillTracking_NoPartnerBatched( pPlayer, hItemDef->GetDefinitionIndex(), eEventType, m_nAmount );
|
||||
|
||||
// Play a spooky sound in their ears
|
||||
CSingleUserRecipientFilter filter( pPlayer );
|
||||
EmitSound_t params;
|
||||
params.m_nChannel = CHAN_STATIC;
|
||||
params.m_pSoundName = SOUND_PLAYER_COLLECT_SOULS;
|
||||
EmitSound( filter, pPlayer->entindex(), params );
|
||||
}
|
||||
|
||||
if ( pOther == m_hTarget || ( !m_hTarget && pOther->IsPlayer() ) )
|
||||
{
|
||||
UTIL_Remove( this );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CHalloweenSoulPack::FlyTowardsTargetEntity( void )
|
||||
{
|
||||
CBaseEntity* pEntity = m_hTarget.Get();
|
||||
float flT = ( gpGlobals->curtime - m_flCreationTime ) / m_flDuration;
|
||||
#ifdef CLIENT_DLL
|
||||
// Client doesn't need to do anything if there's no target
|
||||
if ( !pEntity )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
bool bIsAGhost = false;
|
||||
if ( pEntity && pEntity->IsPlayer() )
|
||||
{
|
||||
CTFPlayer* pTFPlayerTarget = assert_cast< CTFPlayer* >( pEntity );
|
||||
bIsAGhost = pTFPlayerTarget->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE );
|
||||
}
|
||||
|
||||
// If flT > 2.f something has gone terribly wrong, just give up.
|
||||
// Also give up if our entity is gone, dead or now a ghost
|
||||
if( flT > 2.f || pEntity == NULL || !pEntity->IsAlive() || bIsAGhost )
|
||||
{
|
||||
m_hTarget = NULL;
|
||||
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
|
||||
SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + TF_SOULS_TIMEOUT, "RemoveThink" );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Clamp
|
||||
const float flBiasAmt = 0.2f;
|
||||
flT = clamp( Bias( flT, flBiasAmt ), 0.f, 1.f );
|
||||
|
||||
// We want to fly through the front of their chest so they get a good show
|
||||
QAngle eyeAngles = pEntity->EyeAngles();
|
||||
Vector vecBehindChest;
|
||||
AngleVectors( eyeAngles, &vecBehindChest );
|
||||
vecBehindChest *= -2000;
|
||||
|
||||
Vector vecNextCuvePos = pEntity->WorldSpaceCenter() + vecBehindChest;
|
||||
Vector vecOutput;
|
||||
Catmull_Rom_Spline( m_vecPreCurvePos, m_vecStartCurvePos, pEntity->WorldSpaceCenter(), vecNextCuvePos, flT, vecOutput );
|
||||
|
||||
SetAbsOrigin( vecOutput );
|
||||
|
||||
SetContextThink( &CHalloweenSoulPack::FlyThink, gpGlobals->curtime, "HalloweenSoulPackThink" );
|
||||
}
|
||||
|
||||
void CHalloweenSoulPack::InitSplineData( void )
|
||||
{
|
||||
m_flCreationTime = gpGlobals->curtime;
|
||||
#ifdef GAME_DLL
|
||||
m_vecStartCurvePos = GetAbsOrigin();
|
||||
m_vecPreCurvePos = m_vecStartCurvePos + RandomVector( -2000, 2000 );
|
||||
m_vecPreCurvePos.SetZ( Min( m_vecPreCurvePos.GetZ(), 0.f ) );
|
||||
m_flDuration = RandomFloat( 1.f, 3.f );
|
||||
#endif
|
||||
SetContextThink( &CHalloweenSoulPack::FlyThink, gpGlobals->curtime, "HalloweenSoulPackThink" );
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: CTF AmmoPack.
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef TF_HALLOWEEN_SOULS_PICUP_H
|
||||
#define TF_HALLOWEEN_SOULS_PICUP_H
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CHalloweenSoulPack C_HalloweenSoulPack
|
||||
#endif
|
||||
|
||||
|
||||
class CHalloweenSoulPack : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CHalloweenSoulPack, CBaseEntity )
|
||||
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_PREDICTABLE();
|
||||
|
||||
CHalloweenSoulPack();
|
||||
~CHalloweenSoulPack();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Precache() OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void SetAmount( int nAmount ) { m_nAmount = nAmount; }
|
||||
void SetFlyDuration( float flDuration ) { m_flDuration = flDuration; }
|
||||
void SetTarget( CBaseEntity *pTarget ) { m_hTarget = pTarget; }
|
||||
void ItemTouch( CBaseEntity *pOther );
|
||||
virtual int UpdateTransmitState() OVERRIDE;
|
||||
#else
|
||||
virtual void OnDataChanged( DataUpdateType_t type ) OVERRIDE;
|
||||
virtual void ClientThink() OVERRIDE;
|
||||
#endif
|
||||
private:
|
||||
void FlyThink( void );
|
||||
void FlyTowardsTargetEntity( void );
|
||||
void InitSplineData( void );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
int m_nAmount;
|
||||
const char *m_pszParticleName;
|
||||
#endif
|
||||
CNetworkHandle( CBaseEntity, m_hTarget );
|
||||
float m_flCreationTime;
|
||||
|
||||
CNetworkVector( m_vecPreCurvePos );
|
||||
CNetworkVector( m_vecStartCurvePos );
|
||||
CNetworkVar( float, m_flDuration );
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_HALLOWEEN_SOULS_PICUP_H
|
||||
@@ -0,0 +1,110 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "cbase.h"
|
||||
#include "tf_item.h"
|
||||
#include "tf_shareddefs.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_tf_player.h"
|
||||
|
||||
// NVNT haptics system interface
|
||||
#include "haptics/ihaptics.h"
|
||||
#else
|
||||
#include "tf_player.h"
|
||||
#endif
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFItem, DT_TFItem )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTFItem, DT_TFItem )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Identifier.
|
||||
//-----------------------------------------------------------------------------
|
||||
unsigned int CTFItem::GetItemID( void ) const
|
||||
{
|
||||
return TF_ITEM_UNDEFINED;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFItem::PickUp( CTFPlayer *pPlayer, bool bInvisible )
|
||||
{
|
||||
// SetParent with attachment point - look it up later if need be!
|
||||
SetOwnerEntity( pPlayer );
|
||||
SetParent( pPlayer );
|
||||
SetLocalOrigin( vec3_origin );
|
||||
SetLocalAngles( vec3_angle );
|
||||
|
||||
// Make invisible?
|
||||
if ( bInvisible )
|
||||
{
|
||||
AddEffects( EF_NODRAW );
|
||||
}
|
||||
|
||||
// Add the item to the player's item inventory.
|
||||
pPlayer->SetItem( this );
|
||||
// NVNT if this is the client dll and the owner is the local
|
||||
// player notify the haptics system.
|
||||
#ifdef CLIENT_DLL
|
||||
if(pPlayer->IsLocalPlayer())
|
||||
haptics->ProcessHapticEvent(2,"Game","ctf_item_start");
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFItem::Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown /*= false*/, bool bMessage /*= true*/ )
|
||||
{
|
||||
// Remove the item from the player's item inventory.
|
||||
pPlayer->SetItem( NULL );
|
||||
|
||||
// Make visible?
|
||||
if ( bVisible )
|
||||
{
|
||||
RemoveEffects( EF_NODRAW );
|
||||
}
|
||||
// NVNT if this is the client dll and the owner is the local
|
||||
// player notify the haptics system we are dropping this item.
|
||||
#ifdef CLIENT_DLL
|
||||
if(pPlayer->IsLocalPlayer())
|
||||
haptics->ProcessHapticEvent(2,"Game","ctf_item_stop");
|
||||
#endif
|
||||
|
||||
// Clear the parent.
|
||||
SetParent( NULL );
|
||||
SetOwnerEntity( NULL );
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFItem::ShouldDraw()
|
||||
{
|
||||
// If I'm carrying the flag in 1st person, don't draw it
|
||||
if ( ToTFPlayer(GetMoveParent())->InFirstPersonView() )
|
||||
return false;
|
||||
|
||||
return BaseClass::ShouldDraw();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Should this object cast shadows?
|
||||
//-----------------------------------------------------------------------------
|
||||
ShadowType_t CTFItem::ShadowCastType()
|
||||
{
|
||||
if ( ToTFPlayer(GetMoveParent())->ShouldDrawThisPlayer() )
|
||||
{
|
||||
// Using the viewmodel.
|
||||
return SHADOWS_NONE;
|
||||
}
|
||||
|
||||
return BaseClass::ShadowCastType();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
//
|
||||
//=============================================================================//
|
||||
#ifndef TF_ITEM_H
|
||||
#define TF_ITEM_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_props.h"
|
||||
#else
|
||||
#include "props.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFPlayer C_TFPlayer
|
||||
#define CTFItem C_TFItem
|
||||
#endif
|
||||
|
||||
class CTFPlayer;
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// TF Item
|
||||
//
|
||||
class CTFItem : public CDynamicProp
|
||||
{
|
||||
public:
|
||||
DECLARE_CLASS( CTFItem,CDynamicProp )
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
// Unique identifier.
|
||||
virtual unsigned int GetItemID() const;
|
||||
|
||||
// Pick up and drop.
|
||||
virtual void PickUp( CTFPlayer *pPlayer, bool bInvisible );
|
||||
virtual void Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown = false, bool bMessage = true );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool ShouldDraw();
|
||||
virtual ShadowType_t ShadowCastType();
|
||||
virtual bool ShouldHideGlowEffect( void ) { return false; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // TF_ITEM_H
|
||||
@@ -0,0 +1,204 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TFITEMCONSTANTS_H // ECON_ITEM_CONSTANTS_H is used by src/common/econ_item_view.h
|
||||
#define TFITEMCONSTANTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weapon Types
|
||||
//-----------------------------------------------------------------------------
|
||||
enum
|
||||
{
|
||||
TF_WPN_TYPE_PRIMARY = 0,
|
||||
TF_WPN_TYPE_SECONDARY,
|
||||
TF_WPN_TYPE_MELEE,
|
||||
TF_WPN_TYPE_GRENADE,
|
||||
TF_WPN_TYPE_BUILDING,
|
||||
TF_WPN_TYPE_PDA,
|
||||
TF_WPN_TYPE_ITEM1,
|
||||
TF_WPN_TYPE_ITEM2,
|
||||
TF_WPN_TYPE_HEAD,
|
||||
TF_WPN_TYPE_MISC,
|
||||
TF_WPN_TYPE_MELEE_ALLCLASS,
|
||||
TF_WPN_TYPE_SECONDARY2,
|
||||
TF_WPN_TYPE_PRIMARY2,
|
||||
|
||||
|
||||
//
|
||||
// ADD NEW ITEMS HERE TO AVOID BREAKING DEMOS
|
||||
//
|
||||
|
||||
TF_WPN_TYPE_COUNT,
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Slots for items within loadouts
|
||||
//-----------------------------------------------------------------------------
|
||||
enum loadout_positions_t
|
||||
{
|
||||
LOADOUT_POSITION_INVALID = -1,
|
||||
|
||||
// Weapons & Equipment
|
||||
LOADOUT_POSITION_PRIMARY = 0,
|
||||
LOADOUT_POSITION_SECONDARY,
|
||||
LOADOUT_POSITION_MELEE,
|
||||
LOADOUT_POSITION_UTILITY,
|
||||
LOADOUT_POSITION_BUILDING,
|
||||
LOADOUT_POSITION_PDA,
|
||||
LOADOUT_POSITION_PDA2,
|
||||
|
||||
// Wearables. If you add new wearable slots, make sure you add them to IsWearableSlot() below this.
|
||||
LOADOUT_POSITION_HEAD,
|
||||
LOADOUT_POSITION_MISC,
|
||||
|
||||
// other
|
||||
LOADOUT_POSITION_ACTION,
|
||||
|
||||
// More wearables, yay!
|
||||
LOADOUT_POSITION_MISC2,
|
||||
|
||||
// taunts
|
||||
LOADOUT_POSITION_TAUNT,
|
||||
LOADOUT_POSITION_TAUNT2,
|
||||
LOADOUT_POSITION_TAUNT3,
|
||||
LOADOUT_POSITION_TAUNT4,
|
||||
LOADOUT_POSITION_TAUNT5,
|
||||
LOADOUT_POSITION_TAUNT6,
|
||||
LOADOUT_POSITION_TAUNT7,
|
||||
LOADOUT_POSITION_TAUNT8,
|
||||
|
||||
#ifdef STAGING_ONLY
|
||||
// Extra PDA mod slots
|
||||
LOADOUT_POSITION_PDA_ADDON1,
|
||||
LOADOUT_POSITION_PDA_ADDON2,
|
||||
|
||||
LOADOUT_POSITION_PDA3,
|
||||
//LOADOUT_POSITION_MISC3,
|
||||
//LOADOUT_POSITION_MISC4,
|
||||
//LOADOUT_POSITION_MISC5,
|
||||
//LOADOUT_POSITION_MISC6,
|
||||
//LOADOUT_POSITION_MISC7,
|
||||
//LOADOUT_POSITION_MISC8,
|
||||
//LOADOUT_POSITION_MISC9,
|
||||
//LOADOUT_POSITION_MISC10,
|
||||
LOADOUT_POSITION_BUILDING2,
|
||||
#endif // STAGING_ONLY
|
||||
|
||||
CLASS_LOADOUT_POSITION_COUNT,
|
||||
};
|
||||
|
||||
enum account_loadout_positions_t
|
||||
{
|
||||
ACCOUNT_LOADOUT_POSITION_ACCOUNT1,
|
||||
ACCOUNT_LOADOUT_POSITION_ACCOUNT2,
|
||||
ACCOUNT_LOADOUT_POSITION_ACCOUNT3,
|
||||
|
||||
ACCOUNT_LOADOUT_POSITION_COUNT,
|
||||
};
|
||||
|
||||
// We use this to determine the maximum number of wearable instances we'll send from the server down to
|
||||
// connected clients. This was previously hardcoded to be 8 and because of the way RecvPropUtlVector works
|
||||
// we can't easily change this without doing some kludgy work and breaking network/demo compatibility. In
|
||||
// the shorter term, we'll break compatibility in staging where no-one cares but leave public unchanged.
|
||||
#ifdef STAGING_ONLY
|
||||
#define LOADOUT_MAX_WEARABLES_COUNT ( CLASS_LOADOUT_POSITION_COUNT )
|
||||
#else
|
||||
#define LOADOUT_MAX_WEARABLES_COUNT ( 8 /* !!! -- LOADOUT_POSITION_COUNT - 3 */ )
|
||||
#endif
|
||||
|
||||
inline bool IsMiscSlot( int iSlot )
|
||||
{
|
||||
return iSlot == LOADOUT_POSITION_MISC
|
||||
|| iSlot == LOADOUT_POSITION_MISC2
|
||||
|| iSlot == LOADOUT_POSITION_HEAD
|
||||
#ifdef STAGING_ONLY
|
||||
//|| iSlot == LOADOUT_POSITION_MISC3
|
||||
//|| iSlot == LOADOUT_POSITION_MISC4
|
||||
//|| iSlot == LOADOUT_POSITION_MISC5
|
||||
//|| iSlot == LOADOUT_POSITION_MISC6
|
||||
//|| iSlot == LOADOUT_POSITION_MISC7
|
||||
//|| iSlot == LOADOUT_POSITION_MISC8
|
||||
//|| iSlot == LOADOUT_POSITION_MISC9
|
||||
//|| iSlot == LOADOUT_POSITION_MISC10
|
||||
#endif // STAGING_ONLY
|
||||
;
|
||||
}
|
||||
|
||||
inline bool IsBuildingSlot( int iSlot )
|
||||
{
|
||||
return iSlot == LOADOUT_POSITION_BUILDING
|
||||
#ifdef STAGING_ONLY
|
||||
|| iSlot == LOADOUT_POSITION_BUILDING2
|
||||
#endif // STAGING_ONLY
|
||||
;
|
||||
}
|
||||
|
||||
inline bool IsTauntSlot( int iSlot )
|
||||
{
|
||||
return iSlot == LOADOUT_POSITION_TAUNT
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT2
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT3
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT4
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT5
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT6
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT7
|
||||
|| iSlot == LOADOUT_POSITION_TAUNT8;
|
||||
}
|
||||
|
||||
inline bool IsWearableSlot( int iSlot )
|
||||
{
|
||||
return iSlot == LOADOUT_POSITION_HEAD
|
||||
|| iSlot == LOADOUT_POSITION_MISC
|
||||
|| iSlot == LOADOUT_POSITION_ACTION
|
||||
|| IsMiscSlot( iSlot )
|
||||
|| IsTauntSlot( iSlot );
|
||||
}
|
||||
|
||||
inline bool IsQuestSlot( int iSlot )
|
||||
{
|
||||
return iSlot == ACCOUNT_LOADOUT_POSITION_ACCOUNT1
|
||||
|| iSlot == ACCOUNT_LOADOUT_POSITION_ACCOUNT2
|
||||
|| iSlot == ACCOUNT_LOADOUT_POSITION_ACCOUNT3;
|
||||
}
|
||||
|
||||
inline bool IsValidItemSlot( int iSlot )
|
||||
{
|
||||
return iSlot > LOADOUT_POSITION_INVALID && iSlot < CLASS_LOADOUT_POSITION_COUNT;
|
||||
}
|
||||
|
||||
inline bool IsValidPickupWeaponSlot( int iSlot )
|
||||
{
|
||||
return iSlot == LOADOUT_POSITION_PRIMARY
|
||||
|| iSlot == LOADOUT_POSITION_SECONDARY
|
||||
|| iSlot == LOADOUT_POSITION_MELEE;
|
||||
}
|
||||
|
||||
|
||||
// The total number of loadouts to track for each player.
|
||||
// Right now, hardcoded to match TF's 10 classes.
|
||||
#define LOADOUT_COUNT (10+1) // 0th class is undefined
|
||||
|
||||
// Halloween! (Shared by GC and game client.)
|
||||
enum EHalloweenMap
|
||||
{
|
||||
kHalloweenMap_MannManor,
|
||||
kHalloweenMap_Viaduct,
|
||||
kHalloweenMap_Lakeside,
|
||||
kHalloweenMap_Hightower,
|
||||
|
||||
kHalloweenMapCount
|
||||
};
|
||||
|
||||
enum EHalloweenGiftSpawnMetaInfo
|
||||
{
|
||||
kHalloweenGiftMeta_IsUnderworldOnViaduct_DEPRECATED = 0x01,
|
||||
};
|
||||
|
||||
#endif // TFITEMCONSTANTS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Container that allows client & server access to data in player inventories & loadouts
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_ITEM_INVENTORY_H
|
||||
#define TF_ITEM_INVENTORY_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "econ_item_inventory.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "econ_item_constants.h"
|
||||
#include "tf_item_constants.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "econ_notifications.h"
|
||||
#endif
|
||||
|
||||
#define LOADOUT_SLOT_USE_BASE_ITEM 0
|
||||
|
||||
namespace vgui
|
||||
{
|
||||
class Panel;
|
||||
}
|
||||
|
||||
struct baseitemcriteria_t;
|
||||
|
||||
//===============================================================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A single TF player's inventory.
|
||||
// On the client, the inventory manager contains an instance of this for the local player.
|
||||
// On the server, each player contains an instance of this.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFPlayerInventory : public CPlayerInventory
|
||||
{
|
||||
DECLARE_CLASS( CTFPlayerInventory, CPlayerInventory );
|
||||
public:
|
||||
CTFPlayerInventory();
|
||||
virtual ~CTFPlayerInventory();
|
||||
|
||||
virtual CEconItemView *GetItemInLoadout( int iClass, int iSlot );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// Removes any item in a loadout slot. If the slot has a base item,
|
||||
// the player essentially returns to using that item.
|
||||
// NOTE: This can fail if the player has no backpack space to contain the equipped item.
|
||||
bool ClearLoadoutSlot( int iClass, int iSlot );
|
||||
CEconItemView *GetCacheServerItemInLoadout( int iClass, int iSlot );
|
||||
|
||||
void UpdateWeaponSkinRequest();
|
||||
#endif
|
||||
|
||||
virtual int GetMaxItemCount( void ) const;
|
||||
virtual bool CanPurchaseItems( int iItemCount ) const;
|
||||
virtual int GetPreviewItemDef( void ) const;
|
||||
|
||||
// Derived inventory hooks
|
||||
virtual void ItemHasBeenUpdated( CEconItemView *pItem, bool bUpdateAckFile, bool bWriteAckFile ) OVERRIDE;
|
||||
virtual void ItemIsBeingRemoved( CEconItemView *pItem );
|
||||
bool UpdateEquipStateForClass( const itemid_t& itemID, equipped_slot_t nSlot, itemid_t *pLoadout, int nCount );
|
||||
|
||||
// Debugging
|
||||
virtual void DumpInventoryToConsole( bool bRoot );
|
||||
|
||||
bool ClassLoadoutHasChanged( int iClass ) { return m_bLoadoutChanged[iClass]; }
|
||||
void ClearClassLoadoutChangeTracking( void );
|
||||
|
||||
virtual void NotifyHasNewItems() { OnHasNewItems(); }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual ITexture *GetWeaponSkinBaseLowRes( itemid_t nItemId, int iTeam ) const;
|
||||
#endif
|
||||
|
||||
void OnHasNewQuest();
|
||||
|
||||
static CEconItemView *GetFirstItemOfItemDef( item_definition_index_t nDefIndex, CPlayerInventory* pInventory = NULL );
|
||||
|
||||
protected:
|
||||
virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
|
||||
#ifdef CLIENT_DLL
|
||||
// Converts an old format inventory to the new format.
|
||||
void ConvertOldFormatInventoryToNew( void );
|
||||
|
||||
virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
|
||||
virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
|
||||
|
||||
virtual bool AddEconItem( CEconItem * pItem, bool bUpdateAckFile, bool bWriteAckFile, bool bCheckForNewItems ) OVERRIDE;
|
||||
|
||||
void VerifyChangedLoadoutsAreValid();
|
||||
void VerifyLoadoutItemsAreValid( int iClass );
|
||||
#endif
|
||||
|
||||
virtual void OnHasNewItems();
|
||||
virtual void ValidateInventoryPositions( void );
|
||||
|
||||
// Extracts the position that should be used to sort items in the inventory from the backend position.
|
||||
// Necessary if your inventory packs a bunch of info into the position instead of using it just as a position.
|
||||
virtual int ExtractInventorySortPosition( uint32 iBackendPosition )
|
||||
{
|
||||
// Consider unack'd items as -1, so they get stacked up before the 0th slot item
|
||||
if ( IsUnacknowledged(iBackendPosition) )
|
||||
return -1;
|
||||
return ExtractBackpackPositionFromBackend(iBackendPosition);
|
||||
}
|
||||
|
||||
virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
private:
|
||||
void CheckSaxtonMaskAchievement( const CEconItem *pEconItem );
|
||||
void UpdateCachedServerLoadoutItems();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Global indices of the items in our inventory in the loadout slots
|
||||
#ifdef CLIENT_DLL
|
||||
struct SkinRequest_t
|
||||
{
|
||||
int m_nTeam;
|
||||
itemid_t m_nID;
|
||||
MDLHandle_t m_hModel;
|
||||
};
|
||||
CUtlVector< SkinRequest_t > m_vecWeaponSkinRequestList;
|
||||
|
||||
|
||||
itemid_t m_CachedServerLoadoutItems[ TF_CLASS_COUNT ][ CLASS_LOADOUT_POSITION_COUNT ];
|
||||
|
||||
CUtlMap< itemid_t, ITexture* > m_CachedBaseTextureLowRes[ TF_TEAM_COUNT ];
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
itemid_t m_LoadoutItems[ TF_CLASS_COUNT ][ CLASS_LOADOUT_POSITION_COUNT ];
|
||||
bool m_bLoadoutChanged[ TF_CLASS_COUNT ];
|
||||
itemid_t m_AccountLoadoutItems[ ACCOUNT_LOADOUT_POSITION_COUNT ];
|
||||
|
||||
|
||||
friend class CTFInventoryManager;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFInventoryManager : public CInventoryManager
|
||||
{
|
||||
DECLARE_CLASS( CTFInventoryManager, CInventoryManager );
|
||||
public:
|
||||
CTFInventoryManager();
|
||||
~CTFInventoryManager();
|
||||
|
||||
virtual void PostInit( void );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual CPlayerInventory *GeneratePlayerInventoryObject() const { return new CTFPlayerInventory; }
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// CLIENT PICKUP UI HANDLING
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
// Get the number of items picked up
|
||||
virtual int GetNumItemPickedUpItems( void );
|
||||
|
||||
// Show the player a pickup screen with any items they've collected recently, if any
|
||||
virtual bool ShowItemsPickedUp( bool bForce = false, bool bReturnToGame = true, bool bNoPanel = false );
|
||||
|
||||
// Show the player a pickup screen with the items they've crafted
|
||||
virtual void ShowItemsCrafted( CUtlVector<itemid_t> *vecCraftedIndices );
|
||||
|
||||
// Force the player to discard an item to make room for a new item, if they have one
|
||||
virtual bool CheckForRoomAndForceDiscard( void );
|
||||
|
||||
// Tells the GC that the player has acknowledged an item and attempts to move it in to the first available BP slot
|
||||
virtual void AcknowledgeItem( CEconItemView *pItem, bool bMoveToBackpack = true );
|
||||
|
||||
// Gets called each frame
|
||||
virtual void Update( float frametime ) OVERRIDE;
|
||||
#endif
|
||||
|
||||
// Returns the item data for the base item in the loadout slot for a given class
|
||||
CEconItemView *GetBaseItemForClass( int iClass, int iSlot );
|
||||
void GenerateBaseItems( void );
|
||||
|
||||
// Gets the specified inventory for the steam ID
|
||||
CTFPlayerInventory *GetInventoryForPlayer( const CSteamID &playerID );
|
||||
|
||||
// Returns the item in the specified loadout slot for a given class
|
||||
CEconItemView *GetItemInLoadoutForClass( int iClass, int iSlot, CSteamID *pID = NULL );
|
||||
|
||||
CEconItemView *GetItemInLoadoutForAccount( int nSlot, CSteamID *pID = NULL );
|
||||
|
||||
// Fills out the vector with the sets that are currently active on the specified player & class
|
||||
void GetActiveSets( CUtlVector<const CEconItemSetDefinition *> *pItemSets, CSteamID steamIDForPlayer, int iClass );
|
||||
|
||||
// We're generating a base item. We need to add the game-specific keys to the criteria so that it'll find the right base item.
|
||||
virtual void AddBaseItemCriteria( baseitemcriteria_t *pCriteria, CItemSelectionCriteria *pSelectionCriteria );
|
||||
|
||||
bool SlotContainsBaseItems( EEquipType_t eType, int iSlot );
|
||||
|
||||
int GetBaseItemCount( ) { return m_pBaseLoadoutItems.Count(); }
|
||||
CEconItemView* GetBaseItem( int iIndex ) { return m_pBaseLoadoutItems[iIndex]; }
|
||||
|
||||
private:
|
||||
// Base items, returned for slots that the player doesn't have anything in
|
||||
CEconItemView *m_pDefaultItem;
|
||||
CUtlVector<CEconItemView*> m_pBaseLoadoutItems;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// On the client, we have a single inventory for the local player. Stored here, instead of in the
|
||||
// local player entity, because players need to access it while not being connected to a server.
|
||||
public:
|
||||
CPlayerInventory *GetLocalInventory( void ) { return &m_LocalInventory; }
|
||||
CTFPlayerInventory *GetLocalTFInventory( void );
|
||||
|
||||
// Try and equip the specified item in the specified class's loadout slot
|
||||
bool EquipItemInLoadout( int iClass, int iSlot, itemid_t iItemID );
|
||||
|
||||
// Fills out pList with all inventory items that could fit into the specified loadout slot for a given class
|
||||
int GetAllUsableItemsForSlot( int iClass, int iSlot, CUtlVector<CEconItemView*> *pList );
|
||||
|
||||
virtual int GetBackpackPositionFromBackend( uint32 iBackendPosition ) { return ExtractBackpackPositionFromBackend(iBackendPosition); }
|
||||
|
||||
// Fills out pList with all quest item in the local inventory
|
||||
int GetAllQuestItems( CUtlVector<CEconItemView*> *pList );
|
||||
|
||||
private:
|
||||
CTFPlayerInventory m_LocalInventory;
|
||||
#endif // CLIENT_DLL
|
||||
};
|
||||
|
||||
CTFInventoryManager *TFInventoryManager( void );
|
||||
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Econ Notifications
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEconNotification_HasNewItems : public CEconNotification
|
||||
{
|
||||
public:
|
||||
CEconNotification_HasNewItems();
|
||||
~CEconNotification_HasNewItems();
|
||||
|
||||
virtual void SetLifetime( float flSeconds )
|
||||
{
|
||||
m_flExpireTime = engine->Time() + flSeconds;
|
||||
}
|
||||
|
||||
virtual float GetExpireTime() const
|
||||
{
|
||||
if ( m_flExpireTime != 0 )
|
||||
return -1.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual float GetInGameLifeTime() const
|
||||
{
|
||||
return m_flExpireTime;
|
||||
}
|
||||
|
||||
virtual void MarkForDeletion()
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
CEconNotification::MarkForDeletion();
|
||||
}
|
||||
|
||||
virtual EType NotificationType() { return eType_Trigger; }
|
||||
virtual void Trigger()
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
TFInventoryManager()->ShowItemsPickedUp( true );
|
||||
MarkForDeletion();
|
||||
}
|
||||
|
||||
virtual bool BShowInGameElements() const
|
||||
{
|
||||
return m_bShowInGame;
|
||||
}
|
||||
|
||||
static bool IsNotificationType( CEconNotification *pNotification ) { return dynamic_cast<CEconNotification_HasNewItems *>( pNotification ) != NULL; }
|
||||
|
||||
protected:
|
||||
|
||||
bool m_bHasTriggered;
|
||||
bool m_bShowInGame;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEconNotification_HasNewItemsOnKill : public CEconNotification_HasNewItems
|
||||
{
|
||||
public:
|
||||
CEconNotification_HasNewItemsOnKill( int iVictimID );
|
||||
|
||||
virtual EType NotificationType() { return eType_Basic; }
|
||||
virtual void Trigger() {}
|
||||
|
||||
static bool HasUnacknowledgedItems();
|
||||
static bool IsNotificationType( CEconNotification *pNotification ) { return dynamic_cast<CEconNotification_HasNewItemsOnKill *>( pNotification ) != NULL; }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#endif // TF_ITEM_INVENTORY_H
|
||||
@@ -0,0 +1,786 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_item_powerup_bottle.h"
|
||||
#include "tf_gamerules.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "tf_weapon_medigun.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#ifndef GAME_DLL
|
||||
extern ConVar cl_hud_minmode;
|
||||
#endif
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_powerup_bottle, CTFPowerupBottle );
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFPowerupBottle, DT_TFPowerupBottle )
|
||||
|
||||
// Network Table --
|
||||
BEGIN_NETWORK_TABLE( CTFPowerupBottle, DT_TFPowerupBottle )
|
||||
#if defined( GAME_DLL )
|
||||
SendPropBool( SENDINFO( m_bActive ) ),
|
||||
SendPropInt( SENDINFO( m_usNumCharges ), -1, SPROP_UNSIGNED ),
|
||||
#else
|
||||
RecvPropBool( RECVINFO( m_bActive ) ),
|
||||
RecvPropInt( RECVINFO( m_usNumCharges ) ),
|
||||
#endif // GAME_DLL
|
||||
END_NETWORK_TABLE()
|
||||
// -- Network Table
|
||||
|
||||
// Data Desc --
|
||||
BEGIN_DATADESC( CTFPowerupBottle )
|
||||
END_DATADESC()
|
||||
// -- Data Desc
|
||||
|
||||
PRECACHE_REGISTER( tf_powerup_bottle );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SHARED CODE
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CTFPowerupBottle::CTFPowerupBottle() : CTFWearable()
|
||||
{
|
||||
m_bActive = false;
|
||||
m_usNumCharges = 0;
|
||||
m_flLastSpawnTime = 0.f;
|
||||
|
||||
#ifdef TF_CLIENT_DLL
|
||||
ListenForGameEvent( "player_spawn" );
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTFPowerupBottle::Precache( void )
|
||||
{
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_generic.mdl" );
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_krit.mdl" );
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_uber.mdl" );
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_tele.mdl" );
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_ammo.mdl" );
|
||||
PrecacheModel( "models/player/items/mvm_loot/all_class/mvm_flask_build.mdl" );
|
||||
|
||||
BaseClass::Precache();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reset the bottle to its initial state
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::Reset( void )
|
||||
{
|
||||
m_bActive = false;
|
||||
SetNumCharges( 0 );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
class CAttributeIterator_ZeroRefundableCurrency : public IEconItemUntypedAttributeIterator
|
||||
{
|
||||
public:
|
||||
CAttributeIterator_ZeroRefundableCurrency( CAttributeList *pAttrList )
|
||||
: m_pAttrList( pAttrList )
|
||||
{
|
||||
Assert( m_pAttrList );
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef )
|
||||
{
|
||||
if ( ::FindAttribute( m_pAttrList, pAttrDef ) )
|
||||
{
|
||||
m_pAttrList->SetRuntimeAttributeRefundableCurrency( pAttrDef, 0 );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CAttributeList *m_pAttrList;
|
||||
};
|
||||
|
||||
CAttributeIterator_ZeroRefundableCurrency it( GetAttributeList() );
|
||||
GetAttributeList()->IterateAttributes( &it );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PowerupBottleType_t CTFPowerupBottle::GetPowerupType( void ) const
|
||||
{
|
||||
int iHasCritBoost = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasCritBoost, critboost );
|
||||
if ( iHasCritBoost )
|
||||
{
|
||||
return POWERUP_BOTTLE_CRITBOOST;
|
||||
}
|
||||
|
||||
int iHasUbercharge = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasUbercharge, ubercharge );
|
||||
if ( iHasUbercharge )
|
||||
{
|
||||
return POWERUP_BOTTLE_UBERCHARGE;
|
||||
}
|
||||
|
||||
int iHasRecall = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasRecall, recall );
|
||||
if ( iHasRecall )
|
||||
{
|
||||
return POWERUP_BOTTLE_RECALL;
|
||||
}
|
||||
|
||||
int iHasRefillAmmo = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasRefillAmmo, refill_ammo );
|
||||
if ( iHasRefillAmmo )
|
||||
{
|
||||
return POWERUP_BOTTLE_REFILL_AMMO;
|
||||
}
|
||||
|
||||
int iHasInstaBuildingUpgrade = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasInstaBuildingUpgrade, building_instant_upgrade );
|
||||
if ( iHasInstaBuildingUpgrade )
|
||||
{
|
||||
return POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE;
|
||||
}
|
||||
|
||||
#ifdef STAGING_ONLY
|
||||
int iSeeCashThroughWall = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iSeeCashThroughWall, mvm_see_cash_through_wall );
|
||||
if ( iSeeCashThroughWall )
|
||||
{
|
||||
return POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return POWERUP_BOTTLE_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::ReapplyProvision( void )
|
||||
{
|
||||
// let the base class do what it needs to do in terms of adding/removing itself from old and new owners
|
||||
BaseClass::ReapplyProvision();
|
||||
|
||||
CBaseEntity *pOwner = GetOwnerEntity();
|
||||
IHasAttributes *pOwnerAttribInterface = GetAttribInterface( pOwner );
|
||||
if ( pOwnerAttribInterface )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
if ( !pOwnerAttribInterface->GetAttributeManager()->IsBeingProvidedToBy( this ) )
|
||||
{
|
||||
GetAttributeManager()->ProvideTo( pOwner );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetAttributeManager()->StopProvidingTo( pOwner );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
bool bBottleShared = false;
|
||||
CTFPlayer *pTFPlayer = dynamic_cast< CTFPlayer* >( pOwner );
|
||||
if ( pTFPlayer )
|
||||
{
|
||||
float flDuration = 0;
|
||||
CALL_ATTRIB_HOOK_FLOAT( flDuration, powerup_duration );
|
||||
|
||||
// Add extra time?
|
||||
CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFPlayer, flDuration, canteen_specialist );
|
||||
|
||||
// This block of code checks if a medic has the ability to
|
||||
// share bottle charges with their heal target
|
||||
int iShareBottle = 0;
|
||||
CWeaponMedigun *pMedigun = NULL;
|
||||
CTFPlayer *pHealTarget = NULL;
|
||||
if ( pTFPlayer->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
pMedigun = dynamic_cast<CWeaponMedigun *>( pTFPlayer->GetActiveWeapon() );
|
||||
if ( pMedigun )
|
||||
{
|
||||
pHealTarget = ToTFPlayer( pMedigun->GetHealTarget() );
|
||||
if ( pHealTarget )
|
||||
{
|
||||
CALL_ATTRIB_HOOK_INT_ON_OTHER( pTFPlayer, iShareBottle, canteen_specialist );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special stuff for conditions
|
||||
int iHasCritBoost = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasCritBoost, critboost );
|
||||
if ( iHasCritBoost != 0 )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
pTFPlayer->m_Shared.AddCond( TF_COND_CRITBOOSTED_USER_BUFF, flDuration );
|
||||
|
||||
if ( iShareBottle && pHealTarget )
|
||||
{
|
||||
pHealTarget->m_Shared.AddCond( TF_COND_CRITBOOSTED_USER_BUFF, flDuration );
|
||||
bBottleShared = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pTFPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED_USER_BUFF, true );
|
||||
}
|
||||
}
|
||||
|
||||
int iHasUbercharge = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasUbercharge, ubercharge );
|
||||
if ( iHasUbercharge )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
pTFPlayer->m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, flDuration );
|
||||
|
||||
// Shield sentries
|
||||
if ( pTFPlayer->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
for ( int i = pTFPlayer->GetObjectCount()-1; i >= 0; i-- )
|
||||
{
|
||||
CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun *>( pTFPlayer->GetObject(i) );
|
||||
if ( pSentry && !pSentry->IsCarried() )
|
||||
{
|
||||
pSentry->SetShieldLevel( SHIELD_MAX, flDuration );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( iShareBottle && pHealTarget )
|
||||
{
|
||||
pHealTarget->m_Shared.AddCond( TF_COND_INVULNERABLE_USER_BUFF, flDuration, pTFPlayer );
|
||||
bBottleShared = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pTFPlayer->m_Shared.RemoveCond( TF_COND_INVULNERABLE_USER_BUFF, true );
|
||||
}
|
||||
}
|
||||
|
||||
int iHasRecall = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasRecall, recall );
|
||||
if ( iHasRecall )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
pTFPlayer->ForceRespawn();
|
||||
pTFPlayer->m_Shared.AddCond( TF_COND_SPEED_BOOST, 7.f );
|
||||
}
|
||||
}
|
||||
|
||||
int iHasRefillAmmo = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasRefillAmmo, refill_ammo );
|
||||
if ( iHasRefillAmmo )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
// Refill weapon clips
|
||||
for ( int i = 0; i < MAX_WEAPONS; i++ )
|
||||
{
|
||||
CBaseCombatWeapon *pWeapon = pTFPlayer->GetWeapon(i);
|
||||
if ( !pWeapon )
|
||||
continue;
|
||||
|
||||
// ACHIEVEMENT_TF_MVM_USE_AMMO_BOTTLE
|
||||
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
if ( ( pWeapon->UsesPrimaryAmmo() && !pWeapon->HasPrimaryAmmo() ) ||
|
||||
( pWeapon->UsesSecondaryAmmo() && !pWeapon->HasSecondaryAmmo() ) )
|
||||
{
|
||||
pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_MVM_USE_AMMO_BOTTLE );
|
||||
}
|
||||
}
|
||||
|
||||
pWeapon->GiveDefaultAmmo();
|
||||
|
||||
if ( iShareBottle && pHealTarget )
|
||||
{
|
||||
CBaseCombatWeapon *pPatientWeapon = pHealTarget->GetWeapon(i);
|
||||
if ( !pPatientWeapon )
|
||||
continue;
|
||||
|
||||
pPatientWeapon->GiveDefaultAmmo();
|
||||
bBottleShared = true;
|
||||
}
|
||||
}
|
||||
|
||||
// And give the player ammo
|
||||
for ( int iAmmo = 0; iAmmo < TF_AMMO_COUNT; ++iAmmo )
|
||||
{
|
||||
pTFPlayer->GiveAmmo( pTFPlayer->GetMaxAmmo(iAmmo), iAmmo, true, kAmmoSource_Resupply );
|
||||
|
||||
if ( iShareBottle && pHealTarget )
|
||||
{
|
||||
pHealTarget->GiveAmmo( pHealTarget->GetMaxAmmo(iAmmo), iAmmo, true, kAmmoSource_Resupply );
|
||||
bBottleShared = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int iHasInstaBuildingUpgrade = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iHasInstaBuildingUpgrade, building_instant_upgrade );
|
||||
if ( iHasInstaBuildingUpgrade )
|
||||
{
|
||||
if ( m_bActive )
|
||||
{
|
||||
for ( int i = pTFPlayer->GetObjectCount()-1; i >= 0; i-- )
|
||||
{
|
||||
CBaseObject *pObj = pTFPlayer->GetObject(i);
|
||||
if ( pObj )
|
||||
{
|
||||
int nMaxLevel = pObj->GetMaxUpgradeLevel();
|
||||
|
||||
// If object is carried, set the target max and move on
|
||||
if ( pObj->IsCarried() )
|
||||
{
|
||||
pObj->SetHighestUpgradeLevel( nMaxLevel );
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're already at max level, heal
|
||||
if ( pObj->GetUpgradeLevel() == nMaxLevel )
|
||||
{
|
||||
pObj->SetHealth( pObj->GetMaxHealth() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
if ( pObj->GetType() == OBJ_SENTRYGUN )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "mvm_quick_sentry_upgrade" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "player", GetOwnerEntity()->entindex() );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pObj->DoQuickBuild( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ACHIEVEMENT_TF_MVM_MEDIC_SHARE_BOTTLES
|
||||
if ( bBottleShared )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "mvm_medic_powerup_shared" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "player", pTFPlayer->entindex() );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Removes the item and deactivates any effect
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::UnEquip( CBasePlayer* pOwner )
|
||||
{
|
||||
BaseClass::UnEquip( pOwner );
|
||||
RemoveEffect();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFPowerupBottle::Use()
|
||||
{
|
||||
if ( !m_bActive && GetNumCharges() > 0 )
|
||||
{
|
||||
if ( !AllowedToUse() )
|
||||
return false;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// Use up one charge worth of refundable money when a charge is used
|
||||
class CAttributeIterator_ConsumeOneRefundableCharge : public IEconItemUntypedAttributeIterator
|
||||
{
|
||||
public:
|
||||
CAttributeIterator_ConsumeOneRefundableCharge( CAttributeList *pAttrList, int iNumCharges )
|
||||
: m_pAttrList( pAttrList )
|
||||
, m_iNumCharges( iNumCharges )
|
||||
{
|
||||
Assert( m_pAttrList );
|
||||
Assert( m_iNumCharges > 0 );
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool OnIterateAttributeValueUntyped( const CEconItemAttributeDefinition *pAttrDef )
|
||||
{
|
||||
if ( ::FindAttribute( m_pAttrList, pAttrDef ) )
|
||||
{
|
||||
int nRefundableCurrency = m_pAttrList->GetRuntimeAttributeRefundableCurrency( pAttrDef );
|
||||
if ( nRefundableCurrency > 0 )
|
||||
{
|
||||
m_pAttrList->SetRuntimeAttributeRefundableCurrency( pAttrDef, nRefundableCurrency - (nRefundableCurrency / m_iNumCharges) );
|
||||
}
|
||||
}
|
||||
|
||||
// Backwards compatibility -- assume any number of attributes.
|
||||
return true;
|
||||
}
|
||||
|
||||
CAttributeList *m_pAttrList;
|
||||
int m_iNumCharges;
|
||||
};
|
||||
|
||||
CAttributeIterator_ConsumeOneRefundableCharge it( GetAttributeList(), GetNumCharges() );
|
||||
GetAttributeList()->IterateAttributes( &it );
|
||||
#endif
|
||||
|
||||
float flDuration = 0;
|
||||
CALL_ATTRIB_HOOK_FLOAT( flDuration, powerup_duration );
|
||||
|
||||
// Add extra time?
|
||||
CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( pOwner )
|
||||
{
|
||||
CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, flDuration, canteen_specialist );
|
||||
}
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "player_used_powerup_bottle" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "player", GetOwnerEntity()->entindex() );
|
||||
event->SetInt( "type", GetPowerupType() );
|
||||
event->SetFloat( "time", flDuration );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
|
||||
#ifdef GAME_DLL
|
||||
if ( pOwner )
|
||||
{
|
||||
EconEntity_OnOwnerKillEaterEventNoPartner( dynamic_cast<CEconEntity *>( this ), pOwner, kKillEaterEvent_PowerupBottlesUsed );
|
||||
|
||||
// we consumed an upgrade - forget it
|
||||
pOwner->ForgetFirstUpgradeForItem( GetAttributeContainer()->GetItem() );
|
||||
}
|
||||
#endif
|
||||
|
||||
SetNumCharges( GetNumCharges() - 1 );
|
||||
m_bActive = true;
|
||||
ReapplyProvision();
|
||||
|
||||
SetContextThink( &CTFPowerupBottle::StatusThink, gpGlobals->curtime + flDuration, "PowerupBottleThink" );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::StatusThink()
|
||||
{
|
||||
RemoveEffect();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::RemoveEffect()
|
||||
{
|
||||
m_bActive = false;
|
||||
ReapplyProvision();
|
||||
SetContextThink( NULL, 0, "PowerupBottleThink" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPowerupBottle::SetNumCharges( uint8 usNumCharges )
|
||||
{
|
||||
static CSchemaAttributeDefHandle pAttrDef_PowerupCharges( "powerup charges" );
|
||||
|
||||
m_usNumCharges = usNumCharges;
|
||||
|
||||
if ( !pAttrDef_PowerupCharges )
|
||||
return;
|
||||
|
||||
CEconItemView *pEconItemView = GetAttributeContainer()->GetItem();
|
||||
if ( !pEconItemView )
|
||||
return;
|
||||
|
||||
pEconItemView->GetAttributeList()->SetRuntimeAttributeValue( pAttrDef_PowerupCharges, float( usNumCharges ) );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
uint8 CTFPowerupBottle::GetNumCharges() const
|
||||
{
|
||||
return m_usNumCharges;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
uint8 CTFPowerupBottle::GetMaxNumCharges() const
|
||||
{
|
||||
int iMaxNumCharges = 0;
|
||||
CALL_ATTRIB_HOOK_INT( iMaxNumCharges, powerup_max_charges );
|
||||
|
||||
// Default canteen has 3 charges. Medic canteen specialist allows purchasing 3 more charges.
|
||||
// If anything else increases max charges, we need to refactor how canteen specialist is handled.
|
||||
Assert( iMaxNumCharges >= 0 && iMaxNumCharges <= 6 );
|
||||
|
||||
iMaxNumCharges = Min( iMaxNumCharges, 6 );
|
||||
|
||||
return (uint8)iMaxNumCharges;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFPowerupBottle::AllowedToUse()
|
||||
{
|
||||
if ( TFGameRules() && !( TFGameRules()->State_Get() == GR_STATE_BETWEEN_RNDS || TFGameRules()->State_Get() == GR_STATE_RND_RUNNING ) )
|
||||
return false;
|
||||
|
||||
CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( !pPlayer )
|
||||
return false;
|
||||
|
||||
if ( pPlayer->IsObserver() || !pPlayer->IsAlive() )
|
||||
return false;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
m_flLastSpawnTime = pPlayer->GetSpawnTime();
|
||||
#endif
|
||||
|
||||
if ( gpGlobals->curtime < m_flLastSpawnTime + 0.7f )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* CTFPowerupBottle::GetEffectLabelText( void )
|
||||
{
|
||||
#ifndef GAME_DLL
|
||||
if ( cl_hud_minmode.GetBool() )
|
||||
{
|
||||
return "#TF_PVE_UsePowerup_MinMode";
|
||||
}
|
||||
#endif
|
||||
|
||||
switch ( GetPowerupType() )
|
||||
{
|
||||
case POWERUP_BOTTLE_CRITBOOST:
|
||||
return "#TF_PVE_UsePowerup_CritBoost";
|
||||
|
||||
case POWERUP_BOTTLE_UBERCHARGE:
|
||||
return "#TF_PVE_UsePowerup_Ubercharge";
|
||||
|
||||
case POWERUP_BOTTLE_RECALL:
|
||||
return "#TF_PVE_UsePowerup_Recall";
|
||||
|
||||
case POWERUP_BOTTLE_REFILL_AMMO:
|
||||
return "#TF_PVE_UsePowerup_RefillAmmo";
|
||||
|
||||
case POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE:
|
||||
return "#TF_PVE_UsePowerup_BuildinginstaUpgrade";
|
||||
|
||||
case POWERUP_BOTTLE_RADIUS_STEALTH:
|
||||
return "#TF_PVE_UsePowerup_RadiusStealth";
|
||||
#ifdef STAGING_ONLY
|
||||
case POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL:
|
||||
return "#TF_PVE_UsePowerup_SeeCashThroughWall";
|
||||
#endif
|
||||
}
|
||||
|
||||
return "#TF_PVE_UsePowerup_CritBoost";
|
||||
}
|
||||
|
||||
const char* CTFPowerupBottle::GetEffectIconName( void )
|
||||
{
|
||||
switch ( GetPowerupType() )
|
||||
{
|
||||
case POWERUP_BOTTLE_CRITBOOST:
|
||||
return "../hud/ico_powerup_critboost_red";
|
||||
|
||||
case POWERUP_BOTTLE_UBERCHARGE:
|
||||
return "../hud/ico_powerup_ubercharge_red";
|
||||
|
||||
case POWERUP_BOTTLE_RECALL:
|
||||
return "../hud/ico_powerup_recall_red";
|
||||
|
||||
case POWERUP_BOTTLE_REFILL_AMMO:
|
||||
return "../hud/ico_powerup_refill_ammo_red";
|
||||
|
||||
case POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE:
|
||||
return "../hud/ico_powerup_building_instant_red";
|
||||
|
||||
case POWERUP_BOTTLE_RADIUS_STEALTH:
|
||||
return "../vgui/achievements/tf_soldier_kill_spy_killer";
|
||||
#ifdef STAGING_ONLY
|
||||
case POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL:
|
||||
return "../vgui/achievements/tf_mvm_earn_money_bonus";
|
||||
#endif
|
||||
}
|
||||
|
||||
return "../hud/ico_powerup_critboost_red";
|
||||
}
|
||||
|
||||
#ifdef TF_CLIENT_DLL
|
||||
void CTFPowerupBottle::FireGameEvent( IGameEvent *event )
|
||||
{
|
||||
const char *pszEventName = event->GetName();
|
||||
|
||||
if ( FStrEq( pszEventName, "player_spawn" ) )
|
||||
{
|
||||
CTFPlayer *pTFOwner = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( !pTFOwner )
|
||||
return;
|
||||
|
||||
const int nUserID = event->GetInt( "userid" );
|
||||
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByUserId( nUserID ) );
|
||||
if ( pPlayer && pPlayer == pTFOwner )
|
||||
{
|
||||
m_flLastSpawnTime = gpGlobals->curtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CTFPowerupBottle::GetWorldModelIndex( void )
|
||||
{
|
||||
if ( IsBasePowerUpBottle() && ( GetNumCharges() > 0 ) )
|
||||
{
|
||||
switch ( GetPowerupType() )
|
||||
{
|
||||
case POWERUP_BOTTLE_CRITBOOST:
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_krit.mdl" );
|
||||
|
||||
case POWERUP_BOTTLE_UBERCHARGE:
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_uber.mdl" );
|
||||
|
||||
case POWERUP_BOTTLE_RECALL:
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_tele.mdl" );
|
||||
|
||||
case POWERUP_BOTTLE_REFILL_AMMO:
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_ammo.mdl" );
|
||||
|
||||
case POWERUP_BOTTLE_BUILDINGS_INSTANT_UPGRADE:
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_build.mdl" );
|
||||
|
||||
case POWERUP_BOTTLE_RADIUS_STEALTH:
|
||||
#ifdef STAGING_ONLY
|
||||
case POWERUP_BOTTLE_SEE_CASH_THROUGH_WALL:
|
||||
#endif
|
||||
return modelinfo->GetModelIndex( "models/player/items/mvm_loot/all_class/mvm_flask_tele.mdl" );
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::GetWorldModelIndex();
|
||||
}
|
||||
#endif
|
||||
|
||||
int CTFPowerupBottle::GetSkin()
|
||||
{
|
||||
if ( !IsBasePowerUpBottle() )
|
||||
{
|
||||
return ( ( GetNumCharges() > 0 ) ? 1 : 0 );
|
||||
}
|
||||
|
||||
return BaseClass::GetSkin();
|
||||
}
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
// ******************************************************************************************
|
||||
// CEquipMvMCanteenNotification - Client notification to equip a canteen
|
||||
// ******************************************************************************************
|
||||
void CEquipMvMCanteenNotification::Accept()
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
|
||||
CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
|
||||
if ( !pLocalInv )
|
||||
{
|
||||
MarkForDeletion();
|
||||
return;
|
||||
}
|
||||
|
||||
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( !pLocalPlayer )
|
||||
{
|
||||
MarkForDeletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// try to equip non-stock-spellbook first
|
||||
static CSchemaItemDefHandle pItemDef_Robo( "Battery Canteens" );
|
||||
static CSchemaItemDefHandle pItemDef_KritzOrTreat( "Kritz Or Treat Canteen" );
|
||||
static CSchemaItemDefHandle pItemDef_Canteen( "Power Up Canteen (MvM)" );
|
||||
static CSchemaItemDefHandle pItemDef_DefaultCanteen( "Default Power Up Canteen (MvM)" );
|
||||
|
||||
CEconItemView *pCanteen= NULL;
|
||||
|
||||
Assert( pItemDef_Robo );
|
||||
Assert( pItemDef_KritzOrTreat );
|
||||
Assert( pItemDef_Canteen );
|
||||
Assert( pItemDef_DefaultCanteen );
|
||||
|
||||
for ( int i = 0; i < pLocalInv->GetItemCount(); ++i )
|
||||
{
|
||||
CEconItemView *pItem = pLocalInv->GetItem( i );
|
||||
Assert( pItem );
|
||||
|
||||
if ( pItem->GetItemDefinition() == pItemDef_Robo
|
||||
|| pItem->GetItemDefinition() == pItemDef_KritzOrTreat
|
||||
|| pItem->GetItemDefinition() == pItemDef_Canteen
|
||||
|| pItem->GetItemDefinition() == pItemDef_DefaultCanteen
|
||||
) {
|
||||
pCanteen = pItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Default item becomes a spellbook in this mode
|
||||
itemid_t iItemId = INVALID_ITEM_ID;
|
||||
if ( pCanteen )
|
||||
{
|
||||
iItemId = pCanteen->GetItemID();
|
||||
}
|
||||
|
||||
TFInventoryManager()->EquipItemInLoadout( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION, iItemId );
|
||||
|
||||
// Tell the GC to tell server that we should respawn if we're in a respawn room
|
||||
GCSDK::CGCMsg< GCSDK::MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
|
||||
GCClientSystem()->BSendMessage( msg );
|
||||
|
||||
MarkForDeletion();
|
||||
}
|
||||
|
||||
//===========================================================================================
|
||||
void CEquipMvMCanteenNotification::UpdateTick()
|
||||
{
|
||||
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( pLocalPlayer )
|
||||
{
|
||||
CTFPowerupBottle *pCanteen = dynamic_cast<CTFPowerupBottle*>( TFInventoryManager()->GetItemInLoadoutForClass( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION ) );
|
||||
if ( pCanteen )
|
||||
{
|
||||
MarkForDeletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // client
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_POWERUP_BOTTLE_H
|
||||
#define TF_POWERUP_BOTTLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tf_shareddefs.h"
|
||||
#include "tf_item_wearable.h"
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#define CTFPowerupBottle C_TFPowerupBottle
|
||||
#include "econ_notifications.h"
|
||||
#endif
|
||||
|
||||
|
||||
class CTFPowerupBottle : public CTFWearable
|
||||
{
|
||||
DECLARE_CLASS( CTFPowerupBottle, CTFWearable );
|
||||
public:
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFPowerupBottle();
|
||||
virtual ~CTFPowerupBottle() { }
|
||||
|
||||
PowerupBottleType_t GetPowerupType( void ) const;
|
||||
|
||||
virtual void Precache( void );
|
||||
|
||||
// reset the bottle to its initial state
|
||||
void Reset( void );
|
||||
|
||||
// Unequips the item as usual, but also removes any effect it may have been granting
|
||||
virtual void UnEquip( CBasePlayer* pOwner );
|
||||
|
||||
// Overridden so that this item can apply the effect only when it is active
|
||||
virtual void ReapplyProvision();
|
||||
|
||||
// @return true if the effect was applied and a charge was consumed, false otherwise
|
||||
bool Use();
|
||||
|
||||
// Remove the effect applied by the item
|
||||
void RemoveEffect();
|
||||
|
||||
// set the number of charges availabe on this item
|
||||
// @param usNumCharges
|
||||
void SetNumCharges( uint8 usNumCharges );
|
||||
|
||||
// @return the number of charges the item has
|
||||
uint8 GetNumCharges() const;
|
||||
|
||||
// @return the maximum number of charges this item can hold
|
||||
uint8 GetMaxNumCharges() const;
|
||||
|
||||
bool AllowedToUse();
|
||||
|
||||
const char* GetEffectLabelText( void );
|
||||
const char* GetEffectIconName( void );
|
||||
float GetProgress( void ) { return 0.0f; }
|
||||
|
||||
virtual int GetSkin();
|
||||
bool IsBasePowerUpBottle( void ) const { int iMode = 0; CALL_ATTRIB_HOOK_INT( iMode, set_weapon_mode ); return (iMode == 1); };
|
||||
|
||||
protected:
|
||||
|
||||
// Used internally to remove the effect after a tunable amount of time
|
||||
void StatusThink();
|
||||
|
||||
CNetworkVar( bool, m_bActive );
|
||||
CNetworkVar( uint8, m_usNumCharges );
|
||||
|
||||
private:
|
||||
|
||||
#ifdef TF_CLIENT_DLL
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
virtual int GetWorldModelIndex( void );
|
||||
#endif
|
||||
|
||||
float m_flLastSpawnTime;
|
||||
};
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
// ******************************************************************************************
|
||||
// CEquipMvMCanteenNotification - Client notification to equip a canteen
|
||||
// ******************************************************************************************
|
||||
class CEquipMvMCanteenNotification : public CEconNotification
|
||||
{
|
||||
public:
|
||||
CEquipMvMCanteenNotification() : CEconNotification()
|
||||
{
|
||||
m_bHasTriggered = false;
|
||||
}
|
||||
|
||||
~CEquipMvMCanteenNotification()
|
||||
{
|
||||
if ( !m_bHasTriggered )
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void MarkForDeletion()
|
||||
{
|
||||
m_bHasTriggered = true;
|
||||
CEconNotification::MarkForDeletion();
|
||||
}
|
||||
|
||||
virtual EType NotificationType() { return eType_AcceptDecline; }
|
||||
virtual bool BShowInGameElements() const { return true; }
|
||||
|
||||
virtual void Accept();
|
||||
virtual void Trigger() { Accept(); }
|
||||
virtual void Decline() { MarkForDeletion(); }
|
||||
virtual void UpdateTick();
|
||||
|
||||
static bool IsNotificationType( CEconNotification *pNotification ) { return dynamic_cast<CEquipMvMCanteenNotification *>( pNotification ) != NULL; }
|
||||
|
||||
private:
|
||||
bool m_bHasTriggered;
|
||||
};
|
||||
|
||||
#endif // client
|
||||
|
||||
#endif // TF_POWERUP_BOTTLE_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,946 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#ifndef TFITEMSCHEMA_H
|
||||
#define TFITEMSCHEMA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "econ_item_schema.h"
|
||||
#include "tf_item_constants.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "tf_matchmaking_shared.h"
|
||||
|
||||
#ifndef GC_DLL
|
||||
#include "util_shared.h"
|
||||
#endif
|
||||
|
||||
const int k_iMvmMissionIndex_Any = -1;
|
||||
const int k_iMvmMissionIndex_NotInSchema = -2;
|
||||
|
||||
//#ifndef STAGING_ONLY
|
||||
#define USE_MVM_TOUR 1
|
||||
//#endif // !STAGING_ONLY
|
||||
|
||||
const int k_iMvmTourIndex_Empty = -1; // empty tour name
|
||||
const int k_iMvmTourIndex_NotInSchema = -2;
|
||||
const int k_iMvmTourIndex_NotMannedUp = -3; // special value used when asking for the selected tour when not manned up
|
||||
|
||||
const uint32 k_unMvMMaxPointsPerBadgeLevel = 3; // require 3 missions to level up a badge
|
||||
|
||||
class CRandomChanceString
|
||||
{
|
||||
public:
|
||||
CRandomChanceString();
|
||||
|
||||
void AddString( const char *pszString, int nChance );
|
||||
const char *GetRandomString() const;
|
||||
|
||||
private:
|
||||
CUtlVector< std::pair< const char *, int > > m_vecChoices;
|
||||
int m_unTotalChance;
|
||||
};
|
||||
|
||||
class CTFTauntInfo
|
||||
{
|
||||
public:
|
||||
CTFTauntInfo();
|
||||
|
||||
bool BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
int GetIntroSceneCount( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_vecIntroScenes[iClass].Count(); }
|
||||
const char *GetIntroScene( int iClass, int iSceneIndex ) const
|
||||
{
|
||||
Assert( iSceneIndex >= 0 && iSceneIndex < GetIntroSceneCount( iClass ) );
|
||||
return m_vecIntroScenes[iClass][iSceneIndex];
|
||||
}
|
||||
|
||||
int GetOutroSceneCount( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_vecOutroScenes[iClass].Count(); }
|
||||
const char *GetOutroScene( int iClass, int iSceneIndex ) const
|
||||
{
|
||||
Assert( iSceneIndex >= 0 && iSceneIndex < GetOutroSceneCount( iClass ) );
|
||||
return m_vecOutroScenes[iClass][iSceneIndex];
|
||||
}
|
||||
|
||||
int GetPartnerTauntInitiatorSceneCount( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_vecPartnerTauntInitiatorScenes[iClass].Count(); }
|
||||
const char *GetPartnerTauntInitiatorScene( int iClass, int iSceneIndex ) const
|
||||
{
|
||||
Assert( iSceneIndex >= 0 && iSceneIndex < GetPartnerTauntInitiatorSceneCount( iClass ) );
|
||||
return m_vecPartnerTauntInitiatorScenes[iClass][iSceneIndex];
|
||||
}
|
||||
|
||||
int GetPartnerTauntReceiverSceneCount( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_vecPartnerTauntReceiverScenes[iClass].Count(); }
|
||||
const char *GetPartnerTauntReceiverScene( int iClass, int iSceneIndex ) const
|
||||
{
|
||||
Assert( iSceneIndex >= 0 && iSceneIndex < GetPartnerTauntReceiverSceneCount( iClass ) );
|
||||
return m_vecPartnerTauntReceiverScenes[iClass][iSceneIndex];
|
||||
}
|
||||
|
||||
const char *GetProp( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_pszProp[iClass]; }
|
||||
const char *GetPropIntroScene( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_pszPropIntroScene[iClass]; }
|
||||
const char *GetPropOutroScene( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_pszPropOutroScene[iClass]; }
|
||||
|
||||
float GetTauntSeparationForwardDistance() const { return m_flTauntSeparationForwardDistance; }
|
||||
float GetTauntSeparationRightDistance() const { return m_flTauntSeparationRightDistance; }
|
||||
float GetMinTauntTime() const { return m_flMinTauntTime; }
|
||||
|
||||
bool IsPartnerTaunt() const { return m_bIsPartnerTaunt; }
|
||||
bool ShouldStopTauntIfMoved() const { return m_bStopTauntIfMoved; }
|
||||
|
||||
int GetFOV() const { return m_nFOV; }
|
||||
float GetCameraDist() const { return m_flCameraDist; }
|
||||
float GetCameraDistUp() const { return m_flCameraDistUp; }
|
||||
|
||||
const char *GetParticleAttachment() const { return m_pszParticleAttachment; }
|
||||
|
||||
struct TauntInputRemap_t
|
||||
{
|
||||
TauntInputRemap_t()
|
||||
{
|
||||
m_iButton = 0;
|
||||
}
|
||||
int m_iButton;
|
||||
CUtlVector< const char* > m_vecButtonPressedScenes[LOADOUT_COUNT];
|
||||
CUtlVector< const char* > m_vecButtonReleasedScenes[LOADOUT_COUNT];
|
||||
};
|
||||
int GetTauntInputRemapCount() const { return m_vecTauntInputRemap.Count(); }
|
||||
const TauntInputRemap_t &GetTauntInputRemapScene( int iButtonIndex ) const
|
||||
{
|
||||
return m_vecTauntInputRemap[iButtonIndex];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool InitTauntInputRemap( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
CUtlVector< const char* > m_vecIntroScenes[LOADOUT_COUNT];
|
||||
CUtlVector< const char* > m_vecOutroScenes[LOADOUT_COUNT];
|
||||
CUtlVector< const char* > m_vecPartnerTauntInitiatorScenes[LOADOUT_COUNT];
|
||||
CUtlVector< const char* > m_vecPartnerTauntReceiverScenes[LOADOUT_COUNT];
|
||||
CUtlVector< TauntInputRemap_t > m_vecTauntInputRemap;
|
||||
const char *m_pszProp[LOADOUT_COUNT];
|
||||
const char *m_pszPropIntroScene[LOADOUT_COUNT];
|
||||
const char *m_pszPropOutroScene[LOADOUT_COUNT];
|
||||
const char *m_pszParticleAttachment;
|
||||
float m_flTauntSeparationForwardDistance;
|
||||
float m_flTauntSeparationRightDistance;
|
||||
float m_flMinTauntTime;
|
||||
bool m_bIsPartnerTaunt;
|
||||
bool m_bStopTauntIfMoved;
|
||||
|
||||
int m_nFOV;
|
||||
float m_flCameraDist;
|
||||
float m_flCameraDistUp;
|
||||
};
|
||||
|
||||
class CQuestThemeDefinition
|
||||
{
|
||||
public:
|
||||
|
||||
CQuestThemeDefinition( void );
|
||||
virtual ~CQuestThemeDefinition( void );
|
||||
|
||||
bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
|
||||
const char *GetName() const { return m_pszName; }
|
||||
|
||||
const char *GetNotificationResFile() const { return m_pszNotificationRes; }
|
||||
const char *GetQuestItemResFile() const { return m_pszQuestItemRes; }
|
||||
const char *GetInGameTrackerResFile() const { return m_pszInGameTrackerRes; }
|
||||
unacknowledged_item_inventory_positions_t GetUnackPos() const { return m_eUnackPos; }
|
||||
|
||||
#ifndef GC_DLL
|
||||
const char *GetGiveSoundForClass( int iClass ) const { return UTIL_GetRandomSoundFromEntry( m_vecGiveStrings[ iClass ].GetRandomString() ); }
|
||||
const char *GetCompleteSoundForClass( int iClass ) const { return UTIL_GetRandomSoundFromEntry( m_vecCompleteStrings[ iClass ].GetRandomString() ); }
|
||||
const char *GetFullyCompleteSoundForClass( int iClass ) const { return UTIL_GetRandomSoundFromEntry( m_vecFullyCompleteStrings[ iClass ].GetRandomString() ); }
|
||||
const char *GetDiscardSound() const { return UTIL_GetRandomSoundFromEntry( m_pszDiscardString ); }
|
||||
const char *GetRewardSound() const { return UTIL_GetRandomSoundFromEntry( m_pszRewardString ); }
|
||||
const char *GetRevealSound() const { return UTIL_GetRandomSoundFromEntry( m_pszOnRevealText ); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
KeyValues *m_pRawKVs;
|
||||
|
||||
const char *m_pszName;
|
||||
|
||||
// UI
|
||||
const char* m_pszNotificationRes;
|
||||
const char* m_pszQuestItemRes;
|
||||
const char* m_pszInGameTrackerRes;
|
||||
unacknowledged_item_inventory_positions_t m_eUnackPos;
|
||||
|
||||
// Sounds
|
||||
CRandomChanceString m_vecGiveStrings[LOADOUT_COUNT]; // Per class
|
||||
CRandomChanceString m_vecCompleteStrings[LOADOUT_COUNT]; // Per class
|
||||
CRandomChanceString m_vecFullyCompleteStrings[LOADOUT_COUNT]; // Per class
|
||||
const char* m_pszRewardString;
|
||||
const char* m_pszDiscardString;
|
||||
const char* m_pszOnRevealText;
|
||||
};
|
||||
|
||||
typedef CUtlVector< const class CTFQuestObjectiveDefinition* > QuestObjectiveDefVec_t;
|
||||
typedef CUtlVector< const char * > QuestDescriptionVec_t;
|
||||
typedef CUtlVector< const char * > QuestNameVec_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTFRequiredQuestItemsSet
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFRequiredQuestItemsSet
|
||||
{
|
||||
public:
|
||||
CTFRequiredQuestItemsSet( void ) {}
|
||||
|
||||
bool BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
bool BPostInit( CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
|
||||
bool OwnsRequiredItems( const CUtlVector< item_definition_index_t >& vecOwnedItemDefs ) const;
|
||||
const item_definition_index_t& GetLoanerItemDef() const { return m_LoanerItemDef; }
|
||||
|
||||
private:
|
||||
CUtlVector< item_definition_index_t > m_vecQualifyingItemDefs;
|
||||
item_definition_index_t m_LoanerItemDef;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CQuestDefinition
|
||||
//-----------------------------------------------------------------------------
|
||||
class CQuestDefinition
|
||||
{
|
||||
public:
|
||||
|
||||
CQuestDefinition( void );
|
||||
|
||||
bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
|
||||
uint32 GetMaxStandardPoints() const { return m_nMaxStandardPoints; }
|
||||
uint32 GetMaxBonusPoints() const { return m_nMaxBonusPoints; }
|
||||
const char *GetRewardLootlistName() const { return m_pszRewardLootlistName; }
|
||||
const char *GetQuickplayMapName() const { return m_pszQuickplayMapName; }
|
||||
|
||||
const char *GetMatchmakingGroupName() const { return m_strMatchmakingGroupName.Get(); }
|
||||
const char *GetMatchmakingCategoryName() const { return m_strMatchmakingCategoryName.Get(); }
|
||||
const char *GetMatchmakingMapName() const { return m_strMatchmakingMapName.Get(); }
|
||||
|
||||
const QuestObjectiveDefVec_t& GetObjectives() const { return m_vecObjectiveDefinitions; }
|
||||
void GetRolledObjectivesForItem( QuestObjectiveDefVec_t& vecRolledObjectives, const CEconItem* pItem ) const;
|
||||
const CQuestThemeDefinition *GetQuestTheme() const;
|
||||
const char *GetRolledDescriptionForItem( const CEconItem* pItem ) const;
|
||||
const char *GetRolledNameForItem( const CEconItem* pItem ) const;
|
||||
const char *GetCorrespondingOperationName() const { return m_pszCorrespondingOperationName; }
|
||||
|
||||
const CUtlVector< CTFRequiredQuestItemsSet >& GetRequiredItemSets() const { return m_vecRequiredItemSets; }
|
||||
|
||||
private:
|
||||
|
||||
QuestObjectiveDefVec_t m_vecObjectiveDefinitions;
|
||||
uint32 m_nMaxStandardPoints;
|
||||
uint32 m_nMaxBonusPoints;
|
||||
const char *m_pszRewardLootlistName;
|
||||
uint16 m_nNumObjectivesToRoll;
|
||||
const char *m_pszQuestThemeName;
|
||||
const char *m_pszCorrespondingOperationName;
|
||||
const char *m_pszQuickplayMapName;
|
||||
|
||||
CUtlString m_strMatchmakingGroupName;
|
||||
CUtlString m_strMatchmakingCategoryName;
|
||||
CUtlString m_strMatchmakingMapName;
|
||||
|
||||
QuestDescriptionVec_t m_vecQuestDescriptions;
|
||||
QuestNameVec_t m_vecQuestNames;
|
||||
CEconItemDefinition *m_pOperationBadgeDef;
|
||||
|
||||
// loaner items for this quest
|
||||
CUtlVector< CTFRequiredQuestItemsSet > m_vecRequiredItemSets;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wars
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWarDefinition
|
||||
{
|
||||
public:
|
||||
|
||||
CWarDefinition();
|
||||
|
||||
bool BInitFromKV( KeyValues *pKV, CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
struct CWarSideDefinition_t
|
||||
{
|
||||
CWarSideDefinition_t()
|
||||
: m_pszLeaderboardName( NULL )
|
||||
, m_pszLocalizedName( NULL )
|
||||
, m_nSideIndex( INVALID_WAR_SIDE )
|
||||
{}
|
||||
|
||||
bool BInitFromKV( const char* pszContainingWarName, KeyValues *pKVSide, CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
const char* m_pszLocalizedName;
|
||||
const char* m_pszLeaderboardName;
|
||||
war_side_t m_nSideIndex;
|
||||
};
|
||||
typedef CUtlMap< war_side_t, CWarSideDefinition_t > SidesMap_t;
|
||||
|
||||
const SidesMap_t& GetSides() const { return m_mapSides; }
|
||||
const CWarSideDefinition_t* GetSide( war_side_t nSide ) const;
|
||||
war_definition_index_t GetDefIndex() const { return m_nDefIndex; }
|
||||
const char* GetDefName() const { return m_pszDefName; }
|
||||
bool IsActive() const;
|
||||
bool IsValidSide( war_side_t nSide ) const;
|
||||
RTime32 GetStartDate() const { return m_rtTimeStart; }
|
||||
RTime32 GetEndDate() const { return m_rtTimeEnd; }
|
||||
private:
|
||||
|
||||
const char* m_pszLocalizedWarname;
|
||||
const char* m_pszDefName;
|
||||
SidesMap_t m_mapSides;
|
||||
RTime32 m_rtTimeStart;
|
||||
RTime32 m_rtTimeEnd;
|
||||
war_definition_index_t m_nDefIndex;
|
||||
};
|
||||
typedef CUtlMap< war_definition_index_t, CWarDefinition* > WarDefinitionMap_t;
|
||||
|
||||
const char *GetPlayerClassName( int iClass );
|
||||
const char *GetPlayerClassLocalizationKey( int iClass );
|
||||
itemid_t GetAssociatedQuestItemID( const IEconItemInterface *pEconItem );
|
||||
|
||||
class CTFItemDefinition : public CEconItemDefinition
|
||||
{
|
||||
public:
|
||||
|
||||
CTFItemDefinition()
|
||||
{
|
||||
InternalInitialize();
|
||||
}
|
||||
|
||||
~CTFItemDefinition()
|
||||
{
|
||||
if ( m_pTauntData )
|
||||
{
|
||||
delete m_pTauntData;
|
||||
m_pTauntData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// CEconItemDefinition interface.
|
||||
virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL ) OVERRIDE;
|
||||
#if defined(CLIENT_DLL) || defined(GAME_DLL)
|
||||
virtual bool BInitFromTestItemKVs( int iNewDefIndex, KeyValues *pKVItem, CUtlVector<CUtlString>* pVecErrors = NULL ) OVERRIDE;
|
||||
virtual void CopyPolymorphic( const CEconItemDefinition *pSourceDef );
|
||||
virtual void GeneratePrecacheModelStrings( bool bDynamicLoad, CUtlVector<const char *> *out_pVecModelStrings ) const;
|
||||
#endif // defined(CLIENT_DLL) || defined(GAME_DLL)
|
||||
|
||||
int GetAnimSlot( void ) const { return m_iAnimationSlot; }
|
||||
|
||||
// Class & Slot handling
|
||||
int GetDefaultLoadoutSlot( void ) const { return m_iDefaultLoadoutSlot; }
|
||||
int GetAccountLoadoutSlot( void ) const { return m_iDefaultLoadoutSlot; }
|
||||
const CBitVec<LOADOUT_COUNT> *GetClassUsability( void ) const { return &m_vbClassUsability; }
|
||||
void FilloutSlotUsage( CBitVec<LOADOUT_COUNT> *pBV ) const;
|
||||
bool CanBeUsedByClass( int iClass ) const { return iClass == GEconItemSchema().GetAccountIndex() ? m_eEquipType == EQUIP_TYPE_ACCOUNT : m_vbClassUsability.IsBitSet( iClass ); }
|
||||
bool CanBeUsedByAllClasses( void ) const;
|
||||
EEquipType_t GetEquipType( void ) const { return m_eEquipType; }
|
||||
bool CanBePlacedInSlot( int nSlot ) const;
|
||||
const char *GetPlayerDisplayModel( int iClass ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_pszPlayerDisplayModel[iClass]; }
|
||||
virtual const char *GetPlayerDisplayModelAlt( int iClass = 0 ) const { Assert( iClass >= 0 && iClass < LOADOUT_COUNT ); return m_pszPlayerDisplayModelAlt[iClass]; }
|
||||
|
||||
int GetLoadoutSlot( int iLoadoutClass ) const;
|
||||
#ifndef GC_DLL
|
||||
bool IsAWearable() const;
|
||||
bool IsContentStreamable() const;
|
||||
const char* GetAdTextToken() const { return m_pszAdText; }
|
||||
const char* GetAdResFile() const { return m_pszAdResFile; }
|
||||
#endif // !GC_DLL
|
||||
|
||||
CTFTauntInfo *GetTauntData() const { return m_pTauntData; }
|
||||
|
||||
const CQuestDefinition *GetQuestDef() const { return m_pQuestData; }
|
||||
|
||||
KeyValues *GetPaintKitWearDefinition( int nWear ) const;
|
||||
const char *GetPaintKitName( ) const;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
bool HasDetailedIcon() const { return m_bHasDetailedIcon; }
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
private:
|
||||
void InternalInitialize();
|
||||
|
||||
// The load-out slot that this item can be placed into.
|
||||
int m_iDefaultLoadoutSlot;
|
||||
int m_iAnimationSlot;
|
||||
|
||||
// taunt item data
|
||||
CTFTauntInfo *m_pTauntData;
|
||||
|
||||
// Quest data
|
||||
CQuestDefinition *m_pQuestData;
|
||||
|
||||
// The .mdl file used for this item when it's being carried by a player.
|
||||
const char *m_pszPlayerDisplayModel[LOADOUT_COUNT];
|
||||
const char *m_pszPlayerDisplayModelAlt[LOADOUT_COUNT];
|
||||
|
||||
#ifndef GC_DLL
|
||||
const char* m_pszAdText;
|
||||
const char* m_pszAdResFile;
|
||||
#endif
|
||||
|
||||
// Specifies which class can use this item.
|
||||
CBitVec<LOADOUT_COUNT> m_vbClassUsability;
|
||||
int m_iLoadoutSlots[LOADOUT_COUNT]; // Slot that each class places the item into.
|
||||
EEquipType_t m_eEquipType;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
bool m_bHasDetailedIcon;
|
||||
#endif // CLIENT_DLL
|
||||
};
|
||||
|
||||
class CTFStyleInfo : public CEconStyleInfo
|
||||
{
|
||||
public:
|
||||
CTFStyleInfo()
|
||||
{
|
||||
for ( int i = 0; i < ARRAYSIZE( m_pszPlayerDisplayModel ); i++ )
|
||||
{
|
||||
for ( int j = 0; j < ARRAYSIZE( m_pszPlayerDisplayModel[i] ); j++ )
|
||||
{
|
||||
m_pszPlayerDisplayModel[i][j] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors ) OVERRIDE;
|
||||
#if defined(CLIENT_DLL) || defined(GAME_DLL)
|
||||
virtual void GeneratePrecacheModelStringsForStyle( CUtlVector<const char *> *out_pVecModelStrings ) const OVERRIDE;
|
||||
#endif
|
||||
|
||||
const char *GetPlayerDisplayModel( int iClass, int iTeam ) const;
|
||||
|
||||
private:
|
||||
// The .mdl file used for this item when it's being carried by a player.
|
||||
const char *m_pszPlayerDisplayModel[2][LOADOUT_COUNT];
|
||||
};
|
||||
|
||||
class CTFCraftingRecipeDefinition : public CEconCraftingRecipeDefinition
|
||||
{
|
||||
public:
|
||||
virtual bool ItemListMatchesInputs( CUtlVector<CEconItem*> *vecCraftingItems, KeyValues *out_pkvCraftParams, bool bIgnoreSlop, CUtlVector<uint64> *vecChosenItems ) const OVERRIDE;
|
||||
|
||||
// A client function for testing to see if the contents of the player's backpack can match against this recipe.
|
||||
// Broken out into a separate function so we don't run the risk of its thorny logic introducing bugs into the backend crafting logic.
|
||||
bool CanMatchAgainstBackpack( CUtlVector<CEconItem*> *vecAllItems, CUtlVector<CEconItem*> vecItemsByClass[LOADOUT_COUNT], CUtlVector<CEconItem*> vecItemsBySlot[ CLASS_LOADOUT_POSITION_COUNT ], CUtlVector<uint64> *vecChosenItems ) const;
|
||||
|
||||
private:
|
||||
bool CheckSubItemListAgainstBackpack( CUtlVector<CEconItem*> *vecCraftingItems, CUtlVector<uint64> *vecChosenItems ) const;
|
||||
};
|
||||
|
||||
typedef uint32 ObjectiveConditionDefIndex_t;
|
||||
const ObjectiveConditionDefIndex_t INVALID_QUEST_OBJECTIVE_CONDITIONS_INDEX = ObjectiveConditionDefIndex_t(-1);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTFQuestObjectiveConditionsDefinition
|
||||
// These contain the actual logic that can be used by multiple objectives.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFQuestObjectiveConditionsDefinition
|
||||
{
|
||||
public:
|
||||
CTFQuestObjectiveConditionsDefinition( void );
|
||||
virtual ~CTFQuestObjectiveConditionsDefinition( void );
|
||||
|
||||
virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
bool BPostInit( CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
|
||||
ObjectiveConditionDefIndex_t GetDefIndex() const { return m_nDefIndex; }
|
||||
#ifndef GC_DLL
|
||||
KeyValues *GetKeyValues() const { return m_pConditionsKey; }
|
||||
#endif
|
||||
|
||||
const CUtlVector< CTFRequiredQuestItemsSet >& GetRequiredItemSets() const { return m_vecRequiredItemSets; }
|
||||
|
||||
private:
|
||||
ObjectiveConditionDefIndex_t m_nDefIndex;
|
||||
#ifndef GC_DLL
|
||||
KeyValues *m_pConditionsKey;
|
||||
#endif
|
||||
|
||||
CUtlVector< CTFRequiredQuestItemsSet > m_vecRequiredItemSets;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CQuestObjectiveDefinition
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFQuestObjectiveDefinition : public CQuestObjectiveDefinition
|
||||
{
|
||||
public:
|
||||
|
||||
CTFQuestObjectiveDefinition( void );
|
||||
virtual ~CTFQuestObjectiveDefinition( void );
|
||||
|
||||
virtual bool BInitFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors = NULL ) OVERRIDE;
|
||||
|
||||
#ifndef GC_DLL
|
||||
KeyValues *GetConditionsKeyValues() const;
|
||||
#endif
|
||||
const CTFQuestObjectiveConditionsDefinition* GetConditions() const;
|
||||
|
||||
private:
|
||||
ObjectiveConditionDefIndex_t m_nConditionDefIndex;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MvMMap_t
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MvMMap_t
|
||||
{
|
||||
CUtlConstString m_sMap; // name of the map file
|
||||
CUtlConstString m_sDisplayName; // Localization tag starting with '#'
|
||||
CUtlVector<int> m_vecMissions; // indexes into the schema's challenge list
|
||||
};
|
||||
|
||||
enum EMvMChallengeDifficulty
|
||||
{
|
||||
k_EMvMChallengeDifficulty_Invalid = -1,
|
||||
k_EMvMChallengeDifficulty_Normal = 1,
|
||||
k_EMvMChallengeDifficulty_Intermediate = 2,
|
||||
k_EMvMChallengeDifficulty_Advanced = 3,
|
||||
k_EMvMChallengeDifficulty_Expert = 4,
|
||||
k_EMvMChallengeDifficulty_Haunted = 5,
|
||||
|
||||
k_EMvMChallengeDifficultyFirstValid = k_EMvMChallengeDifficulty_Normal,
|
||||
k_EMvMChallengeDifficultyLastValid = k_EMvMChallengeDifficulty_Haunted
|
||||
};
|
||||
|
||||
extern EMvMChallengeDifficulty GetMvMChallengeDifficultyByInternalName( const char *pszEnglishID );
|
||||
extern const char *GetMvMChallengeDifficultyLocName( EMvMChallengeDifficulty eDifficulty );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MvMMission_t
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MvMMission_t
|
||||
{
|
||||
int m_iDisplayMapIndex; // Index into the schema's map list, for UI purposes
|
||||
CUtlConstString m_sPop; // name of the pop file
|
||||
CUtlConstString m_sDisplayName; // Localization tag starting with '#'
|
||||
CUtlConstString m_sMode; // Localization tag starting with '#'
|
||||
CUtlConstString m_sMapNameActual; // name of the map file to really load
|
||||
EMvMChallengeDifficulty m_eDifficulty;
|
||||
uint32 m_unMannUpPoints; // points for completing mission
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MvMTour_t
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MvMTourMission_t
|
||||
{
|
||||
int m_iMissionIndex; // index to the schema's challenge list
|
||||
int m_iBadgeSlot; // *index* (0...31) of the slot on the badge. -1 if not assigned a slot. (No bragging rights for this challenge.)
|
||||
};
|
||||
|
||||
struct MvMTour_t
|
||||
{
|
||||
CUtlConstString m_sTourInternalName;
|
||||
CUtlConstString m_sTourNameLocalizationToken; // Localization tag starting with '#', shown to clients
|
||||
CUtlConstString m_sLootImageName;
|
||||
const CEconItemDefinition *m_pBadgeItemDef; // can be NULL if there is no badge reward. Implies all badge slots will be -1. Only really valid for practice tours.
|
||||
#ifdef GC
|
||||
const CEconLootListDefinition *m_pMissionCompleteLootList; // can be NULL, but really only makes sense if there is no badge reward.
|
||||
const CEconLootListDefinition *m_pTourCompleteLootList; // can be NULL, but really only makes sense if there is no badge reward.
|
||||
#endif
|
||||
CCopyableUtlVector<MvMTourMission_t> m_vecMissions; // indexes into the schema's challenge list
|
||||
uint32 m_nAllChallengesBits;
|
||||
EMvMChallengeDifficulty m_eDifficulty;
|
||||
bool m_bIsNew;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Maps
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum EGameCategory
|
||||
{
|
||||
kGameCategory_Escort = 0,
|
||||
kGameCategory_CTF,
|
||||
kGameCategory_AttackDefense,
|
||||
kGameCategory_Koth,
|
||||
kGameCategory_CP,
|
||||
kGameCategory_EscortRace,
|
||||
kGameCategory_EventMix,
|
||||
kGameCategory_SD,
|
||||
kGameCategory_Quickplay,
|
||||
kGameCategory_Event247,
|
||||
kGameCategory_Arena,
|
||||
kGameCategory_RobotDestruction,
|
||||
kGameCategory_Powerup,
|
||||
kGameCategory_Featured,
|
||||
kGameCategory_Passtime,
|
||||
kGameCategory_Community_Update,
|
||||
kGameCategory_Misc,
|
||||
kGameCategory_Competitive_6v6,
|
||||
kGameCategory_Other,
|
||||
kGameCategory_Halloween,
|
||||
|
||||
// Note: Don't reorder this list. Only add to the end
|
||||
|
||||
eNumGameCategories,
|
||||
};
|
||||
|
||||
typedef uint32 map_identifier_t;
|
||||
|
||||
enum eQuickplayMatchType
|
||||
{
|
||||
kQuickplay_AdvancedUsersOnly,
|
||||
kQuickplay_AllUsers, // everyone
|
||||
kQuickplay_Disabled, // no-one
|
||||
|
||||
kQuickplayTypeCount
|
||||
};
|
||||
|
||||
enum EMatchmakingGroupType
|
||||
{
|
||||
kMatchmakingType_None = -1,
|
||||
|
||||
kMatchmakingType_SpecialEvents,
|
||||
kMatchmakingType_Core,
|
||||
kMatchmakingType_Alternative,
|
||||
kMatchmakingType_Competitive_6v6,
|
||||
|
||||
kMatchmakingTypeCount
|
||||
};
|
||||
|
||||
enum EMatchmakingGameModeRestrictionType
|
||||
{
|
||||
kMatchmakingGameModeRestrictionType_None = -1,
|
||||
|
||||
kMatchmakingGameModeRestrictionType_Holiday,
|
||||
kMatchmakingGameModeRestrictionType_Operation,
|
||||
|
||||
kMatchmakingGameModeRestrictionTypeCount
|
||||
};
|
||||
|
||||
typedef uint32 MapDefIndex_t;
|
||||
|
||||
struct MapDef_t
|
||||
{
|
||||
MapDef_t( const char* pszMapStampDefName )
|
||||
: mapStampDef( pszMapStampDefName )
|
||||
, m_nStatsIdentifier( (MapDefIndex_t)-1 )
|
||||
{}
|
||||
|
||||
CSchemaItemDefHandle mapStampDef;
|
||||
MapDefIndex_t m_nDefIndex;
|
||||
const char* pszMapName;
|
||||
const char* pszMapNameLocKey;
|
||||
const char* pszAuthorsLocKey; // if set, will be considered a community map in the UI
|
||||
const char* pszStrangePrefixLocKey;
|
||||
|
||||
// The m_nStatsIdentifier field is used when looking up a map in a user's gamestats.
|
||||
// It's a relic from the quickplay days and how the maps were defined in the schema back then.
|
||||
// We've since switched to using a map defindex, which is easier to read and manage, but this
|
||||
// field still needs to be used to lookup map gamestats because millions of customers
|
||||
// have these maps identified by those numbers in their gamestats. The old numbers for existing
|
||||
// maps is already defined in _maps.txt newly defined maps don't need to specify a "statsidentifier"
|
||||
// field, because they will generate their own unique identifier.
|
||||
map_identifier_t m_nStatsIdentifier;
|
||||
map_identifier_t GetStatsIdentifier() const { return m_nStatsIdentifier == -1 ? (m_nDefIndex << 16) : m_nStatsIdentifier; }
|
||||
bool IsCommunityMap() const { return pszAuthorsLocKey != NULL; }
|
||||
CUtlVector< EGameCategory > m_vecAssociatedGameCategories;
|
||||
CUtlVector<econ_tag_handle_t> vecTags;
|
||||
// The rolling match tags for this map. When a rolling match vote happens, only allow voting on
|
||||
// maps that have at least one matching tag with this map.
|
||||
struct WeightedNextMapCandidates_t
|
||||
{
|
||||
MapDefIndex_t m_nDefIndex;
|
||||
float m_flWeight;
|
||||
};
|
||||
CUtlVector< WeightedNextMapCandidates_t > m_vecRollingMatchMaps;
|
||||
void AddMapAsTargetWithWeight( const WeightedNextMapCandidates_t& target )
|
||||
{
|
||||
FOR_EACH_VEC( m_vecRollingMatchMaps, i )
|
||||
{
|
||||
if ( m_vecRollingMatchMaps[ i ].m_nDefIndex == target.m_nDefIndex )
|
||||
{
|
||||
m_vecRollingMatchMaps[ i ].m_flWeight = Max( m_vecRollingMatchMaps[ i ].m_flWeight, target.m_flWeight );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_vecRollingMatchMaps.AddToTail( target );
|
||||
}
|
||||
|
||||
CUtlVector< econ_tag_handle_t > m_vecRollingMatchTags;
|
||||
bool BHasRollingMatchTag( econ_tag_handle_t tag ) const
|
||||
{
|
||||
FOR_EACH_VEC( m_vecRollingMatchTags, i )
|
||||
{
|
||||
if ( m_vecRollingMatchTags[ i ] == tag )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct WeightedNextMapTargets_t
|
||||
{
|
||||
econ_tag_handle_t m_tag;
|
||||
float m_flWeight;
|
||||
};
|
||||
CUtlVector< WeightedNextMapTargets_t > m_vecRollingMatchTargets;
|
||||
|
||||
};
|
||||
|
||||
struct SchemaMMGameModeRestriction_t
|
||||
{
|
||||
SchemaMMGameModeRestriction_t()
|
||||
{
|
||||
m_eType = kMatchmakingGameModeRestrictionType_None;
|
||||
m_nValue = -1;
|
||||
}
|
||||
|
||||
EMatchmakingGameModeRestrictionType m_eType;
|
||||
int m_nValue;
|
||||
CUtlString m_strValue;
|
||||
};
|
||||
|
||||
struct SchemaMMGroup_t;
|
||||
struct SchemaGameCategory_t
|
||||
{
|
||||
SchemaGameCategory_t()
|
||||
: m_eGameCategory( eNumGameCategories )
|
||||
, m_pszLocalizedName( NULL )
|
||||
, m_pMMGroup( NULL )
|
||||
, m_pszLocalizedDesc( NULL )
|
||||
, m_pszListImage( NULL )
|
||||
{}
|
||||
|
||||
SchemaGameCategory_t( const SchemaGameCategory_t& other )
|
||||
{
|
||||
m_eGameCategory = other.m_eGameCategory;
|
||||
m_pMMGroup = other.m_pMMGroup;
|
||||
m_pszLocalizedName = other.m_pszLocalizedName;
|
||||
m_pszLocalizedDesc = other.m_pszLocalizedDesc;
|
||||
m_pszListImage = other.m_pszListImage;
|
||||
m_vecMaps.Purge();
|
||||
m_vecMaps.CopyArray( other.m_vecMaps.Base(), other.m_vecMaps.Count() );
|
||||
m_vecRestrictions.Purge();
|
||||
m_vecRestrictions.CopyArray( other.m_vecRestrictions.Base(), other.m_vecRestrictions.Count() );
|
||||
}
|
||||
|
||||
~SchemaGameCategory_t()
|
||||
{}
|
||||
|
||||
void AddMap( const MapDef_t *pMap, bool bEnabled )
|
||||
{
|
||||
if ( !pMap )
|
||||
return;
|
||||
|
||||
m_vecMaps.AddToTail( pMap );
|
||||
|
||||
if ( bEnabled )
|
||||
{
|
||||
m_vecEnabledMaps.AddToTail( pMap );
|
||||
}
|
||||
}
|
||||
|
||||
const MapDef_t *GetRandomMap( void ) const
|
||||
{
|
||||
Assert( m_vecEnabledMaps.Count() );
|
||||
return m_vecEnabledMaps[RandomInt( 0, m_vecEnabledMaps.Count() - 1 )];
|
||||
}
|
||||
|
||||
bool PassesRestrictions() const;
|
||||
|
||||
//void SerializeToKVs( KeyValues* pKV );
|
||||
|
||||
EGameCategory m_eGameCategory;
|
||||
const SchemaMMGroup_t* m_pMMGroup;
|
||||
const char* m_pszName;
|
||||
const char* m_pszLocalizedName;
|
||||
const char* m_pszLocalizedDesc;
|
||||
const char* m_pszListImage;
|
||||
const char* m_pszMMType;
|
||||
CUtlVector< const MapDef_t* > m_vecEnabledMaps;
|
||||
CUtlVector< SchemaMMGameModeRestriction_t > m_vecRestrictions;
|
||||
CUtlVector< const MapDef_t* > m_vecMaps;
|
||||
};
|
||||
typedef CUtlMap< EGameCategory, SchemaGameCategory_t* > GameCategoryMap_t;
|
||||
|
||||
struct SchemaMMGroup_t
|
||||
{
|
||||
SchemaMMGroup_t()
|
||||
: m_eMMGroup( kMatchmakingType_None )
|
||||
, m_pszLocalizedName( NULL )
|
||||
, m_nMaxExcludes( 0 )
|
||||
{}
|
||||
|
||||
SchemaMMGroup_t( const SchemaMMGroup_t& other )
|
||||
{
|
||||
m_eMMGroup = other.m_eMMGroup;
|
||||
m_pszLocalizedName = other.m_pszLocalizedName;
|
||||
m_nMaxExcludes = other.m_nMaxExcludes;
|
||||
m_vecModes.Purge();
|
||||
m_vecModes.CopyArray( other.m_vecModes.Base(), other.m_vecModes.Count() );
|
||||
}
|
||||
|
||||
bool IsCategoryValid() const;
|
||||
|
||||
~SchemaMMGroup_t()
|
||||
{}
|
||||
|
||||
EMatchmakingGroupType m_eMMGroup;
|
||||
const char* m_pszName;
|
||||
const char* m_pszLocalizedName;
|
||||
int m_nMaxExcludes;
|
||||
CBitVec<k_nMatchGroup_Count> m_bitsValidMMGroups;
|
||||
CUtlVector< const SchemaGameCategory_t* > m_vecModes;
|
||||
};
|
||||
typedef CUtlMap< EMatchmakingGroupType, SchemaMMGroup_t* > MMGroupMap_t;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CTFItemSchema
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFItemSchema : public CEconItemSchema
|
||||
{
|
||||
public:
|
||||
CTFItemSchema();
|
||||
|
||||
virtual void Reset();
|
||||
|
||||
CTFItemDefinition *GetTFItemDefinition( int iItemIndex )
|
||||
{
|
||||
return (CTFItemDefinition *)GetItemDefinition( iItemIndex );
|
||||
}
|
||||
|
||||
CTFCraftingRecipeDefinition *GetTFCraftingRecipeDefinition( int iRecipeIndex )
|
||||
{
|
||||
return (CTFCraftingRecipeDefinition *)GetRecipeDefinition( iRecipeIndex );
|
||||
}
|
||||
|
||||
const CQuestThemeDefinition *GetQuestThemeByName( const char *pszDefName ) const;
|
||||
const CUtlMap<const char*, CQuestThemeDefinition*, int >& GetQuestThemes() const { return m_mapQuestThemes; }
|
||||
const CTFQuestObjectiveConditionsDefinition* GetQuestObjectiveConditionByDefIndex( ObjectiveConditionDefIndex_t nDefIndex );
|
||||
|
||||
const CWarDefinition *GetWarDefinitionByIndex( war_definition_index_t nDefIndex ) const;
|
||||
const CWarDefinition *GetWarDefinitionByName( const char* pszDefName ) const;
|
||||
const WarDefinitionMap_t& GetWarDefinitions() const { return m_mapWars; }
|
||||
|
||||
const CUtlVector<const char *>& GetClassUsabilityStrings() const { return m_vecClassUsabilityStrings; }
|
||||
const CUtlVector<const char *>& GetLoadoutStrings( EEquipType_t eType ) const { return eType == EQUIP_TYPE_CLASS ? m_vecClassLoadoutStrings : m_vecAccountLoadoutStrings; }
|
||||
const CUtlVector<const char *>& GetLoadoutStringsForDisplay( EEquipType_t eType ) const { return eType == EQUIP_TYPE_CLASS ? m_vecClassLoadoutStringsForDisplay : m_vecAccountLoadoutStringsForDisplay; }
|
||||
const CUtlVector<const char *>& GetWeaponTypeSubstrings() const { return m_vecWeaponTypeSubstrings; }
|
||||
|
||||
static const char k_rchOverrideItemLevelDescStringAttribName[];
|
||||
|
||||
static const char k_rchMvMTicketItemDefName[];
|
||||
static const char k_rchMvMSquadSurplusVoucherItemDefName[];
|
||||
static const char k_rchMvMPowerupBottleItemDefName[];
|
||||
static const char k_rchMvMChallengeCompletedMaskAttribName[];
|
||||
static const char k_rchLadderPassItemDefName[];
|
||||
|
||||
static const char *GetMvMBadgeContractPointsAttributeName( EMvMChallengeDifficulty difficulty );
|
||||
static const char *GetMvMBadgeContractLevelAttributeName( EMvMChallengeDifficulty difficulty );
|
||||
|
||||
const CUtlVector<MvMMap_t>& GetMvmMaps() const { return m_vecMvMMaps; }
|
||||
const CUtlVector<MvMMission_t>& GetMvmMissions() const { return m_vecMvMMissions; }
|
||||
const CUtlVector<MvMTour_t>& GetMvmTours() const { return m_vecMvMTours; }
|
||||
//
|
||||
/// Return index into mission list, or one of these special values:
|
||||
/// k_iMvmMissionIndex_Any if empty string is passed
|
||||
/// k_iMvmMissionIndex_NotInSchema if not found
|
||||
///
|
||||
/// Input is the full pop filename, but without the directory or extension
|
||||
int FindMvmMissionByName( const char *pszChallengeName ) const;
|
||||
|
||||
/// Get pop filename (without extension) given the challenge index.
|
||||
/// Handles k_iMvmMissionIndex_Any and k_iMvmMissionIndex_NotInSchema
|
||||
const char *GetMvmMissionName( int iChallengeIndex ) const;
|
||||
|
||||
/// Return index into tour list, or one of these special values:
|
||||
/// k_iMvmTourIndex_Any if empty string is passed
|
||||
/// k_iMvmTourIndex_NotInSchema if not found
|
||||
///
|
||||
/// Input is the value of MvMTour_t::m_sTourInternalName
|
||||
int FindMvmTourByName( const char *pszTourName ) const;
|
||||
|
||||
/// Find mission within a particular tour, and return index into MvMTour_t::m_vecMissions.
|
||||
/// Returns -1 if invalid tour index or mission is not part of the tour
|
||||
int FindMvmMissionInTour( int idxTour, int idxMissionInSchema ) const;
|
||||
|
||||
/// Get badge slot corresponding to particular mission, for a given tour.
|
||||
/// Returns bit index MvMTourMission_t::m_iBadgeSlot (NOT BITMASK), or -1 if
|
||||
/// invalid tour index of mission is not part of the tour
|
||||
int GetMvmMissionBadgeSlotForTour( int idxTour, int idxMissionInSchema ) const;
|
||||
|
||||
int GetMapCount() const { return m_vecMasterListOfMaps.Count(); }
|
||||
const MapDef_t *GetMasterMapDefByName( const char *pszSearchName ) const;
|
||||
const MapDef_t *GetMasterMapDefByIndex( MapDefIndex_t unIndex ) const;
|
||||
const CUtlVector<MapDef_t*>& GetMasterMapsList() const { return m_vecMasterListOfMaps; }
|
||||
const GameCategoryMap_t& GetGameCategoryMap() const { return m_mapGameCategories; }
|
||||
const SchemaGameCategory_t* GetGameCategory( EGameCategory eType ) const;
|
||||
const MMGroupMap_t& GetMMGroupMap() const { return m_mapMMGroups; }
|
||||
const SchemaMMGroup_t* GetMMGroup( EMatchmakingGroupType eCat ) const;
|
||||
|
||||
public:
|
||||
// CEconItemSchema interface.
|
||||
virtual CEconItemDefinition *CreateEconItemDefinition() { return new CTFItemDefinition; }
|
||||
virtual CEconCraftingRecipeDefinition *CreateCraftingRecipeDefinition() { return new CTFCraftingRecipeDefinition; }
|
||||
virtual CEconStyleInfo *CreateEconStyleInfo() { return new CTFStyleInfo; }
|
||||
virtual CQuestObjectiveDefinition *CreateQuestDefinition() { return new CTFQuestObjectiveDefinition; }
|
||||
|
||||
virtual bool BCanStrangeFilterApplyToStrangeSlotInItem( uint32 /*strange_event_restriction_t*/ unRestrictionType, uint32 unRestrictionValue, const IEconItemInterface *pItem, int iStrangeSlot, uint32 *out_pOptionalScoreType ) const;
|
||||
|
||||
virtual IEconTool *CreateEconToolImpl( const char *pszToolType, const char *pszUseString, const char *pszUsageRestriction, item_capabilities_t unCapabilities, KeyValues *pUsageKV ) OVERRIDE;
|
||||
|
||||
virtual bool BInitSchema( KeyValues *pKVRawDefinition, CUtlVector<CUtlString> *pVecErrors = NULL );
|
||||
|
||||
virtual RTime32 GetCustomExpirationDate( const char *pszExpirationDate ) const OVERRIDE;
|
||||
|
||||
protected:
|
||||
#ifdef TF_CLIENT_DLL
|
||||
virtual int CalculateNumberOfConcreteItems( const CEconItemDefinition *pItemDef );
|
||||
#endif // TF_CLIENT_DLL
|
||||
|
||||
private:
|
||||
void InitializeStringTable( const char **ppStringTable, unsigned int unStringCount, CUtlVector<const char *> *out_pvecStringTable );
|
||||
|
||||
bool BInitMvmMissions( KeyValues *pKVMvmMaps, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitMvmTours( KeyValues *pKVMvmTours, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitGameModes( KeyValues *pKVMaps, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitMaps( KeyValues *pKVMaps, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitMMCategories( KeyValues *pKVCategories, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitQuestThemes( KeyValues *pKVThemes, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitQuestObjectiveConditions( KeyValues *pKVConditionsBlock, CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BObjectiveConditionsPostInit( CUtlVector<CUtlString> *pVecErrors );
|
||||
bool BInitWarDefs( KeyValues *pKVWarDefs, CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
bool BPostInitMaps( CUtlVector<CUtlString> *pVecErrors );
|
||||
|
||||
CUtlVector<const char *> m_vecClassUsabilityStrings;
|
||||
CUtlVector<const char *> m_vecClassLoadoutStrings;
|
||||
CUtlVector<const char *> m_vecClassLoadoutStringsForDisplay;
|
||||
CUtlVector<const char *> m_vecAccountLoadoutStrings;
|
||||
CUtlVector<const char *> m_vecAccountLoadoutStringsForDisplay;
|
||||
CUtlVector<const char *> m_vecWeaponTypeSubstrings;
|
||||
|
||||
CUtlVector<MvMMap_t> m_vecMvMMaps;
|
||||
CUtlVector<MvMMission_t> m_vecMvMMissions;
|
||||
CUtlVector<MvMTour_t> m_vecMvMTours;
|
||||
// Contains the list of the quest themes
|
||||
CUtlMap<const char*, CQuestThemeDefinition*, int > m_mapQuestThemes;
|
||||
CUtlMap< ObjectiveConditionDefIndex_t, CTFQuestObjectiveConditionsDefinition* > m_mapQuestObjectiveConditions;
|
||||
|
||||
CUtlVector<MapDef_t*> m_vecMasterListOfMaps;
|
||||
GameCategoryMap_t m_mapGameCategories;
|
||||
MMGroupMap_t m_mapMMGroups;
|
||||
WarDefinitionMap_t m_mapWars;
|
||||
};
|
||||
|
||||
#endif // TFITEMSCHEMA_H
|
||||
@@ -0,0 +1,41 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_item_system.h"
|
||||
#include "econ_item_inventory.h"
|
||||
#include "tf_item_inventory.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Generate the base item for a class's loadout slot
|
||||
//-----------------------------------------------------------------------------
|
||||
item_definition_index_t CTFItemSystem::GenerateBaseItem( baseitemcriteria_t *pCriteria )
|
||||
{
|
||||
Assert( pCriteria->iClass != 0 );
|
||||
Assert( pCriteria->iSlot != LOADOUT_POSITION_INVALID );
|
||||
|
||||
// Some slots don't have base items (i.e. were added after launch)
|
||||
if ( !TFInventoryManager()->SlotContainsBaseItems( GEconItemSchema().GetEquipTypeFromClassIndex( pCriteria->iClass ), pCriteria->iSlot ) )
|
||||
return INVALID_ITEM_DEF_INDEX;
|
||||
|
||||
CItemSelectionCriteria criteria;
|
||||
criteria.SetQuality( AE_NORMAL );
|
||||
criteria.SetItemLevel( 1 );
|
||||
criteria.BAddCondition( "baseitem", k_EOperator_String_EQ, "1", true );
|
||||
InventoryManager()->AddBaseItemCriteria( pCriteria, &criteria );
|
||||
int iChosenItem = GenerateRandomItem( &criteria, NULL );
|
||||
return iChosenItem;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFItemSystem *TFItemSystem( void )
|
||||
{
|
||||
CEconItemSystem *pItemSystem = ItemSystem();
|
||||
Assert( dynamic_cast<CTFItemSystem *>( pItemSystem ) != NULL );
|
||||
return (CTFItemSystem *)pItemSystem;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_ITEM_SYSTEM_H
|
||||
#define TF_ITEM_SYSTEM_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "econ_item_system.h"
|
||||
#include "tf_item_constants.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Criteria used by the system to generate a base item for a slot in a class's loadout
|
||||
struct baseitemcriteria_t
|
||||
{
|
||||
baseitemcriteria_t()
|
||||
{
|
||||
iClass = 0;
|
||||
iSlot = LOADOUT_POSITION_INVALID;
|
||||
}
|
||||
|
||||
int iClass;
|
||||
int iSlot;
|
||||
};
|
||||
|
||||
|
||||
class CTFItemSystem : public CEconItemSystem
|
||||
{
|
||||
public:
|
||||
// Select and return the base item definition index for a class's load-out slot
|
||||
virtual item_definition_index_t GenerateBaseItem( baseitemcriteria_t *pCriteria );
|
||||
};
|
||||
|
||||
CTFItemSystem *TFItemSystem( void );
|
||||
|
||||
#endif // TF_ITEM_SYSTEM_H
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
#include "econ_item_tools.h"
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//---------------------------------------------------------------------------------------
|
||||
class CEconTool_TFSpellbookPage : public IEconTool
|
||||
{
|
||||
public:
|
||||
CEconTool_TFSpellbookPage( const char *pszTypeName, item_capabilities_t unCapabilities )
|
||||
: IEconTool( pszTypeName, NULL, NULL, unCapabilities )
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool ShouldDisplayAsUseableOnItemsInArmory() const { return false; }
|
||||
|
||||
virtual void OnClientApplyTool( CEconItemView *pTool, CEconItemView *pSubject, vgui::Panel *pParent ) const;
|
||||
#endif // CLIENT_DLL
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//---------------------------------------------------------------------------------------
|
||||
class CEconTool_TFEventEnableHalloween : public IEconTool
|
||||
{
|
||||
public:
|
||||
CEconTool_TFEventEnableHalloween( const char *pszTypeName, const char *pszUseString ) : IEconTool( pszTypeName, pszUseString, NULL, ITEM_CAP_NONE ) { }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual void OnClientUseConsumable( CEconItemView *pItem, vgui::Panel *pParent ) const;
|
||||
#endif // CLIENT_DLL
|
||||
};
|
||||
@@ -0,0 +1,789 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_item_wearable.h"
|
||||
#include "vcollide_parse.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "animation.h"
|
||||
#include "basecombatweapon_shared.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "c_tf_player.h"
|
||||
#include "model_types.h"
|
||||
#include "props_shared.h"
|
||||
#include "tf_mapinfo.h"
|
||||
#else
|
||||
#include "tf_player.h"
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_wearable, CTFWearable );
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearable, DT_TFWearable )
|
||||
|
||||
// Network Table --
|
||||
BEGIN_NETWORK_TABLE( CTFWearable, DT_TFWearable )
|
||||
#if defined( GAME_DLL )
|
||||
SendPropBool( SENDINFO( m_bDisguiseWearable ) ),
|
||||
SendPropEHandle( SENDINFO( m_hWeaponAssociatedWith ) ),
|
||||
#else
|
||||
RecvPropBool( RECVINFO( m_bDisguiseWearable ) ),
|
||||
RecvPropEHandle( RECVINFO( m_hWeaponAssociatedWith ) ),
|
||||
#endif // GAME_DLL
|
||||
END_NETWORK_TABLE()
|
||||
// -- Network Table
|
||||
|
||||
// Data Desc --
|
||||
BEGIN_DATADESC( CTFWearable )
|
||||
END_DATADESC()
|
||||
// -- Data Desc
|
||||
|
||||
PRECACHE_REGISTER( tf_wearable );
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_wearable_vm, CTFWearableVM );
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFWearableVM, DT_TFWearableVM )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTFWearableVM, DT_TFWearableVM )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
PRECACHE_REGISTER( tf_wearable_vm );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SHARED CODE
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
CTFWearable::CTFWearable() : CEconWearable()
|
||||
{
|
||||
m_bDisguiseWearable = false;
|
||||
m_hWeaponAssociatedWith = NULL;
|
||||
#if defined( CLIENT_DLL )
|
||||
m_eParticleSystemVisibility = kParticleSystemVisibility_Undetermined;
|
||||
m_nWorldModelIndex = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SERVER ONLY CODE
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
void CTFWearable::Break( void )
|
||||
{
|
||||
CPVSFilter filter( GetAbsOrigin() );
|
||||
UserMessageBegin( filter, "BreakModel" );
|
||||
WRITE_SHORT( GetModelIndex() );
|
||||
WRITE_VEC3COORD( GetAbsOrigin() );
|
||||
WRITE_ANGLES( GetAbsAngles() );
|
||||
WRITE_SHORT( GetSkin() );
|
||||
MessageEnd();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CTFWearable::CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer )
|
||||
{
|
||||
if ( m_bDisguiseWearable )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer );
|
||||
if ( pTFPlayer )
|
||||
return pTFPlayer->m_Shared.GetDisguiseClass();
|
||||
}
|
||||
return BaseClass::CalculateVisibleClassFor( pPlayer );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CTFWearable::UpdateTransmitState()
|
||||
{
|
||||
return SetTransmitState( FL_EDICT_FULLCHECK );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CTFWearable::ShouldTransmit( const CCheckTransmitInfo *pInfo )
|
||||
{
|
||||
if ( pInfo->m_pClientEnt && GetOwnerEntity() && CBaseEntity::Instance( pInfo->m_pClientEnt ) == GetOwnerEntity() )
|
||||
{
|
||||
return FL_EDICT_ALWAYS;
|
||||
}
|
||||
|
||||
// We have some entities that have no model (ie., "hatless hats") but we still want
|
||||
// to transmit them down to clients so that the clients can do things like update body
|
||||
// groups, etc.
|
||||
return FL_EDICT_PVSCHECK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
ConVar tf_test_hat_bodygroup( "tf_test_hat_bodygroup", "0", 0, "For testing bodygroups on hats." );
|
||||
#endif
|
||||
|
||||
static int CalcBodyGroup( CBaseCombatCharacter* pOwner, CEconItemView *pItem, const char *pBodyGroup, codecontrolledbodygroupdata_t &ccbgd )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
if ( !Q_strnicmp( ccbgd.pFuncName, "test", ARRAYSIZE( "test" ) ) )
|
||||
{
|
||||
return tf_test_hat_bodygroup.GetInt();
|
||||
}
|
||||
else if ( !Q_strnicmp( ccbgd.pFuncName, "map_contributor", ARRAYSIZE( "map_contributor" ) ) )
|
||||
{
|
||||
int iDonationAmount = MapInfo_GetDonationAmount( pItem->GetAccountID(), engine->GetLevelName() );
|
||||
return MIN( iDonationAmount / 25, 4 );
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CLIENT ONLY CODE
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
extern ConVar tf_playergib_forceup;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Receive the BreakModel user message
|
||||
//-----------------------------------------------------------------------------
|
||||
void HandleBreakModel( bf_read &msg, bool bCheap )
|
||||
{
|
||||
int nModelIndex = (int)msg.ReadShort();
|
||||
CUtlVector<breakmodel_t> aGibs;
|
||||
BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
|
||||
if ( !aGibs.Count() )
|
||||
return;
|
||||
|
||||
// Get the origin & angles
|
||||
Vector vecOrigin;
|
||||
QAngle vecAngles;
|
||||
int nSkin = 0;
|
||||
msg.ReadBitVec3Coord( vecOrigin );
|
||||
if ( !bCheap )
|
||||
{
|
||||
msg.ReadBitAngles( vecAngles );
|
||||
nSkin = (int)msg.ReadShort();
|
||||
}
|
||||
else
|
||||
{
|
||||
vecAngles = vec3_angle;
|
||||
}
|
||||
|
||||
// Launch it straight up with some random spread
|
||||
Vector vecBreakVelocity = Vector(0,0,200);
|
||||
AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
|
||||
breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse );
|
||||
breakParams.impactEnergyScale = 1.0f;
|
||||
breakParams.nDefaultSkin = nSkin;
|
||||
|
||||
CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Receive the BreakModel user message
|
||||
//-----------------------------------------------------------------------------
|
||||
void __MsgFunc_BreakModel( bf_read &msg )
|
||||
{
|
||||
HandleBreakModel( msg, false );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Receive the CheapBreakModel user message
|
||||
//-----------------------------------------------------------------------------
|
||||
void __MsgFunc_CheapBreakModel( bf_read &msg )
|
||||
{
|
||||
HandleBreakModel( msg, true );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Receive the BreakModel_Pumpkin user message
|
||||
//-----------------------------------------------------------------------------
|
||||
void __MsgFunc_BreakModel_Pumpkin( bf_read &msg )
|
||||
{
|
||||
int nModelIndex = (int)msg.ReadShort();
|
||||
CUtlVector<breakmodel_t> aGibs;
|
||||
BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
|
||||
if ( !aGibs.Count() )
|
||||
return;
|
||||
|
||||
// Get the origin & angles
|
||||
Vector vecOrigin;
|
||||
QAngle vecAngles;
|
||||
msg.ReadBitVec3Coord( vecOrigin );
|
||||
msg.ReadBitAngles( vecAngles );
|
||||
|
||||
// Launch it straight up with some random spread
|
||||
Vector vecBreakVelocity = Vector(0,0,0);
|
||||
AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
|
||||
breakablepropparams_t breakParams( vecOrigin /*+ Vector(0,0,20)*/, vecAngles, vecBreakVelocity, angularImpulse );
|
||||
breakParams.impactEnergyScale = 1.0f;
|
||||
|
||||
for ( int i=0; i<aGibs.Count(); ++i )
|
||||
{
|
||||
aGibs[i].burstScale = 1000.f;
|
||||
}
|
||||
|
||||
CUtlVector<EHANDLE> hSpawnedGibs;
|
||||
CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true, &hSpawnedGibs );
|
||||
|
||||
// Make the base stay low to the ground.
|
||||
for ( int i=0; i<hSpawnedGibs.Count(); ++i )
|
||||
{
|
||||
CBaseEntity *pGib = hSpawnedGibs[i];
|
||||
if ( pGib )
|
||||
{
|
||||
IPhysicsObject *pPhysObj = pGib->VPhysicsGetObject();
|
||||
if ( pPhysObj )
|
||||
{
|
||||
Vector vecVel;
|
||||
AngularImpulse angImp;
|
||||
pPhysObj->GetVelocity( &vecVel, &angImp );
|
||||
vecVel *= 3.0;
|
||||
if ( i == 3 )
|
||||
{
|
||||
vecVel.z = 300;
|
||||
}
|
||||
else
|
||||
{
|
||||
vecVel.z = 400;
|
||||
}
|
||||
pPhysObj->SetVelocity( &vecVel, &angImp );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
int CTFWearable::InternalDrawModel( int flags )
|
||||
{
|
||||
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||||
|
||||
if ( pOwner && pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
|
||||
{
|
||||
bool bShouldDraw = false;
|
||||
const CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem )
|
||||
{
|
||||
econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( "ghost_wearable" );
|
||||
if ( pItem->GetItemDefinition()->HasEconTag( tagHandle ) )
|
||||
bShouldDraw = true;
|
||||
}
|
||||
|
||||
if ( !bShouldDraw )
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bUseInvulnMaterial = ( pOwner && pOwner->m_Shared.IsInvulnerable() &&
|
||||
( !pOwner->m_Shared.InCond( TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED ) || gpGlobals->curtime < pOwner->GetLastDamageTime() + 2.0f ) );
|
||||
|
||||
if ( bUseInvulnMaterial && (flags & STUDIO_RENDER) )
|
||||
{
|
||||
modelrender->ForcedMaterialOverride( *pOwner->GetInvulnMaterialRef() );
|
||||
}
|
||||
|
||||
int ret = BaseClass::InternalDrawModel( flags );
|
||||
|
||||
if ( bUseInvulnMaterial && (flags & STUDIO_RENDER) )
|
||||
{
|
||||
modelrender->ForcedMaterialOverride( NULL );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFWearable::ShouldDraw()
|
||||
{
|
||||
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||||
|
||||
if ( pOwner )
|
||||
{
|
||||
if ( pOwner->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) )
|
||||
{
|
||||
const CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem )
|
||||
{
|
||||
econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( "ghost_wearable" );
|
||||
if ( pItem->GetItemDefinition()->HasEconTag( tagHandle ) )
|
||||
return BaseClass::ShouldDraw();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't draw cosmetic while sniper is zoom
|
||||
if ( pOwner == C_TFPlayer::GetLocalTFPlayer() && pOwner->m_Shared.InCond( TF_COND_ZOOMED ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't draw 3rd person wearables if our owner is disguised.
|
||||
if ( pOwner && pOwner->m_Shared.InCond( TF_COND_DISGUISED ) && !IsViewModelWearable() )
|
||||
{
|
||||
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( m_bDisguiseWearable && pLocalPlayer )
|
||||
{
|
||||
int iLocalPlayerTeam = pLocalPlayer->GetTeamNumber();
|
||||
if ( pLocalPlayer->m_bIsCoaching && pLocalPlayer->m_hStudent )
|
||||
{
|
||||
iLocalPlayerTeam = pLocalPlayer->m_hStudent->GetTeamNumber();
|
||||
}
|
||||
|
||||
// This wearable is a part of our disguise -- we might want to draw it.
|
||||
if ( GetEnemyTeam( pOwner->GetTeamNumber() ) != iLocalPlayerTeam )
|
||||
{
|
||||
// The local player is on this spy's team. We don't see the disguise.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pOwner->m_Shared.GetDisguiseClass() == TF_CLASS_SPY &&
|
||||
pOwner->m_Shared.GetDisguiseTeam() == iLocalPlayerTeam )
|
||||
{
|
||||
// This enemy spy is disguised as a spy on our team, don't draw wearables.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The local player is an enemy. Show the disguise wearable.
|
||||
return BaseClass::ShouldDraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if the visibility is controlled by a weapon.
|
||||
CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( GetWeaponAssociatedWith() );
|
||||
if ( pWeapon )
|
||||
{
|
||||
// If the weapon isn't active, don't draw
|
||||
if ( pOwner && pOwner->GetActiveWeapon() != pWeapon )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !IsViewModelWearable() )
|
||||
{
|
||||
// If it's the 3rd person wearable, don't draw it when the weapon is hidden
|
||||
if ( !pWeapon->ShouldDraw() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the weapon is being repurposed for a taunt dont draw.
|
||||
// The Brutal Legend taunt changes your weapon's model to be the guitar,
|
||||
// but we dont want things like bot-killer skulls or festive lights
|
||||
// to continue to draw
|
||||
if( pWeapon->IsBeingRepurposedForTaunt() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseClass::ShouldDraw();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFWearable::ShouldDrawParticleSystems( void )
|
||||
{
|
||||
if ( !BaseClass::ShouldDrawParticleSystems() )
|
||||
return false;
|
||||
|
||||
C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
|
||||
bool bStealthed = pPlayer->m_Shared.IsStealthed();
|
||||
|
||||
// If we're disguised, this ought to only be getting called on disguise wearables,
|
||||
// otherwise we could get two particles showing at once (disguise wearable + real wearable).
|
||||
Assert( !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) || IsDisguiseWearable() );
|
||||
|
||||
if ( bStealthed )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_eParticleSystemVisibility == kParticleSystemVisibility_Undetermined )
|
||||
{
|
||||
static CSchemaItemDefHandle pItemDef_MapLoverHat( "World Traveler" );
|
||||
|
||||
m_eParticleSystemVisibility = kParticleSystemVisibility_Shown;
|
||||
|
||||
const CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem && pItem->GetStaticData() == pItemDef_MapLoverHat )
|
||||
{
|
||||
if ( MapInfo_DidPlayerDonate( pItem->GetAccountID(), engine->GetLevelName() ) == false )
|
||||
{
|
||||
m_eParticleSystemVisibility = kParticleSystemVisibility_Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_eParticleSystemVisibility == kParticleSystemVisibility_Shown;
|
||||
}
|
||||
|
||||
int CTFWearable::GetWorldModelIndex( void )
|
||||
{
|
||||
if ( m_nWorldModelIndex == 0 )
|
||||
return m_nModelIndex;
|
||||
|
||||
static CSchemaItemDefHandle pItemDef_OculusRiftHeadset( "The TF2VRH" );
|
||||
const CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem && pItem->GetStaticData() == pItemDef_OculusRiftHeadset )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( pTFPlayer )
|
||||
{
|
||||
if ( pTFPlayer->IsUsingVRHeadset() && pTFPlayer->GetPlayerClass() )
|
||||
{
|
||||
const char *pszReplacementModel = pItem->GetStaticData()->GetPlayerDisplayModelAlt( pTFPlayer->GetPlayerClass()->GetClassIndex() );
|
||||
if ( pszReplacementModel && pszReplacementModel[0] )
|
||||
{
|
||||
return modelinfo->GetModelIndex( pszReplacementModel );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//*********************************************************************************
|
||||
// Parachute states
|
||||
static CSchemaItemDefHandle pItemDef_BaseJumper( "The B.A.S.E. Jumper" );
|
||||
const int iParachuteOpen = modelinfo->GetModelIndex( "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack_open.mdl" );
|
||||
const int iParachuteClosed = modelinfo->GetModelIndex( "models/workshop/weapons/c_models/c_paratooper_pack/c_paratrooper_pack.mdl" );
|
||||
if ( m_nModelIndex == iParachuteOpen || m_nModelIndex == iParachuteClosed )
|
||||
{
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( pTFPlayer )
|
||||
{
|
||||
if ( pTFPlayer->m_Shared.InCond( TF_COND_PARACHUTE_DEPLOYED ) )
|
||||
{
|
||||
return iParachuteOpen;
|
||||
}
|
||||
else
|
||||
{
|
||||
return iParachuteClosed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( GameRules() )
|
||||
{
|
||||
const char *pBaseName = modelinfo->GetModelName( modelinfo->GetModel( m_nWorldModelIndex ) );
|
||||
const char *pTranslatedName = GameRules()->TranslateEffectForVisionFilter( "weapons", pBaseName );
|
||||
|
||||
if ( pTranslatedName != pBaseName )
|
||||
{
|
||||
return modelinfo->GetModelIndex( pTranslatedName );
|
||||
}
|
||||
}
|
||||
|
||||
return m_nWorldModelIndex;
|
||||
}
|
||||
|
||||
void CTFWearable::ValidateModelIndex( void )
|
||||
{
|
||||
m_nModelIndex = GetWorldModelIndex();
|
||||
|
||||
BaseClass::ValidateModelIndex();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Hides or shows masked bodygroups associated with this item.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFWearable::UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState )
|
||||
{
|
||||
CTFPlayer *pTFOwner = ToTFPlayer( pOwner );
|
||||
if ( !pTFOwner )
|
||||
return false;
|
||||
|
||||
bool bBaseUpdate = BaseClass::UpdateBodygroups( pOwner, iState );
|
||||
if ( bBaseUpdate && m_bDisguiseWearable )
|
||||
{
|
||||
CEconItemView *pItem = GetAttributeContainer()->GetItem(); // Safe. Checked in base class call.
|
||||
|
||||
CTFPlayer *pDisguiseTarget = ToTFPlayer( pTFOwner->m_Shared.GetDisguiseTarget() );
|
||||
if ( !pDisguiseTarget )
|
||||
return false;
|
||||
|
||||
// Update our disguise bodygroup.
|
||||
int iDisguiseBody = pTFOwner->m_Shared.GetDisguiseBody();
|
||||
int iTeam = pTFOwner->m_Shared.GetDisguiseTeam();
|
||||
int iNumBodyGroups = pItem->GetStaticData()->GetNumModifiedBodyGroups( iTeam );
|
||||
for ( int i=0; i<iNumBodyGroups; ++i )
|
||||
{
|
||||
int iBody = 0;
|
||||
const char *pszBodyGroup = pItem->GetStaticData()->GetModifiedBodyGroup( iTeam, i, iBody );
|
||||
int iBodyGroup = pDisguiseTarget->FindBodygroupByName( pszBodyGroup );
|
||||
|
||||
if ( iBodyGroup == -1 )
|
||||
continue;
|
||||
|
||||
::SetBodygroup( pDisguiseTarget->GetModelPtr(), iDisguiseBody, iBodyGroup, iState );
|
||||
}
|
||||
|
||||
pTFOwner->m_Shared.SetDisguiseBody( iDisguiseBody );
|
||||
}
|
||||
|
||||
CEconItemView *pItem = GetAttributeContainer() ? GetAttributeContainer()->GetItem() : NULL;
|
||||
if ( pItem )
|
||||
{
|
||||
int iTeam = pTFOwner->GetTeamNumber();
|
||||
int iNumBodyGroups = pItem->GetStaticData()->GetNumCodeControlledBodyGroups( iTeam );
|
||||
for ( int i=0; i<iNumBodyGroups; ++i )
|
||||
{
|
||||
codecontrolledbodygroupdata_t ccbgd = { NULL, NULL };
|
||||
const char *pszBodyGroup = pItem->GetStaticData()->GetCodeControlledBodyGroup( iTeam, i, ccbgd );
|
||||
int iBodyGroup = FindBodygroupByName( pszBodyGroup );
|
||||
if ( iBodyGroup != -1 )
|
||||
{
|
||||
SetBodygroup( iBodyGroup, CalcBodyGroup( pOwner, pItem, pszBodyGroup, ccbgd ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional hidden bodygroups.
|
||||
for ( int i=0; i<m_HiddenBodyGroups.Count(); ++i )
|
||||
{
|
||||
int iBodyGroup = pOwner->FindBodygroupByName( m_HiddenBodyGroups[i] );
|
||||
if ( iBodyGroup == -1 )
|
||||
continue;
|
||||
pOwner->SetBodygroup( iBodyGroup, iState );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CTFWearable::GetSkin()
|
||||
{
|
||||
CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
|
||||
if ( !pPlayer )
|
||||
return 0;
|
||||
|
||||
int iTeamNumber = pPlayer->GetTeamNumber();
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
// Run client-only "is the viewer on the same team as the wielder" logic. Assumed to
|
||||
// always be false on the server.
|
||||
CTFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
if ( !pLocalPlayer )
|
||||
return 0;
|
||||
|
||||
int iLocalTeam = pLocalPlayer->GetTeamNumber();
|
||||
|
||||
// We only show disguise weapon to the enemy team when owner is disguised
|
||||
bool bUseDisguiseWeapon = ( iTeamNumber != iLocalTeam && iLocalTeam > LAST_SHARED_TEAM );
|
||||
|
||||
if ( bUseDisguiseWeapon && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
|
||||
{
|
||||
if ( pLocalPlayer != pPlayer )
|
||||
{
|
||||
iTeamNumber = pPlayer->m_Shared.GetDisguiseTeam();
|
||||
}
|
||||
}
|
||||
#endif // defined( CLIENT_DLL )
|
||||
|
||||
// See if the item wants to override the skin
|
||||
int nSkin = -1;
|
||||
|
||||
CBaseCombatWeapon *pWeapon = assert_cast< CBaseCombatWeapon* >( GetWeaponAssociatedWith() );
|
||||
if ( pWeapon )
|
||||
{
|
||||
CEconItemView *pItem = pWeapon->GetAttributeContainer()->GetItem();
|
||||
if ( pItem->IsValid() )
|
||||
{
|
||||
nSkin = pItem->GetSkin( iTeamNumber ); // if we didn't have custom code, fall back to the item definition
|
||||
}
|
||||
}
|
||||
|
||||
if ( nSkin != -1 )
|
||||
{
|
||||
return nSkin;
|
||||
}
|
||||
|
||||
return BaseClass::GetSkin();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::InternalSetPlayerDisplayModel( void )
|
||||
{
|
||||
// Set our model to the player model
|
||||
CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem && pItem->IsValid() && pItem->GetStaticData() )
|
||||
{
|
||||
if ( pItem->GetStaticData()->IsContentStreamable() )
|
||||
{
|
||||
const char *pszPlayerDisplayModelAlt = pItem->GetStaticData()->GetPlayerDisplayModelAlt();
|
||||
if ( pszPlayerDisplayModelAlt && pszPlayerDisplayModelAlt[0] )
|
||||
{
|
||||
modelinfo->RegisterDynamicModel( pszPlayerDisplayModelAlt, IsClient() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseClass::InternalSetPlayerDisplayModel();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::AddHiddenBodyGroup( const char* bodygroup )
|
||||
{
|
||||
m_HiddenBodyGroups.AddToHead( bodygroup );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::ReapplyProvision( void )
|
||||
{
|
||||
// Disguise wearables never provide
|
||||
if ( IsDisguiseWearable() )
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
UpdateModelToClass();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::ReapplyProvision();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Attaches the item to the player.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::Equip( CBasePlayer* pOwner )
|
||||
{
|
||||
BaseClass::Equip( pOwner );
|
||||
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pOwner );
|
||||
if ( !pTFPlayer )
|
||||
return;
|
||||
|
||||
int iTeamNumber = pTFPlayer->GetTeamNumber();
|
||||
if ( m_bDisguiseWearable )
|
||||
{
|
||||
iTeamNumber = pTFPlayer->m_Shared.GetDisguiseTeam();
|
||||
}
|
||||
ChangeTeam( iTeamNumber );
|
||||
m_nSkin = ( iTeamNumber == (LAST_SHARED_TEAM+1) ) ? 0 : 1;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
pTFPlayer->SetBodygroupsDirty();
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// Reapply upgrades for wearables upon equip
|
||||
CEconItemView *pItem = ( (CTFWearable *)this )->GetAttributeContainer()->GetItem();
|
||||
if ( pTFPlayer && pItem->IsValid() )
|
||||
{
|
||||
pTFPlayer->ReapplyItemUpgrades( pItem );
|
||||
}
|
||||
#endif // GAME_DLL
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Attaches the item to the player.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::UnEquip( CBasePlayer* pOwner )
|
||||
{
|
||||
BaseClass::UnEquip( pOwner );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
CTFPlayer *pTFPlayer = ToTFPlayer( pOwner );
|
||||
if ( pTFPlayer )
|
||||
{
|
||||
pTFPlayer->SetBodygroupsDirty();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Check for any TF specific restrictions on item use.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CTFWearable::CanEquip( CBaseEntity *pOther )
|
||||
{
|
||||
CEconItemView *pItem = GetAttributeContainer()->GetItem();
|
||||
if ( pItem && TFGameRules() )
|
||||
{
|
||||
CEconItemDefinition* pData = pItem->GetStaticData();
|
||||
if ( pData && pData->GetHolidayRestriction() )
|
||||
{
|
||||
int iHolidayRestriction = UTIL_GetHolidayForString( pData->GetHolidayRestriction() );
|
||||
if ( iHolidayRestriction != kHoliday_None && !TFGameRules()->IsHolidayActive( iHolidayRestriction ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::OnDataChanged( DataUpdateType_t updateType )
|
||||
{
|
||||
BaseClass::OnDataChanged( updateType );
|
||||
|
||||
if ( updateType == DATA_UPDATE_CREATED )
|
||||
{
|
||||
ListenForGameEvent( "localplayer_changeteam" );
|
||||
|
||||
m_nWorldModelIndex = m_nModelIndex;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFWearable::FireGameEvent( IGameEvent *event )
|
||||
{
|
||||
const char *pszEventName = event->GetName();
|
||||
if ( Q_strcmp( pszEventName, "localplayer_changeteam" ) == 0 )
|
||||
{
|
||||
UpdateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Choose shadow type for VM-wearables.
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined( CLIENT_DLL )
|
||||
ShadowType_t CTFWearableVM::ShadowCastType( void )
|
||||
{
|
||||
if ( ToTFPlayer(GetMoveParent())->ShouldDrawThisPlayer() )
|
||||
{
|
||||
// Using the viewmodel.
|
||||
return SHADOWS_NONE;
|
||||
}
|
||||
|
||||
return SHADOWS_RENDER_TO_TEXTURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_WEARABLE_H
|
||||
#define TF_WEARABLE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "econ_wearable.h"
|
||||
#include "props_shared.h"
|
||||
#include "GameEventListener.h"
|
||||
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
#define CTFWearable C_TFWearable
|
||||
#define CTFWearableVM C_TFWearableVM
|
||||
#endif
|
||||
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
class CTFWearable : public CEconWearable, public CGameEventListener
|
||||
#else
|
||||
class CTFWearable : public CEconWearable
|
||||
#endif
|
||||
{
|
||||
DECLARE_CLASS( CTFWearable, CEconWearable );
|
||||
public:
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_DATADESC();
|
||||
|
||||
CTFWearable();
|
||||
|
||||
virtual void Equip( CBasePlayer* pOwner );
|
||||
virtual void UnEquip( CBasePlayer* pOwner );
|
||||
virtual bool CanEquip( CBaseEntity *pOther );
|
||||
void SetDisguiseWearable( bool bState ) { m_bDisguiseWearable = bState; }
|
||||
bool IsDisguiseWearable( void ) const { return m_bDisguiseWearable; }
|
||||
void SetWeaponAssociatedWith( CBaseEntity *pWeapon ) { m_hWeaponAssociatedWith = pWeapon; }
|
||||
CBaseEntity* GetWeaponAssociatedWith( void ) const { return m_hWeaponAssociatedWith.Get(); }
|
||||
virtual bool UpdateBodygroups( CBaseCombatCharacter* pOwner, int iState );
|
||||
virtual void ReapplyProvision( void );
|
||||
|
||||
#if defined( GAME_DLL )
|
||||
void Break( void );
|
||||
virtual int CalculateVisibleClassFor( CBaseCombatCharacter *pPlayer );
|
||||
virtual int UpdateTransmitState();
|
||||
virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
|
||||
|
||||
int GetKillStreak ( ) { return m_iKillStreak; }
|
||||
void SetKillStreak ( int value ) { m_iKillStreak = value; };
|
||||
#endif
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
virtual int InternalDrawModel( int flags );
|
||||
virtual bool ShouldDraw();
|
||||
virtual bool ShouldDrawWhenPlayerIsDead() { return ( GetWeaponAssociatedWith() == NULL ); }
|
||||
virtual bool ShouldDrawParticleSystems( void ); // can't be const because it potentially mutates m_eParticleSystemVisibility state
|
||||
virtual int GetWorldModelIndex( void );
|
||||
virtual void ValidateModelIndex( void );
|
||||
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType );
|
||||
virtual void FireGameEvent( IGameEvent *event );
|
||||
#endif
|
||||
|
||||
virtual int GetSkin( void );
|
||||
|
||||
void AddHiddenBodyGroup( const char* bodygroup );
|
||||
|
||||
protected:
|
||||
virtual void InternalSetPlayerDisplayModel( void );
|
||||
|
||||
private:
|
||||
CNetworkVar( bool, m_bDisguiseWearable );
|
||||
CNetworkHandle( CBaseEntity, m_hWeaponAssociatedWith );
|
||||
|
||||
CUtlVector< const char* > m_HiddenBodyGroups;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
int m_iKillStreak;
|
||||
#endif // GAME_DLL
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
enum eParticleSystemVisibility
|
||||
{
|
||||
kParticleSystemVisibility_Undetermined,
|
||||
kParticleSystemVisibility_Shown,
|
||||
kParticleSystemVisibility_Hidden,
|
||||
};
|
||||
eParticleSystemVisibility m_eParticleSystemVisibility;
|
||||
|
||||
short m_nWorldModelIndex;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
class CTFWearableVM : public CTFWearable
|
||||
{
|
||||
DECLARE_CLASS( CTFWearableVM, CTFWearable );
|
||||
public:
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
virtual bool IsViewModelWearable( void ) { return true; }
|
||||
|
||||
#if defined( CLIENT_DLL )
|
||||
virtual ShadowType_t ShadowCastType( void );
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // TF_WEARABLE_H
|
||||
@@ -0,0 +1,519 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_ladder_data.h"
|
||||
#include "gcsdk/enumutils.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "econ/confirm_dialog.h"
|
||||
#include "tf_matchmaking_shared.h"
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Get player's ladder stat data by steamID. Returns nullptr if it doesn't exist
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFLadderData *YieldingGetPlayerLadderDataBySteamID( const CSteamID &steamID, EMatchGroup nMatchGroup )
|
||||
{
|
||||
#ifdef GC_DLL
|
||||
GCSDK::CGCSharedObjectCache *pSOCache = GGCEcon()->YieldingFindOrLoadSOCache( steamID );
|
||||
#else
|
||||
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
|
||||
#endif
|
||||
if ( pSOCache )
|
||||
{
|
||||
auto *pTypeCache = pSOCache->FindTypeCache( CSOTFLadderData::k_nTypeID );
|
||||
if ( pTypeCache )
|
||||
{
|
||||
for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
|
||||
{
|
||||
CSOTFLadderData *pLadderData = (CSOTFLadderData*)pTypeCache->GetObject( i );
|
||||
if ( nMatchGroup == (EMatchGroup)pLadderData->Obj().match_group() )
|
||||
{
|
||||
return pLadderData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if !defined( GC )
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Get the local player's Ladder Data. Returns NULL if it doesn't exist (no GC)
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFLadderData *GetLocalPlayerLadderData( EMatchGroup nMatchGroup )
|
||||
{
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
return YieldingGetPlayerLadderDataBySteamID( steamapicontext->SteamUser()->GetSteamID(), nMatchGroup );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif // !GC
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFLadderData::CSOTFLadderData()
|
||||
{
|
||||
Obj().set_account_id( 0 );
|
||||
Obj().set_match_group( k_nMatchGroup_Invalid );
|
||||
Obj().set_season_id( 1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFLadderData::CSOTFLadderData( uint32 unAccountID, EMatchGroup eMatchGroup )
|
||||
{
|
||||
Obj().set_account_id( unAccountID );
|
||||
Obj().set_match_group( eMatchGroup );
|
||||
Obj().set_season_id( 1 );
|
||||
}
|
||||
#ifdef GC
|
||||
|
||||
IMPLEMENT_CLASS_MEMPOOL( CSOTFLadderData, 1000, UTLMEMORYPOOL_GROW_SLOW );
|
||||
IMPLEMENT_CLASS_MEMPOOL( CSOTFMatchResultPlayerInfo, 1000, UTLMEMORYPOOL_GROW_SLOW );
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
bool CSOTFLadderData::BIsKeyLess( const CSharedObject & soRHS ) const
|
||||
{
|
||||
Assert( GetTypeID() == soRHS.GetTypeID() );
|
||||
const CSOTFLadderPlayerStats &obj = Obj();
|
||||
const CSOTFLadderPlayerStats &rhs = ( static_cast< const CSOTFLadderData & >( soRHS ) ).Obj();
|
||||
|
||||
if ( obj.account_id() < rhs.account_id() ) return true;
|
||||
if ( obj.account_id() > rhs.account_id() ) return false;
|
||||
if ( obj.match_group() < rhs.match_group() ) return true;
|
||||
if ( obj.match_group() > rhs.match_group() ) return false;
|
||||
|
||||
return obj.season_id() < rhs.season_id();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFLadderData::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchLadderData schLadderData;
|
||||
WriteToRecord( &schLadderData );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schLadderData );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFLadderData::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
|
||||
{
|
||||
CSchLadderData schLadderData;
|
||||
WriteToRecord( &schLadderData );
|
||||
CColumnSet csDatabaseDirty( schLadderData.GetPSchema()->GetRecordInfo() );
|
||||
csDatabaseDirty.MakeEmpty();
|
||||
FOR_EACH_VEC( fields, nField )
|
||||
{
|
||||
switch ( fields[nField] )
|
||||
{
|
||||
case CSOTFLadderPlayerStats::kAccountIdFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unAccountID ); break;
|
||||
case CSOTFLadderPlayerStats::kMatchGroupFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_nMatchGroup ); break;
|
||||
case CSOTFLadderPlayerStats::kSeasonIdFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unSeasonID ); break;
|
||||
|
||||
case CSOTFLadderPlayerStats::kRankFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unRank ); break;
|
||||
case CSOTFLadderPlayerStats::kHighestRankFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unHighestRank ); break;
|
||||
case CSOTFLadderPlayerStats::kExperienceFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unExperience ); break;
|
||||
case CSOTFLadderPlayerStats::kLastAckdExperienceFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unLastAckdExperience ); break;
|
||||
|
||||
case CSOTFLadderPlayerStats::kGamesFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unGames ); break;
|
||||
case CSOTFLadderPlayerStats::kScoreFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unScore ); break;
|
||||
case CSOTFLadderPlayerStats::kKillsFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unKills ); break;
|
||||
case CSOTFLadderPlayerStats::kDeathsFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unDeaths ); break;
|
||||
case CSOTFLadderPlayerStats::kDamageFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unDamage ); break;
|
||||
case CSOTFLadderPlayerStats::kHealingFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unHealing ); break;
|
||||
case CSOTFLadderPlayerStats::kSupportFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unSupport ); break;
|
||||
|
||||
case CSOTFLadderPlayerStats::kScoreBronzeFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unScoreBronze ); break;
|
||||
case CSOTFLadderPlayerStats::kScoreSilverFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unScoreSilver ); break;
|
||||
case CSOTFLadderPlayerStats::kScoreGoldFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unScoreGold ); break;
|
||||
case CSOTFLadderPlayerStats::kKillsBronzeFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unKillsBronze ); break;
|
||||
case CSOTFLadderPlayerStats::kKillsSilverFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unKillsSilver ); break;
|
||||
case CSOTFLadderPlayerStats::kKillsGoldFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unKillsGold ); break;
|
||||
case CSOTFLadderPlayerStats::kDamageBronzeFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unDamageBronze ); break;
|
||||
case CSOTFLadderPlayerStats::kDamageSilverFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unDamageSilver ); break;
|
||||
case CSOTFLadderPlayerStats::kDamageGoldFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unDamageGold ); break;
|
||||
case CSOTFLadderPlayerStats::kHealingBronzeFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unHealingBronze ); break;
|
||||
case CSOTFLadderPlayerStats::kHealingSilverFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unHealingSilver ); break;
|
||||
case CSOTFLadderPlayerStats::kHealingGoldFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unHealingGold ); break;
|
||||
case CSOTFLadderPlayerStats::kSupportBronzeFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unSupportBronze ); break;
|
||||
case CSOTFLadderPlayerStats::kSupportSilverFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unSupportSilver ); break;
|
||||
case CSOTFLadderPlayerStats::kSupportGoldFieldNumber: csDatabaseDirty.BAddColumn( CSchLadderData::k_iField_unSupportGold ); break;
|
||||
|
||||
default:
|
||||
Assert( false );
|
||||
}
|
||||
}
|
||||
return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schLadderData, csDatabaseDirty );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFLadderData::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchLadderData schLadderData;
|
||||
WriteToRecord( &schLadderData );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schLadderData );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTFLadderData::WriteToRecord( CSchLadderData *pLadderData ) const
|
||||
{
|
||||
pLadderData->m_unAccountID = Obj().account_id();
|
||||
|
||||
pLadderData->m_nMatchGroup = (int16)Obj().match_group();
|
||||
pLadderData->m_unSeasonID = (uint16)Obj().season_id();
|
||||
|
||||
pLadderData->m_unRank = (uint16)Obj().rank();
|
||||
pLadderData->m_unHighestRank = (uint16)Obj().highest_rank();
|
||||
pLadderData->m_unExperience = Obj().experience();
|
||||
pLadderData->m_unLastAckdExperience = Obj().last_ackd_experience();
|
||||
|
||||
pLadderData->m_unGames = Obj().games();
|
||||
pLadderData->m_unScore = Obj().score();
|
||||
pLadderData->m_unKills = Obj().kills();
|
||||
pLadderData->m_unDeaths = Obj().deaths();
|
||||
pLadderData->m_unDamage = Obj().damage();
|
||||
pLadderData->m_unHealing = Obj().healing();
|
||||
pLadderData->m_unSupport = Obj().support();
|
||||
|
||||
pLadderData->m_unScoreBronze = (uint16)Obj().score_bronze();
|
||||
pLadderData->m_unScoreSilver = (uint16)Obj().score_silver();
|
||||
pLadderData->m_unScoreGold = (uint16)Obj().score_gold();
|
||||
pLadderData->m_unKillsBronze = (uint16)Obj().score_bronze();
|
||||
pLadderData->m_unKillsSilver = (uint16)Obj().score_silver();
|
||||
pLadderData->m_unKillsGold = (uint16)Obj().score_gold();
|
||||
pLadderData->m_unDamageBronze = (uint16)Obj().damage_bronze();
|
||||
pLadderData->m_unDamageSilver = (uint16)Obj().damage_silver();
|
||||
pLadderData->m_unDamageGold = (uint16)Obj().damage_gold();
|
||||
pLadderData->m_unHealingBronze = (uint16)Obj().healing_bronze();
|
||||
pLadderData->m_unHealingSilver = (uint16)Obj().healing_silver();
|
||||
pLadderData->m_unHealingGold = (uint16)Obj().healing_gold();
|
||||
pLadderData->m_unSupportBronze = (uint16)Obj().support_bronze();
|
||||
pLadderData->m_unSupportSilver = (uint16)Obj().support_silver();
|
||||
pLadderData->m_unSupportGold = (uint16)Obj().support_gold();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTFLadderData::ReadFromRecord( const CSchLadderData &ladderData )
|
||||
{
|
||||
Obj().set_account_id( ladderData.m_unAccountID );
|
||||
Obj().set_match_group( ladderData.m_nMatchGroup );
|
||||
Obj().set_season_id( 1 ); // TODO: GetSeasonID()
|
||||
|
||||
Obj().set_rank( ladderData.m_unRank );
|
||||
Obj().set_highest_rank( ladderData.m_unHighestRank );
|
||||
Obj().set_experience( ladderData.m_unExperience );
|
||||
Obj().set_last_ackd_experience( ladderData.m_unLastAckdExperience );
|
||||
|
||||
Obj().set_games( ladderData.m_unGames );
|
||||
Obj().set_score( ladderData.m_unScore );
|
||||
Obj().set_kills( ladderData.m_unKills );
|
||||
Obj().set_deaths( ladderData.m_unDeaths );
|
||||
Obj().set_damage( ladderData.m_unDamage );
|
||||
Obj().set_healing( ladderData.m_unHealing );
|
||||
Obj().set_support( ladderData.m_unSupport );
|
||||
|
||||
Obj().set_score_bronze( ladderData.m_unScoreBronze );
|
||||
Obj().set_score_silver( ladderData.m_unScoreSilver );
|
||||
Obj().set_score_gold( ladderData.m_unScoreGold );
|
||||
Obj().set_kills_bronze( ladderData.m_unKillsBronze );
|
||||
Obj().set_kills_silver( ladderData.m_unKillsSilver );
|
||||
Obj().set_kills_gold( ladderData.m_unKillsGold );
|
||||
Obj().set_damage_bronze( ladderData.m_unDamageBronze );
|
||||
Obj().set_damage_silver( ladderData.m_unDamageSilver);
|
||||
Obj().set_damage_gold( ladderData.m_unDamageGold );
|
||||
Obj().set_healing_bronze( ladderData.m_unHealingBronze );
|
||||
Obj().set_healing_silver( ladderData.m_unHealingSilver );
|
||||
Obj().set_healing_gold( ladderData.m_unHealingGold );
|
||||
Obj().set_support_bronze( ladderData.m_unSupportBronze );
|
||||
Obj().set_support_silver( ladderData.m_unSupportSilver );
|
||||
Obj().set_support_gold( ladderData.m_unSupportGold );
|
||||
}
|
||||
#endif // GC
|
||||
|
||||
#if !defined( GC )
|
||||
void GetLocalPlayerMatchHistory( EMatchGroup nMatchGroup, CUtlVector < CSOTFMatchResultPlayerStats > &vecMatchesOut )
|
||||
{
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
|
||||
if ( pSOCache )
|
||||
{
|
||||
GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CSOTFMatchResultPlayerInfo::k_nTypeID );
|
||||
if ( pTypeCache )
|
||||
{
|
||||
for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
|
||||
{
|
||||
CSOTFMatchResultPlayerInfo *pMatchStats = (CSOTFMatchResultPlayerInfo*)pTypeCache->GetObject( i );
|
||||
if ( nMatchGroup == (EMatchGroup)pMatchStats->Obj().match_group() )
|
||||
{
|
||||
vecMatchesOut.AddToTail( pMatchStats->Obj() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !GC
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFMatchResultPlayerInfo::CSOTFMatchResultPlayerInfo()
|
||||
{
|
||||
Obj().set_account_id( 0 );
|
||||
}
|
||||
|
||||
#ifdef GC
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CSOTFMatchResultPlayerInfo::CSOTFMatchResultPlayerInfo( uint32 unAccountID )
|
||||
{
|
||||
Obj().set_match_id( 0 );
|
||||
Obj().set_account_id( unAccountID );
|
||||
Obj().set_match_group( k_nMatchGroup_Invalid );
|
||||
Obj().set_endtime( 0 );
|
||||
Obj().set_season_id( 1 );
|
||||
Obj().set_status( 0 );
|
||||
|
||||
Obj().set_party_id( 0 );
|
||||
Obj().set_team( 0 );
|
||||
Obj().set_score( 0 );
|
||||
Obj().set_ping( 0 );
|
||||
Obj().set_flags( 0 );
|
||||
Obj().set_display_rating( 0 );
|
||||
Obj().set_display_rating_change( 0 );
|
||||
Obj().set_rank( 0 );
|
||||
Obj().set_classes_played( 0 );
|
||||
|
||||
Obj().set_kills( 0 );
|
||||
Obj().set_deaths( 0 );
|
||||
Obj().set_damage( 0 );
|
||||
Obj().set_healing( 0 );
|
||||
Obj().set_support( 0 );
|
||||
|
||||
Obj().set_score_medal( 0 );
|
||||
Obj().set_kills_medal( 0 );
|
||||
Obj().set_damage_medal( 0 );
|
||||
Obj().set_healing_medal( 0 );
|
||||
Obj().set_support_medal( 0 );
|
||||
|
||||
Obj().set_map_index( 0 );
|
||||
}
|
||||
|
||||
bool CSOTFMatchResultPlayerInfo::BIsKeyLess( const CSharedObject & soRHS ) const
|
||||
{
|
||||
Assert( GetTypeID() == soRHS.GetTypeID() );
|
||||
const CSOTFMatchResultPlayerStats &obj = Obj();
|
||||
const CSOTFMatchResultPlayerStats &rhs = ( static_cast<const CSOTFMatchResultPlayerInfo &>( soRHS ) ).Obj();
|
||||
Assert( obj.account_id() == obj.account_id() );
|
||||
|
||||
if ( obj.match_group() < rhs.match_group() ) return true;
|
||||
if ( obj.match_group() > rhs.match_group() ) return false;
|
||||
|
||||
return obj.season_id() < rhs.season_id();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFMatchResultPlayerInfo::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchMatchResultPlayerInfo schMatchInfo;
|
||||
WriteToRecord( &schMatchInfo );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schMatchInfo );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFMatchResultPlayerInfo::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
|
||||
{
|
||||
CSchMatchResultPlayerInfo schMatchInfo;
|
||||
WriteToRecord( &schMatchInfo );
|
||||
CColumnSet csDatabaseDirty( schMatchInfo.GetPSchema()->GetRecordInfo() );
|
||||
csDatabaseDirty.MakeEmpty();
|
||||
FOR_EACH_VEC( fields, nField )
|
||||
{
|
||||
switch ( fields[nField] )
|
||||
{
|
||||
case CSOTFMatchResultPlayerStats::kMatchIdFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unMatchID ); break;
|
||||
case CSOTFMatchResultPlayerStats::kAccountIdFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unAccountID ); break;
|
||||
case CSOTFMatchResultPlayerStats::kMatchGroupFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_nMatchGroup ); break;
|
||||
case CSOTFMatchResultPlayerStats::kEndtimeFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_RTime32Stamp ); break;
|
||||
case CSOTFMatchResultPlayerStats::kSeasonIdFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unSeasonID ); break;
|
||||
case CSOTFMatchResultPlayerStats::kStatusFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unStatus ); break;
|
||||
|
||||
case CSOTFMatchResultPlayerStats::kPartyIdFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unPartyID ); break;
|
||||
case CSOTFMatchResultPlayerStats::kTeamFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unTeam ); break;
|
||||
case CSOTFMatchResultPlayerStats::kScoreFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unScore ); break;
|
||||
case CSOTFMatchResultPlayerStats::kPingFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unPing ); break;
|
||||
case CSOTFMatchResultPlayerStats::kFlagsFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unFlags ); break;
|
||||
case CSOTFMatchResultPlayerStats::kDisplayRatingFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unDisplayRating ); break;
|
||||
case CSOTFMatchResultPlayerStats::kDisplayRatingChangeFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_nDisplayRatingChange ); break;
|
||||
case CSOTFMatchResultPlayerStats::kRankFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unRank ); break;
|
||||
case CSOTFMatchResultPlayerStats::kClassesPlayedFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unClassesPlayed ); break;
|
||||
|
||||
case CSOTFMatchResultPlayerStats::kKillsFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unKills ); break;
|
||||
case CSOTFMatchResultPlayerStats::kDeathsFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unDeaths ); break;
|
||||
case CSOTFMatchResultPlayerStats::kDamageFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unDamage ); break;
|
||||
case CSOTFMatchResultPlayerStats::kHealingFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unHealing ); break;
|
||||
case CSOTFMatchResultPlayerStats::kSupportFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unSupport ); break;
|
||||
|
||||
case CSOTFMatchResultPlayerStats::kScoreMedalFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unScoreMedal ); break;
|
||||
case CSOTFMatchResultPlayerStats::kKillsMedalFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unKillsMedal ); break;
|
||||
case CSOTFMatchResultPlayerStats::kDamageMedalFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unDamageMedal ); break;
|
||||
case CSOTFMatchResultPlayerStats::kHealingMedalFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unHealingMedal ); break;
|
||||
case CSOTFMatchResultPlayerStats::kSupportMedalFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unSupportMedal ); break;
|
||||
|
||||
case CSOTFMatchResultPlayerStats::kMapIndexFieldNumber: csDatabaseDirty.BAddColumn( CSchMatchResultPlayerInfo::k_iField_unMapIndex ); break;
|
||||
|
||||
default:
|
||||
Assert( false );
|
||||
}
|
||||
}
|
||||
return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schMatchInfo, csDatabaseDirty );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CSOTFMatchResultPlayerInfo::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchMatchResultPlayerInfo schMatchInfo;
|
||||
WriteToRecord( &schMatchInfo );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schMatchInfo );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTFMatchResultPlayerInfo::WriteToRecord( CSchMatchResultPlayerInfo *pMatchInfo ) const
|
||||
{
|
||||
pMatchInfo->m_unMatchID = Obj().match_id();
|
||||
pMatchInfo->m_unAccountID = Obj().account_id();
|
||||
pMatchInfo->m_nMatchGroup = (int16)Obj().match_group();
|
||||
pMatchInfo->m_RTime32Stamp = Obj().endtime();
|
||||
pMatchInfo->m_unSeasonID = (uint16)Obj().season_id();
|
||||
pMatchInfo->m_unStatus = (uint16)Obj().status();
|
||||
|
||||
pMatchInfo->m_unPartyID = Obj().party_id();
|
||||
pMatchInfo->m_unTeam = (uint16)Obj().team();
|
||||
pMatchInfo->m_unScore = (uint16)Obj().score();
|
||||
pMatchInfo->m_unPing = (uint16)Obj().ping();
|
||||
pMatchInfo->m_unFlags = Obj().flags();
|
||||
pMatchInfo->m_unDisplayRating = Obj().display_rating();
|
||||
pMatchInfo->m_nDisplayRatingChange = Obj().display_rating_change();
|
||||
pMatchInfo->m_unRank = (uint16)Obj().rank();
|
||||
pMatchInfo->m_unClassesPlayed = Obj().classes_played();
|
||||
|
||||
pMatchInfo->m_unKills = (uint16)Obj().kills();
|
||||
pMatchInfo->m_unDeaths = (uint16)Obj().deaths();
|
||||
pMatchInfo->m_unDamage = Obj().damage();
|
||||
pMatchInfo->m_unHealing = Obj().healing();
|
||||
pMatchInfo->m_unSupport = Obj().support();
|
||||
|
||||
pMatchInfo->m_unScoreMedal = (uint8)Obj().score_medal();
|
||||
pMatchInfo->m_unKillsMedal = (uint8)Obj().kills_medal();
|
||||
pMatchInfo->m_unDamageMedal = (uint8)Obj().damage_medal();
|
||||
pMatchInfo->m_unHealingMedal = (uint8)Obj().healing_medal();
|
||||
pMatchInfo->m_unSupportMedal = (uint8)Obj().support_medal();
|
||||
|
||||
pMatchInfo->m_unMapIndex = (uint16)Obj().map_index();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CSOTFMatchResultPlayerInfo::ReadFromRecord( const CSchMatchResultPlayerInfo &matchInfo )
|
||||
{
|
||||
Obj().set_match_id( matchInfo.m_unMatchID );
|
||||
Obj().set_account_id( matchInfo.m_unAccountID );
|
||||
Obj().set_match_group( matchInfo.m_nMatchGroup );
|
||||
Obj().set_endtime( matchInfo.m_RTime32Stamp );
|
||||
Obj().set_season_id( matchInfo.m_unSeasonID );
|
||||
Obj().set_status( matchInfo.m_unStatus );
|
||||
|
||||
Obj().set_party_id( matchInfo.m_unPartyID );
|
||||
Obj().set_team( matchInfo.m_unTeam );
|
||||
Obj().set_score( matchInfo.m_unScore );
|
||||
Obj().set_ping( matchInfo.m_unPing );
|
||||
Obj().set_flags( matchInfo.m_unFlags );
|
||||
Obj().set_display_rating( matchInfo.m_unDisplayRating );
|
||||
Obj().set_display_rating_change( matchInfo.m_nDisplayRatingChange );
|
||||
Obj().set_rank( matchInfo.m_unRank );
|
||||
Obj().set_classes_played( matchInfo.m_unClassesPlayed );
|
||||
|
||||
Obj().set_kills( matchInfo.m_unKills );
|
||||
Obj().set_deaths( matchInfo.m_unDeaths );
|
||||
Obj().set_damage( matchInfo.m_unDamage );
|
||||
Obj().set_healing( matchInfo.m_unHealing );
|
||||
Obj().set_support( matchInfo.m_unSupport );
|
||||
|
||||
Obj().set_score_medal( matchInfo.m_unScoreMedal );
|
||||
Obj().set_kills_medal( matchInfo.m_unKillsMedal );
|
||||
Obj().set_damage_medal( matchInfo.m_unDamageMedal );
|
||||
Obj().set_healing_medal( matchInfo.m_unHealingMedal );
|
||||
Obj().set_support_medal( matchInfo.m_unSupportMedal );
|
||||
|
||||
Obj().set_map_index( matchInfo.m_unMapIndex );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCMatchHistoryLoad : public CGCEconJob
|
||||
{
|
||||
public:
|
||||
CGCMatchHistoryLoad( CGCEcon *pGC ) : CGCEconJob( pGC ) {}
|
||||
bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CGCMatchHistoryLoad::BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket )
|
||||
{
|
||||
CProtoBufMsg < CMsgGCMatchHistoryLoad > msg( pNetPacket );
|
||||
const CSteamID steamID( msg.Hdr().client_steam_id() );
|
||||
if ( !steamID.IsValid() || !steamID.BIndividualAccount() )
|
||||
return true;
|
||||
|
||||
CTFSharedObjectCache *pSOCache = GGCTF()->YieldingGetLockedTFSOCache( steamID, __FILE__, __LINE__ );
|
||||
if ( !pSOCache )
|
||||
return true;
|
||||
|
||||
CScopedSteamIDLock playerLock;
|
||||
playerLock.MarkLocked( steamID );
|
||||
|
||||
pSOCache->BYieldingLoadMatchHistoryObjects( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GC_REG_JOB( CGCEcon, CGCMatchHistoryLoad, "CGCMatchHistoryLoad", k_EMsgGCMatchHistoryLoad, k_EServerTypeGC );
|
||||
|
||||
#endif // GC
|
||||
@@ -0,0 +1,81 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Holds WarData
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TFLADDERDATA_H
|
||||
#define TFLADDERDATA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/protobufsharedobject.h"
|
||||
#include "tf_gcmessages.h"
|
||||
#if defined (CLIENT_DLL) || defined (GAME_DLL)
|
||||
#include "gc_clientsystem.h"
|
||||
#endif
|
||||
|
||||
#ifdef GC
|
||||
#include "tf_gc.h"
|
||||
#else
|
||||
#include "tf_matchmaking_shared.h"
|
||||
#endif
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Purpose: The shared object that contains a ladder player's stats
|
||||
//---------------------------------------------------------------------------------
|
||||
class CSOTFLadderData : public GCSDK::CProtoBufSharedObject< CSOTFLadderPlayerStats, k_EEConTypeLadderData >
|
||||
{
|
||||
public:
|
||||
CSOTFLadderData();
|
||||
CSOTFLadderData( uint32 unAccountID, EMatchGroup eMatchGroup );
|
||||
#ifdef GC
|
||||
DECLARE_CLASS_MEMPOOL( CSOTFLadderData );
|
||||
|
||||
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const OVERRIDE;
|
||||
|
||||
virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess ) OVERRIDE;
|
||||
virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields ) OVERRIDE;
|
||||
virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess ) OVERRIDE;
|
||||
|
||||
void WriteToRecord( CSchLadderData *pLadderData ) const;
|
||||
void ReadFromRecord( const CSchLadderData &ladderData );
|
||||
#endif // GC
|
||||
};
|
||||
|
||||
|
||||
CSOTFLadderData *YieldingGetPlayerLadderDataBySteamID( const CSteamID &steamID, EMatchGroup nMatchGroup );
|
||||
#ifndef GC
|
||||
CSOTFLadderData *GetLocalPlayerLadderData( EMatchGroup nMatchGroup ); // TODO: GetSeasonID()
|
||||
#endif // !GC
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Purpose: The shared object that contains stats from a specific match - for match history on the client
|
||||
//---------------------------------------------------------------------------------
|
||||
class CSOTFMatchResultPlayerInfo : public GCSDK::CProtoBufSharedObject< CSOTFMatchResultPlayerStats, k_EEConTypeMatchResultPlayerInfo >
|
||||
{
|
||||
public:
|
||||
CSOTFMatchResultPlayerInfo();
|
||||
#ifdef GC
|
||||
DECLARE_CLASS_MEMPOOL( CSOTFMatchResultPlayerInfo );
|
||||
CSOTFMatchResultPlayerInfo( uint32 unAccountID );
|
||||
|
||||
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const OVERRIDE;
|
||||
|
||||
virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess ) OVERRIDE;
|
||||
virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields ) OVERRIDE;
|
||||
virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess ) OVERRIDE;
|
||||
|
||||
void WriteToRecord( CSchMatchResultPlayerInfo *pMatchInfo ) const;
|
||||
void ReadFromRecord( const CSchMatchResultPlayerInfo &matchInfo );
|
||||
#endif // GC
|
||||
};
|
||||
|
||||
#ifndef GC
|
||||
void GetLocalPlayerMatchHistory( EMatchGroup nMatchGroup, CUtlVector < CSOTFMatchResultPlayerStats > &vecMatchesOut );
|
||||
#endif // !GC
|
||||
|
||||
#endif // TFLADDERDATA_H
|
||||
@@ -0,0 +1,73 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
#include "cbase.h"
|
||||
#include "gcsdk/gcsdk_auto.h"
|
||||
#include "tf_lobby_server.h"
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
const CTFLobbyMember* CTFGSLobby::GetMemberDetails( CSteamID steamID ) const
|
||||
{
|
||||
for ( int i = 0; i < Obj().members_size(); i++ )
|
||||
{
|
||||
if ( Obj().members( i ).id() == steamID.ConvertToUint64() )
|
||||
return &Obj().members( i );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CTFLobbyMember* CTFGSLobby::GetMemberDetails( int i ) const
|
||||
{
|
||||
if ( !BAssertValidMemberIndex( i ) )
|
||||
return NULL;
|
||||
|
||||
return &Obj().members( i );
|
||||
}
|
||||
|
||||
const CSteamID CTFGSLobby::GetMember( int i ) const
|
||||
{
|
||||
Assert( i >= 0 && i < Obj().members_size() );
|
||||
if ( i < 0 || i >= Obj().members_size() )
|
||||
return k_steamIDNil;
|
||||
|
||||
return Obj().members( i ).id();
|
||||
}
|
||||
|
||||
CTFLobbyMember_ConnectState CTFGSLobby::GetMemberConnectState( int iMemberIndex ) const
|
||||
{
|
||||
if ( !BAssertValidMemberIndex( iMemberIndex ) )
|
||||
return CTFLobbyMember_ConnectState_INVALID;
|
||||
return Obj().members( iMemberIndex ).connect_state();
|
||||
}
|
||||
|
||||
bool CTFGSLobby::BAssertValidMemberIndex( int iMemberIndex ) const
|
||||
{
|
||||
bool bValidMemberIndex = iMemberIndex >= 0 && iMemberIndex < Obj().members_size();
|
||||
Assert( bValidMemberIndex );
|
||||
return bValidMemberIndex;
|
||||
}
|
||||
|
||||
void CTFGSLobby::SpewDebug()
|
||||
{
|
||||
Msg( "CTFGSLobby: ID:%016llx %d member(s) allow_spectators: %d\n", GetGroupID(), GetNumMembers(), Obj().allow_spectating() );
|
||||
for ( int i = 0; i < GetNumMembers(); i++ )
|
||||
{
|
||||
Msg( " Member[%d] %s team = %d\n", i, GetMember( i ).Render(), GetMemberDetails( i )->team() );
|
||||
}
|
||||
Msg(" Dump:\n" );
|
||||
Dump();
|
||||
}
|
||||
|
||||
#ifdef USE_MVM_TOUR
|
||||
const char *CTFGSLobby::GetMannUpTourName() const
|
||||
{
|
||||
if ( !IsMannUpGroup( GetMatchGroup() ) )
|
||||
return NULL;
|
||||
return Obj().mannup_tour_name().c_str();
|
||||
}
|
||||
#endif // USE_MVM_TOUR
|
||||
@@ -0,0 +1,55 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: The lobby shared object for gameservers, managed by CTFLobby
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_LOBBY_SERVER_H
|
||||
#define TF_LOBBY_SERVER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/protobufsharedobject.h"
|
||||
#include "tf_gcmessages.h"
|
||||
#include "tf_matchmaking_shared.h"
|
||||
#include "playergroup.h"
|
||||
|
||||
class CTFGSLobby : public GCSDK::CProtoBufSharedObject<CSOTFGameServerLobby, k_EProtoObjectTFGameServerLobby>
|
||||
{
|
||||
typedef GCSDK::CProtoBufSharedObject<CSOTFGameServerLobby, k_EProtoObjectTFGameServerLobby> BaseClass;
|
||||
public:
|
||||
virtual ~CTFGSLobby() {}
|
||||
|
||||
// Debug
|
||||
void SpewDebug();
|
||||
|
||||
// Member helpers
|
||||
const CTFLobbyMember* GetMemberDetails( CSteamID steamID ) const;
|
||||
const CTFLobbyMember* GetMemberDetails( int i ) const;
|
||||
const CSteamID GetMember( int i ) const;
|
||||
int GetNumMembers() const { return Obj().members_size(); }
|
||||
CTFLobbyMember_ConnectState GetMemberConnectState( int iMemberIndex ) const;
|
||||
bool BAssertValidMemberIndex( int iMemberIndex ) const;
|
||||
|
||||
// Inline helpers
|
||||
CSOTFGameServerLobby::State GetState() const { return Obj().state(); }
|
||||
const char *GetMissionName() const { return Obj().mission_name().c_str(); }
|
||||
EMatchGroup GetMatchGroup() const { return Obj().has_match_group() ? (EMatchGroup)Obj().match_group() : k_nMatchGroup_Invalid; }
|
||||
uint64 GetMatchID( void ) const { return Obj().match_id(); }
|
||||
uint32 GetFlags( void ) const { return Obj().flags(); }
|
||||
const char *GetMapName() const { return Obj().map_name().c_str(); }
|
||||
GCSDK::PlayerGroupID_t GetGroupID() const { return Obj().lobby_id(); }
|
||||
bool GetLateJoinEligible() const { return Obj().late_join_eligible(); }
|
||||
CSteamID GetServerID() const { return Obj().server_id(); }
|
||||
const char *GetConnect() const { return Obj().connect().c_str(); }
|
||||
uint32_t GetLobbyMMVersion() const { return Obj().lobby_mm_version(); }
|
||||
|
||||
#ifdef USE_MVM_TOUR
|
||||
// Returns name of tour that we are playing for. Returns NULL if we are not playing for bragging rights!
|
||||
const char *GetMannUpTourName() const;
|
||||
#endif // USE_MVM_TOUR
|
||||
|
||||
};
|
||||
|
||||
#endif // TF_LOBBY_SERVER_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,286 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Entities for use in the Robot Destruction TF2 game mode.
|
||||
//
|
||||
//=========================================================================//
|
||||
#ifndef TF_LOGIC_HALLOWEEN_2014_H
|
||||
#define TF_LOGIC_HALLOWEEN_2014_H
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFMinigameLogic C_TFMinigameLogic
|
||||
#define CTFMiniGame C_TFMiniGame
|
||||
#define CTFHalloweenMinigame C_TFHalloweenMinigame
|
||||
#define CTFHalloweenMinigame_FallingPlatforms C_TFHalloweenMinigame_FallingPlatforms
|
||||
#endif
|
||||
|
||||
#define MINIGAME_INVALID -1
|
||||
|
||||
DECLARE_AUTO_LIST( IMinigameAutoList );
|
||||
|
||||
class CTFMiniGame : public CBaseEntity
|
||||
#ifdef GAME_DLL
|
||||
, public CGameEventListener
|
||||
#endif
|
||||
, public IMinigameAutoList
|
||||
{
|
||||
public:
|
||||
enum EScoringType
|
||||
{
|
||||
SCORING_TYPE_POINTS = 0,
|
||||
SCORING_TYPE_PLAYERS_ALIVE,
|
||||
|
||||
NUM_SCORING_TYPES
|
||||
};
|
||||
|
||||
enum EMinigameType
|
||||
{
|
||||
MINIGAME_GENERIC = 0,
|
||||
MINIGAME_HALLOWEEN2014_COLLECTION,
|
||||
MINIGAME_HALLOWEEN2014_PLATFORMS,
|
||||
MINIGAME_HALLOWEEN2014_SOCCER,
|
||||
// don't change the order of these first three types because the TF_HALLOWEEN_DOOMSDAY_WIN_MINIROUNDS achievement depends on it
|
||||
|
||||
NUM_MINIGAME_TYPES
|
||||
};
|
||||
|
||||
DECLARE_CLASS( CTFMiniGame, CBaseEntity )
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_DATADESC();
|
||||
#endif
|
||||
|
||||
CTFMiniGame();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Precache() OVERRIDE;
|
||||
virtual void FireGameEvent( IGameEvent * event ) OVERRIDE;
|
||||
virtual int UpdateTransmitState() OVERRIDE { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
|
||||
void InputScoreTeamRed( inputdata_t &inputdata );
|
||||
void InputScoreTeamBlue( inputdata_t &inputdata );
|
||||
void InputChangeHudResFile( inputdata_t &inputdata );
|
||||
|
||||
virtual void ScorePointsForTeam( int nTeamNum, int nPoints );
|
||||
virtual void TeleportAllPlayers();
|
||||
virtual void OnTeleportPlayerToMinigame( CTFPlayer *pPlayer );
|
||||
virtual void ReturnAllPlayers();
|
||||
const char *GetTeamSpawnPointName( int nTeamNum ) const;
|
||||
const bool AllowedInRandom() const { return m_bMinigameAllowedInRamdomPool; }
|
||||
virtual void UpdateDeadPlayers( int nTeam, COutputEvent& eventWin, COutputEvent& eventAllDead, bool& bCanWin );
|
||||
EMinigameType GetMinigameType() const { return m_eMinigameType; }
|
||||
void SetAdvantagedTeam ( int iAdvantageTeam ) { m_iAdvantagedTeam = iAdvantageTeam; }
|
||||
#else
|
||||
const char *GetResFile() const { return m_pszHudResFile; }
|
||||
int GetMaxScore( void ) const { return m_nMaxScoreForMiniGame; }
|
||||
int GetScoreForTeam( int nTeamNum ) const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void InternalHandleInputScore( inputdata_t &inputdata ){}
|
||||
virtual void SuddenDeathTimeStartThink();
|
||||
|
||||
COutputEvent m_OnRedHitMaxScore;
|
||||
COutputEvent m_OnBlueHitMaxScore;
|
||||
COutputEvent m_OnTeleportToMinigame;
|
||||
COutputEvent m_OnReturnFromMinigame;
|
||||
COutputEvent m_OnAllRedDead;
|
||||
COutputEvent m_OnAllBlueDead;
|
||||
COutputEvent m_OnSuddenDeathStart;
|
||||
|
||||
const char *m_pszTeamSpawnPoint[ TF_TEAM_COUNT ];
|
||||
bool m_bMinigameAllowedInRamdomPool;
|
||||
bool m_bIsActive;
|
||||
string_t m_iszHudResFile;
|
||||
EMinigameType m_eMinigameType;
|
||||
string_t m_iszYourTeamScoreSound;
|
||||
string_t m_iszEnemyTeamScoreSound;
|
||||
float m_flSuddenDeathTime; // -1: No sudden death, 0: In sudden death, >0: Sudden death time.
|
||||
int m_iAdvantagedTeam;
|
||||
#endif
|
||||
|
||||
CNetworkString( m_pszHudResFile, MAX_PATH );
|
||||
CNetworkVar( int, m_nMaxScoreForMiniGame );
|
||||
CNetworkArray( int, m_nMinigameTeamScore, TF_TEAM_COUNT );
|
||||
CNetworkVar( EScoringType, m_eScoringType );
|
||||
};
|
||||
|
||||
|
||||
class CTFHalloweenMinigame : public CTFMiniGame
|
||||
{
|
||||
DECLARE_CLASS( CTFHalloweenMinigame, CTFMiniGame )
|
||||
DECLARE_NETWORKCLASS();
|
||||
public:
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CTFHalloweenMinigame();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void FireGameEvent( IGameEvent * event ) OVERRIDE;
|
||||
|
||||
virtual void TeleportAllPlayers() OVERRIDE;
|
||||
virtual void OnTeleportPlayerToMinigame( CTFPlayer *pPlayer ) OVERRIDE;
|
||||
virtual void ReturnAllPlayers() OVERRIDE;
|
||||
|
||||
void InputKartWinAnimationRed( inputdata_t &inputdata );
|
||||
void InputKartWinAnimationBlue( inputdata_t &inputdata );
|
||||
void InputKartLoseAnimationRed( inputdata_t &inputdata );
|
||||
void InputKartLoseAnimationBlue( inputdata_t &inputdata );
|
||||
|
||||
void InputEnableSpawnBoss( inputdata_t &inputdata );
|
||||
void InputDisableSpawnBoss( inputdata_t &inputdata );
|
||||
|
||||
protected:
|
||||
virtual void InternalHandleInputScore( inputdata_t &inputdata ) OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
void TeleportAllPlayersThink();
|
||||
|
||||
EHANDLE m_hBossSpawnPoint;
|
||||
EHANDLE m_hHalloweenBoss;
|
||||
#endif // GAME_DLL
|
||||
};
|
||||
|
||||
class CTFHalloweenMinigame_FallingPlatforms : public CTFHalloweenMinigame
|
||||
{
|
||||
DECLARE_CLASS( CTFHalloweenMinigame_FallingPlatforms, CTFHalloweenMinigame )
|
||||
DECLARE_NETWORKCLASS();
|
||||
public:
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CTFHalloweenMinigame_FallingPlatforms();
|
||||
|
||||
DECLARE_DATADESC();
|
||||
|
||||
void InputChoosePlatform( inputdata_t &inputdata );
|
||||
virtual void FireGameEvent( IGameEvent * event ) OVERRIDE;
|
||||
|
||||
COutputInt m_OutputSafePlatform;
|
||||
COutputInt m_OutputRemovePlatform;
|
||||
|
||||
private:
|
||||
CCopyableUtlVector< int > m_vecRemainingPlatforms;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CTFMinigameLogic : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CTFMinigameLogic, CBaseEntity )
|
||||
DECLARE_NETWORKCLASS();
|
||||
public:
|
||||
CTFMinigameLogic();
|
||||
virtual ~CTFMinigameLogic();
|
||||
|
||||
static CTFMinigameLogic* GetMinigameLogic() { return m_sMinigameLogic; }
|
||||
CTFMiniGame *GetActiveMinigame() const { return m_hActiveMinigame; }
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_DATADESC();
|
||||
virtual int UpdateTransmitState() OVERRIDE { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
|
||||
void InputReturnFromMinigame( inputdata_t &inputdata );
|
||||
void InputTeleportToMinigame( inputdata_t &inputdata );
|
||||
void InputSetAdvantageTeam( inputdata_t &inputdata );
|
||||
void InputTeleportToRandomMinigame( inputdata_t &inputdata );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
static CTFMinigameLogic* m_sMinigameLogic;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
virtual void TeleportToMinigame( int nMiniGameIndex );
|
||||
virtual void ReturnFromMinigame();
|
||||
|
||||
int m_iAdvantagedTeam;
|
||||
|
||||
#endif
|
||||
CNetworkHandle( CTFMiniGame, m_hActiveMinigame );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFHalloweenFortuneTeller : public CBaseAnimating
|
||||
#ifdef GAME_DLL
|
||||
, public CGameEventListener
|
||||
#endif
|
||||
{
|
||||
DECLARE_CLASS( CTFHalloweenFortuneTeller, CBaseAnimating );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
enum ETellerType
|
||||
{
|
||||
TELLER_TYPE_EVERYBODY = 0,
|
||||
TELLER_TYPE_PERSONAL,
|
||||
|
||||
NUM_TELLER_TYPES
|
||||
};
|
||||
|
||||
public:
|
||||
CTFHalloweenFortuneTeller();
|
||||
~CTFHalloweenFortuneTeller();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void UpdateOnRemove() OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void InputEnableFortuneTelling( inputdata_t & );
|
||||
void InputDisableFortuneTelling( inputdata_t & );
|
||||
void InputStartFortuneTelling( inputdata_t & );
|
||||
void InputEndFortuneTelling( inputdata_t & );
|
||||
#endif // GAME_DLL
|
||||
|
||||
protected:
|
||||
virtual void Precache() OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
void FireGameEvent( IGameEvent* pEvent );
|
||||
void UpdateFortuneTellerTime();
|
||||
void PauseTimer();
|
||||
void ResetTimer();
|
||||
|
||||
void StartFortuneWarning();
|
||||
void StartFortuneTell();
|
||||
void EndFortuneTell();
|
||||
void TellFortune();
|
||||
void ApplyFortuneEffect();
|
||||
void StopTalkingAnim();
|
||||
void DanceThink();
|
||||
void SpeakThink();
|
||||
#endif // GAME_DLL
|
||||
|
||||
private:
|
||||
#ifdef GAME_DLL
|
||||
COutputEvent m_OnFortuneWarning;
|
||||
COutputEvent m_OnFortuneTold;
|
||||
COutputEvent m_OnFortuneCurse;
|
||||
COutputEvent m_OnFortuneEnd;
|
||||
class CConditionFortuneTellerEffect* m_pActiveFortune;
|
||||
|
||||
string_t m_iszRedTeleport;
|
||||
string_t m_iszBlueTeleport;
|
||||
|
||||
bool m_bUseTimer;
|
||||
bool m_bWasUsingTimer;
|
||||
float m_flStartTime;
|
||||
float m_flPauseTime;
|
||||
#endif // GAME_DLL
|
||||
};
|
||||
|
||||
#endif // TF_LOGIC_HALLOWEEN_2014
|
||||
@@ -0,0 +1,468 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Entities for use in the Robot Destruction TF2 game mode.
|
||||
//
|
||||
//=========================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_logic_player_destruction.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_player.h"
|
||||
#include "entity_capture_flag.h"
|
||||
#include "tf_obj_dispenser.h"
|
||||
#include "tf_gamerules.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#endif // GAME_DLL
|
||||
|
||||
BEGIN_DATADESC( CPlayerDestructionDispenser )
|
||||
END_DATADESC()
|
||||
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( PlayerDestructionDispenser, DT_PlayerDestructionDispenser )
|
||||
LINK_ENTITY_TO_CLASS( pd_dispenser, CPlayerDestructionDispenser );
|
||||
|
||||
BEGIN_NETWORK_TABLE( CPlayerDestructionDispenser, DT_PlayerDestructionDispenser )
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
#ifdef GAME_DLL
|
||||
BEGIN_DATADESC( CTFPlayerDestructionLogic )
|
||||
DEFINE_KEYFIELD( m_iszPropModelName, FIELD_STRING, "prop_model_name" ),
|
||||
DEFINE_KEYFIELD( m_iszPropDropSound, FIELD_STRING, "prop_drop_sound" ),
|
||||
DEFINE_KEYFIELD( m_iszPropPickupSound, FIELD_STRING, "prop_pickup_sound" ),
|
||||
DEFINE_KEYFIELD( m_nMinPoints, FIELD_INTEGER, "min_points" ),
|
||||
DEFINE_KEYFIELD( m_nPointsPerPlayer, FIELD_INTEGER, "points_per_player" ),
|
||||
DEFINE_KEYFIELD( m_nFlagResetDelay, FIELD_INTEGER, "flag_reset_delay" ),
|
||||
DEFINE_KEYFIELD( m_nHealDistance, FIELD_INTEGER, "heal_distance" ),
|
||||
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "ScoreRedPoints", InputScoreRedPoints ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "ScoreBluePoints", InputScoreBluePoints ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "EnableMaxScoreUpdating", InputEnableMaxScoreUpdating ),
|
||||
DEFINE_INPUTFUNC( FIELD_VOID, "DisableMaxScoreUpdating", InputDisableMaxScoreUpdating ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetCountdownTimer", InputSetCountdownTimer ),
|
||||
DEFINE_INPUTFUNC( FIELD_STRING, "SetCountdownImage", InputSetCountdownImage ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetFlagResetDelay", InputSetFlagResetDelay ),
|
||||
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPointsOnPlayerDeath", InputSetPointsOnPlayerDeath ),
|
||||
|
||||
DEFINE_OUTPUT( m_OnRedScoreChanged, "OnRedScoreChanged" ),
|
||||
DEFINE_OUTPUT( m_OnBlueScoreChanged, "OnBlueScoreChanged" ),
|
||||
DEFINE_OUTPUT( m_OnCountdownTimerExpired, "OnCountdownTimerExpired" ),
|
||||
END_DATADESC()
|
||||
#endif
|
||||
|
||||
LINK_ENTITY_TO_CLASS( tf_logic_player_destruction, CTFPlayerDestructionLogic );
|
||||
IMPLEMENT_NETWORKCLASS_ALIASED( TFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
|
||||
|
||||
BEGIN_NETWORK_TABLE( CTFPlayerDestructionLogic, DT_TFPlayerDestructionLogic )
|
||||
#ifdef CLIENT_DLL
|
||||
RecvPropEHandle( RECVINFO( m_hRedTeamLeader ) ),
|
||||
RecvPropEHandle( RECVINFO( m_hBlueTeamLeader ) ),
|
||||
RecvPropString( RECVINFO( m_iszCountdownImage ) ),
|
||||
RecvPropBool( RECVINFO( m_bUsingCountdownImage ) ),
|
||||
#else
|
||||
SendPropEHandle( SENDINFO( m_hRedTeamLeader ) ),
|
||||
SendPropEHandle( SENDINFO( m_hBlueTeamLeader ) ),
|
||||
SendPropStringT( SENDINFO( m_iszCountdownImage ) ),
|
||||
SendPropBool( SENDINFO( m_bUsingCountdownImage ) ),
|
||||
#endif
|
||||
END_NETWORK_TABLE()
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFPlayerDestructionLogic::CTFPlayerDestructionLogic()
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
m_iszPropModelName = MAKE_STRING( "models/flag/flag.mdl" );
|
||||
ListenForGameEvent( "player_disconnect" );
|
||||
m_bMaxScoreUpdatingAllowed = false;
|
||||
m_nFlagResetDelay = 60;
|
||||
m_nHealDistance = 450;
|
||||
m_nPointsOnPlayerDeath = 1;
|
||||
#endif // GAME_DLL
|
||||
|
||||
m_hRedTeamLeader = NULL;
|
||||
m_hBlueTeamLeader = NULL;
|
||||
|
||||
m_bUsingCountdownImage = false;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
m_iszCountdownImage[0] = '\0';
|
||||
#else
|
||||
m_iszCountdownImage.Set( NULL_STRING );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFPlayerDestructionLogic* CTFPlayerDestructionLogic::GetPlayerDestructionLogic()
|
||||
{
|
||||
return assert_cast< CTFPlayerDestructionLogic* >( CTFRobotDestructionLogic::GetRobotDestructionLogic() );
|
||||
}
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::Precache()
|
||||
{
|
||||
BaseClass::Precache();
|
||||
|
||||
PrecacheModel( GetPropModelName() );
|
||||
PrecacheScriptSound( STRING( m_iszPropDropSound ) );
|
||||
PrecacheScriptSound( STRING( m_iszPropPickupSound ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CTFPlayerDestructionLogic::GetPropModelName() const
|
||||
{
|
||||
return STRING( m_iszPropModelName );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::CalcTeamLeader( int iTeam )
|
||||
{
|
||||
// team leader's changed team, recalculate team leader for that team
|
||||
if ( m_hRedTeamLeader.Get() && m_hRedTeamLeader.Get()->GetTeamNumber() != TF_TEAM_RED )
|
||||
{
|
||||
m_hRedTeamLeader = NULL;
|
||||
CalcTeamLeader( TF_TEAM_RED );
|
||||
}
|
||||
if ( m_hBlueTeamLeader.Get() && m_hBlueTeamLeader.Get()->GetTeamNumber() != TF_TEAM_BLUE )
|
||||
{
|
||||
m_hBlueTeamLeader = NULL;
|
||||
CalcTeamLeader( TF_TEAM_BLUE );
|
||||
}
|
||||
|
||||
CUtlVector< CTFPlayer * > playerVector;
|
||||
CollectPlayers( &playerVector, iTeam, COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
CTFPlayer *pTeamLeader = iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
|
||||
int iCurrentLeadingPoint = 0;
|
||||
if ( pTeamLeader && pTeamLeader->HasItem() )
|
||||
{
|
||||
CCaptureFlag *pFlag = dynamic_cast<CCaptureFlag*>( pTeamLeader->GetItem() );
|
||||
if ( pFlag )
|
||||
{
|
||||
iCurrentLeadingPoint = pFlag->GetPointValue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset team leader
|
||||
pTeamLeader = NULL;
|
||||
if ( iTeam == TF_TEAM_RED )
|
||||
{
|
||||
m_hRedTeamLeader = NULL;
|
||||
UTIL_Remove( m_hRedDispenser );
|
||||
m_hRedDispenser = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hBlueTeamLeader = NULL;
|
||||
UTIL_Remove( m_hBlueDispenser );
|
||||
m_hBlueDispenser = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// find new team leader
|
||||
CTFPlayer *pNewTeamLeader = NULL;
|
||||
FOR_EACH_VEC( playerVector, i )
|
||||
{
|
||||
CTFPlayer *pPlayer = playerVector[i];
|
||||
if ( pPlayer == pTeamLeader )
|
||||
continue;
|
||||
|
||||
// community request from Watergate author to never have a SPY be the team leader
|
||||
if ( pPlayer->HasItem() && !pPlayer->IsPlayerClass( TF_CLASS_SPY ) )
|
||||
{
|
||||
CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag* >( pPlayer->GetItem() );
|
||||
if ( pFlag && pFlag->GetPointValue() > iCurrentLeadingPoint )
|
||||
{
|
||||
iCurrentLeadingPoint = pFlag->GetPointValue();
|
||||
pNewTeamLeader = pPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set new leader
|
||||
if ( pNewTeamLeader )
|
||||
{
|
||||
CObjectDispenser *pDispenser = NULL;
|
||||
if ( iTeam == TF_TEAM_RED )
|
||||
{
|
||||
m_hRedTeamLeader = pNewTeamLeader;
|
||||
|
||||
if ( !m_hRedDispenser )
|
||||
{
|
||||
m_hRedDispenser = CreateDispenser( iTeam );
|
||||
}
|
||||
pDispenser = m_hRedDispenser;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_hBlueTeamLeader = pNewTeamLeader;
|
||||
|
||||
if ( !m_hBlueDispenser )
|
||||
{
|
||||
m_hBlueDispenser = CreateDispenser( iTeam );
|
||||
}
|
||||
pDispenser = m_hBlueDispenser;
|
||||
}
|
||||
|
||||
if ( pDispenser )
|
||||
{
|
||||
pDispenser->SetOwnerEntity( pNewTeamLeader );
|
||||
pDispenser->FollowEntity( pNewTeamLeader );
|
||||
pDispenser->SetBuilder( pNewTeamLeader );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::FireGameEvent( IGameEvent *pEvent )
|
||||
{
|
||||
const char* pszName = pEvent->GetName();
|
||||
if ( FStrEq( pszName, "player_spawn" ) || FStrEq( pszName, "player_disconnect" ) )
|
||||
{
|
||||
EvaluatePlayerCount();
|
||||
return;
|
||||
}
|
||||
else if( FStrEq( pszName, "teamplay_pre_round_time_left" ) )
|
||||
{
|
||||
// Eat this event so the RD logic doesn't talk
|
||||
return;
|
||||
}
|
||||
|
||||
BaseClass::FireGameEvent( pEvent );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::OnRedScoreChanged()
|
||||
{
|
||||
m_OnRedScoreChanged.Set( (float)m_nRedScore / m_nMaxPoints, this, this );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::OnBlueScoreChanged()
|
||||
{
|
||||
m_OnBlueScoreChanged.Set( (float)m_nBlueScore / m_nMaxPoints, this, this );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::EvaluatePlayerCount()
|
||||
{
|
||||
// Bail if we're not allowed
|
||||
if ( !m_bMaxScoreUpdatingAllowed )
|
||||
return;
|
||||
|
||||
CUtlVector< CTFPlayer* > vecAllPlayers;
|
||||
CollectPlayers( &vecAllPlayers );
|
||||
|
||||
m_nMaxPoints = Max( m_nMinPoints, m_nPointsPerPlayer * vecAllPlayers.Count() );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputScoreRedPoints( inputdata_t& inputdata )
|
||||
{
|
||||
ScorePoints( TF_TEAM_RED, 1, SCORE_CORES_COLLECTED, NULL );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputScoreBluePoints( inputdata_t& inputdata )
|
||||
{
|
||||
ScorePoints( TF_TEAM_BLUE, 1, SCORE_CORES_COLLECTED, NULL );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputEnableMaxScoreUpdating( inputdata_t& inputdata )
|
||||
{
|
||||
m_bMaxScoreUpdatingAllowed = true;
|
||||
EvaluatePlayerCount();
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputDisableMaxScoreUpdating( inputdata_t& inputdata )
|
||||
{
|
||||
EvaluatePlayerCount();
|
||||
m_bMaxScoreUpdatingAllowed = false;
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputSetCountdownTimer( inputdata_t& inputdata )
|
||||
{
|
||||
int nTime = inputdata.value.Int();
|
||||
|
||||
if ( nTime > 0 )
|
||||
{
|
||||
SetCountdownEndTime( gpGlobals->curtime + nTime );
|
||||
SetThink( &CTFPlayerDestructionLogic::CountdownThink );
|
||||
SetNextThink( gpGlobals->curtime + 0.05f );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCountdownEndTime( -1.f );
|
||||
SetThink( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::CountdownThink( void )
|
||||
{
|
||||
if ( m_flCountdownEndTime > -1.f )
|
||||
{
|
||||
// if we're done, just reset the end time
|
||||
if ( m_flCountdownEndTime < gpGlobals->curtime )
|
||||
{
|
||||
m_OnCountdownTimerExpired.FireOutput( this, this );
|
||||
m_flCountdownEndTime = -1.f;
|
||||
SetThink( NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetNextThink( gpGlobals->curtime + 0.05f );
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputSetCountdownImage( inputdata_t& inputdata )
|
||||
{
|
||||
m_bUsingCountdownImage = true;
|
||||
m_iszCountdownImage = inputdata.value.StringID();
|
||||
}
|
||||
|
||||
|
||||
void CTFPlayerDestructionLogic::InputSetFlagResetDelay( inputdata_t& inputdata )
|
||||
{
|
||||
int nDelay = inputdata.value.Int();
|
||||
if ( nDelay < 0 )
|
||||
{
|
||||
nDelay = 0;
|
||||
}
|
||||
|
||||
m_nFlagResetDelay = nDelay;
|
||||
}
|
||||
|
||||
void CTFPlayerDestructionLogic::InputSetPointsOnPlayerDeath( inputdata_t& inputdata )
|
||||
{
|
||||
int nPointsOnPlayerDeath = inputdata.value.Int();
|
||||
if ( nPointsOnPlayerDeath < 0 )
|
||||
{
|
||||
nPointsOnPlayerDeath = 0;
|
||||
}
|
||||
|
||||
m_nPointsOnPlayerDeath = nPointsOnPlayerDeath;
|
||||
}
|
||||
|
||||
CObjectDispenser *CTFPlayerDestructionLogic::CreateDispenser( int iTeam )
|
||||
{
|
||||
CPlayerDestructionDispenser *pDispenser = static_cast< CPlayerDestructionDispenser* >( CBaseEntity::CreateNoSpawn( "pd_dispenser", vec3_origin, vec3_angle, NULL ) );
|
||||
pDispenser->ChangeTeam( iTeam );
|
||||
pDispenser->SetObjectFlags( pDispenser->GetObjectFlags() | OF_DOESNT_HAVE_A_MODEL | OF_PLAYER_DESTRUCTION );
|
||||
pDispenser->m_iUpgradeLevel = 1;
|
||||
DispatchSpawn( pDispenser );
|
||||
pDispenser->FinishedBuilding();
|
||||
pDispenser->AddEffects( EF_NODRAW );
|
||||
pDispenser->DisableAmmoPickupSound();
|
||||
pDispenser->DisableGenerateMetalSound();
|
||||
pDispenser->m_takedamage = DAMAGE_NO;
|
||||
|
||||
CBaseEntity *pTouchTrigger = pDispenser->GetTouchTrigger();
|
||||
if ( pTouchTrigger )
|
||||
{
|
||||
pTouchTrigger->FollowEntity( pDispenser );
|
||||
}
|
||||
|
||||
return pDispenser;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::PlayPropDropSound( CTFPlayer *pPlayer )
|
||||
{
|
||||
PlaySound( STRING( m_iszPropDropSound ), pPlayer );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::PlayPropPickupSound( CTFPlayer *pPlayer )
|
||||
{
|
||||
PlaySound( STRING( m_iszPropPickupSound ), pPlayer );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::PlaySound( const char *pszSound, CTFPlayer *pPlayer )
|
||||
{
|
||||
EmitSound_t params;
|
||||
params.m_pSoundName = pszSound;
|
||||
params.m_flSoundTime = 0;
|
||||
params.m_pflSoundDuration = 0;
|
||||
params.m_SoundLevel = SNDLVL_70dB;
|
||||
CPASFilter filter( pPlayer->GetAbsOrigin() );
|
||||
pPlayer->EmitSound( filter, pPlayer->entindex(), params );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPlayerDestructionDispenser::Spawn( void )
|
||||
{
|
||||
// This cast is for the benefit of GCC
|
||||
m_fObjectFlags |= (int)OF_DOESNT_HAVE_A_MODEL;
|
||||
m_takedamage = DAMAGE_NO;
|
||||
m_iUpgradeLevel = 1;
|
||||
|
||||
TFGameRules()->OnDispenserBuilt( this );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Finished building
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPlayerDestructionDispenser::OnGoActive( void )
|
||||
{
|
||||
BaseClass::OnGoActive();
|
||||
|
||||
if ( m_hTouchTrigger )
|
||||
{
|
||||
m_hTouchTrigger->SetParent( GetParent() );
|
||||
}
|
||||
|
||||
SetModel( "" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Spawn the vgui control screens on the object
|
||||
//-----------------------------------------------------------------------------
|
||||
void CPlayerDestructionDispenser::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
|
||||
{
|
||||
// no panels
|
||||
return;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CTFPlayerDestructionLogic::TeamWin( int nTeam )
|
||||
{
|
||||
if ( TFGameRules() )
|
||||
{
|
||||
TFGameRules()->SetWinningTeam( nTeam, WINREASON_PD_POINTS );
|
||||
}
|
||||
}
|
||||
#endif // GAME_DLL
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CTFPlayer *CTFPlayerDestructionLogic::GetTeamLeader( int iTeam ) const
|
||||
{
|
||||
return iTeam == TF_TEAM_RED ? m_hRedTeamLeader.Get() : m_hBlueTeamLeader.Get();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Entities for use in the Robot Destruction TF2 game mode.
|
||||
//
|
||||
//=========================================================================//
|
||||
#ifndef PLAYER_DESTRUCTION_H
|
||||
#define PLAYER_DESTRUCTION_H
|
||||
#pragma once
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_logic_robot_destruction.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFPlayerDestructionLogic C_TFPlayerDestructionLogic
|
||||
#define CPlayerDestructionDispenser C_PlayerDestructionDispenser
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFPlayerDestructionLogic : public CTFRobotDestructionLogic
|
||||
{
|
||||
public:
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_DATADESC();
|
||||
#endif // GAME_DLL
|
||||
DECLARE_CLASS( CTFPlayerDestructionLogic, CTFRobotDestructionLogic )
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
virtual EType GetType() const { return TYPE_PLAYER_DESTRUCTION; }
|
||||
|
||||
CTFPlayerDestructionLogic();
|
||||
static CTFPlayerDestructionLogic* GetPlayerDestructionLogic();
|
||||
|
||||
CTFPlayer* GetRedTeamLeader() const { return m_hRedTeamLeader.Get(); }
|
||||
CTFPlayer* GetBlueTeamLeader() const { return m_hBlueTeamLeader.Get(); }
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void Precache() OVERRIDE;
|
||||
|
||||
const char *GetPropModelName() const;
|
||||
|
||||
void CalcTeamLeader( int iTeam );
|
||||
|
||||
virtual void FireGameEvent( IGameEvent *pEvent ) OVERRIDE;
|
||||
|
||||
void InputScoreRedPoints( inputdata_t& inputdata );
|
||||
void InputScoreBluePoints( inputdata_t& inputdata );
|
||||
void InputEnableMaxScoreUpdating( inputdata_t& inputdata );
|
||||
void InputDisableMaxScoreUpdating( inputdata_t& inputdata );
|
||||
void InputSetCountdownTimer( inputdata_t& inputdata );
|
||||
void InputSetCountdownImage( inputdata_t& inputdata );
|
||||
void InputSetFlagResetDelay( inputdata_t& inputdata );
|
||||
void InputSetPointsOnPlayerDeath( inputdata_t& inputdata );
|
||||
|
||||
void PlayPropDropSound( CTFPlayer *pPlayer );
|
||||
void PlayPropPickupSound( CTFPlayer *pPlayer );
|
||||
|
||||
void CountdownThink( void );
|
||||
int GetFlagResetDelay( void ){ return m_nFlagResetDelay; }
|
||||
int GetPointsOnPlayerDeath( void ){ return m_nPointsOnPlayerDeath; }
|
||||
virtual int GetHealDistance( void ) OVERRIDE { return m_nHealDistance; }
|
||||
virtual void TeamWin( int nTeam ) OVERRIDE;
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
CTFPlayer *GetTeamLeader( int iTeam ) const OVERRIDE;
|
||||
string_t GetCountdownImage( void ) OVERRIDE { return m_iszCountdownImage; }
|
||||
virtual bool IsUsingCustomCountdownImage( void ) OVERRIDE{ return m_bUsingCountdownImage; }
|
||||
|
||||
private:
|
||||
#ifdef GAME_DLL
|
||||
void PlaySound( const char *pszSound, CTFPlayer *pPlayer );
|
||||
virtual void OnRedScoreChanged() OVERRIDE;
|
||||
virtual void OnBlueScoreChanged() OVERRIDE;
|
||||
|
||||
void EvaluatePlayerCount();
|
||||
|
||||
void SetCountdownImage( string_t iszCountdownImage ) { m_iszCountdownImage = iszCountdownImage; }
|
||||
|
||||
string_t m_iszPropModelName;
|
||||
string_t m_iszPropDropSound;
|
||||
string_t m_iszPropPickupSound;
|
||||
|
||||
int m_nMinPoints;
|
||||
int m_nPointsPerPlayer;
|
||||
bool m_bMaxScoreUpdatingAllowed;
|
||||
|
||||
int m_nFlagResetDelay;
|
||||
int m_nHealDistance;
|
||||
|
||||
CObjectDispenser* CreateDispenser( int iTeam );
|
||||
CHandle< CObjectDispenser > m_hRedDispenser;
|
||||
CHandle< CObjectDispenser > m_hBlueDispenser;
|
||||
|
||||
COutputFloat m_OnRedScoreChanged;
|
||||
COutputFloat m_OnBlueScoreChanged;
|
||||
|
||||
COutputEvent m_OnCountdownTimerExpired;
|
||||
#endif // GAME_DLL
|
||||
|
||||
CNetworkVar( CHandle<CTFPlayer>, m_hRedTeamLeader );
|
||||
CNetworkVar( CHandle<CTFPlayer>, m_hBlueTeamLeader );
|
||||
|
||||
CNetworkVar( bool, m_bUsingCountdownImage );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
char m_iszCountdownImage[MAX_PATH];
|
||||
#else
|
||||
CNetworkVar( string_t, m_iszCountdownImage );
|
||||
int m_nPointsOnPlayerDeath;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CPlayerDestructionDispenser :
|
||||
#ifdef GAME_DLL
|
||||
public CObjectDispenser
|
||||
#else
|
||||
public C_ObjectDispenser
|
||||
#endif
|
||||
{
|
||||
#ifdef GAME_DLL
|
||||
DECLARE_CLASS( CPlayerDestructionDispenser, CObjectDispenser )
|
||||
#else
|
||||
DECLARE_CLASS( CPlayerDestructionDispenser, C_ObjectDispenser )
|
||||
#endif
|
||||
DECLARE_NETWORKCLASS();
|
||||
DECLARE_DATADESC();
|
||||
public:
|
||||
#ifdef GAME_DLL
|
||||
virtual float GetDispenserRadius( void ) OVERRIDE
|
||||
{
|
||||
if ( CTFPlayerDestructionLogic::GetRobotDestructionLogic() && ( CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetType() == CTFPlayerDestructionLogic::TYPE_PLAYER_DESTRUCTION ) )
|
||||
{
|
||||
return CTFPlayerDestructionLogic::GetRobotDestructionLogic()->GetHealDistance();
|
||||
}
|
||||
|
||||
return 450;
|
||||
}
|
||||
|
||||
virtual void Spawn( void ) OVERRIDE;
|
||||
void OnGoActive( void ) OVERRIDE;
|
||||
void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) OVERRIDE;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif// PLAYER_DESTRUCTION_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,319 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Entities for use in the Robot Destruction TF2 game mode.
|
||||
//
|
||||
//=========================================================================//
|
||||
#ifndef LOGIC_ROBOT_DESTRUCTION_H
|
||||
#define LOGIC_ROBOT_DESTRUCTION_H
|
||||
#pragma once
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "triggers.h"
|
||||
#include "tf_shareddefs.h"
|
||||
#include "GameEventListener.h"
|
||||
#include "entity_capture_flag.h"
|
||||
#else
|
||||
#include "c_tf_player.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include "tf_robot_destruction_robot.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFRobotDestructionLogic C_TFRobotDestructionLogic
|
||||
#define CTFRobotDestruction_RobotSpawn C_TFRobotDestruction_RobotSpawn
|
||||
#define CTFRobotDestruction_RobotGroup C_TFRobotDestruction_RobotGroup
|
||||
#endif
|
||||
|
||||
#include "props_shared.h"
|
||||
|
||||
#define RD_POINTS_STOLEN_PER_TICK 2
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFRobotDestruction_RobotSpawn : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_CLASS( CTFRobotDestruction_RobotSpawn, CBaseEntity )
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CTFRobotDestruction_RobotSpawn();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Activate() OVERRIDE;
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void Precache() OVERRIDE;
|
||||
virtual bool ShouldCollide( int collisionGroup, int contentsMask ) const OVERRIDE;
|
||||
|
||||
CTFRobotDestruction_Robot* GetRobot() const { return m_hRobot.Get(); }
|
||||
void OnRobotKilled();
|
||||
void ClearRobot();
|
||||
void SpawnRobot();
|
||||
void SetGroup( class CTFRobotDestruction_RobotGroup* pGroup ) { m_hGroup.Set( pGroup ); }
|
||||
// Inputs
|
||||
void InputSpawnRobot( inputdata_t &inputdata );
|
||||
|
||||
#endif
|
||||
private:
|
||||
CHandle< CTFRobotDestruction_Robot > m_hRobot;
|
||||
#ifdef GAME_DLL
|
||||
CHandle< class CTFRobotDestruction_RobotGroup > m_hGroup;
|
||||
RobotSpawnData_t m_spawnData;
|
||||
COutputEvent m_OnRobotKilled;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
DECLARE_AUTO_LIST( IRobotDestructionGroupAutoList );
|
||||
class CTFRobotDestruction_RobotGroup : public CBaseEntity, public IRobotDestructionGroupAutoList
|
||||
{
|
||||
DECLARE_DATADESC();
|
||||
DECLARE_CLASS( CTFRobotDestruction_RobotGroup, CBaseEntity )
|
||||
DECLARE_NETWORKCLASS();
|
||||
public:
|
||||
virtual ~CTFRobotDestruction_RobotGroup();
|
||||
#ifdef GAME_DLL
|
||||
|
||||
CTFRobotDestruction_RobotGroup();
|
||||
|
||||
virtual int UpdateTransmitState() OVERRIDE { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Activate() OVERRIDE;
|
||||
void AddToGroup( CTFRobotDestruction_RobotSpawn * pSpawn );
|
||||
void RemoveFromGroup( CTFRobotDestruction_RobotSpawn * pSpawn );
|
||||
void UpdateState();
|
||||
void RespawnRobots();
|
||||
int GetNumAliveBots() const;
|
||||
float GetTeamRespawnScale() const { return m_flTeamRespawnReductionScale; }
|
||||
|
||||
// Respawn functions
|
||||
void StopRespawnTimer();
|
||||
void StartRespawnTimerIfNeeded( CTFRobotDestruction_RobotGroup *pMasterGroup );
|
||||
void RespawnCountdownFinish();
|
||||
|
||||
void EnableUberForGroup();
|
||||
void DisableUberForGroup();
|
||||
|
||||
void OnRobotAttacked();
|
||||
void OnRobotKilled();
|
||||
void OnRobotSpawned();
|
||||
#else
|
||||
virtual void PostDataUpdate( DataUpdateType_t updateType ) OVERRIDE;
|
||||
virtual int GetTeamNumber( void ) const OVERRIDE { return m_iTeamNum; }
|
||||
virtual void SetDormant( bool bDormant ) OVERRIDE;
|
||||
#endif
|
||||
const char *GetHUDIcon() const { return m_pszHudIcon; }
|
||||
int GetGroupNumber() const { return m_nGroupNumber; }
|
||||
int GetState() const { return m_nState; }
|
||||
float GetRespawnStartTime() const { return m_flRespawnStartTime; }
|
||||
float GetRespawnEndTime() const { return m_flRespawnEndTime; }
|
||||
float GetLastAttackedTime() const { return m_flLastAttackedTime; }
|
||||
|
||||
private:
|
||||
|
||||
#ifdef GAME_DLL
|
||||
CUtlVector< CTFRobotDestruction_RobotSpawn* > m_vecSpawns;
|
||||
int m_nTeamNumber;
|
||||
IMPLEMENT_NETWORK_VAR_FOR_DERIVED( m_iTeamNum );
|
||||
float m_flRespawnTime;
|
||||
static float m_sflNextAllowedAttackAlertTime[ TF_TEAM_COUNT ];
|
||||
string_t m_iszHudIcon;
|
||||
float m_flTeamRespawnReductionScale;
|
||||
|
||||
COutputEvent m_OnRobotsRespawn;
|
||||
COutputEvent m_OnAllRobotsDead;
|
||||
#else
|
||||
int m_iTeamNum;
|
||||
#endif
|
||||
CNetworkString( m_pszHudIcon, MAX_PATH );
|
||||
CNetworkVar( int, m_nGroupNumber );
|
||||
CNetworkVar( int, m_nState );
|
||||
CNetworkVar( float, m_flRespawnStartTime );
|
||||
CNetworkVar( float, m_flRespawnEndTime );
|
||||
CNetworkVar( float, m_flLastAttackedTime );
|
||||
};
|
||||
|
||||
struct RateLimitedSound_t
|
||||
{
|
||||
RateLimitedSound_t( float flPause )
|
||||
{
|
||||
m_mapNextAllowedTime.SetLessFunc( DefLessFunc( const CBaseEntity* ) );
|
||||
m_flPause = flPause;
|
||||
}
|
||||
|
||||
float m_flPause;
|
||||
CUtlMap< const CBaseEntity*, float > m_mapNextAllowedTime;
|
||||
};
|
||||
|
||||
struct TeamSound_t
|
||||
{
|
||||
const char *m_pszYourTeam;
|
||||
const char *m_pszTheirTeam;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFRobotDestructionLogic : public CBaseEntity
|
||||
#ifdef GAME_DLL
|
||||
, public CGameEventListener
|
||||
#endif
|
||||
{
|
||||
DECLARE_CLASS( CTFRobotDestructionLogic, CBaseEntity )
|
||||
DECLARE_NETWORKCLASS();
|
||||
public:
|
||||
|
||||
enum EType
|
||||
{
|
||||
TYPE_ROBOT_DESTRUCTION,
|
||||
TYPE_PLAYER_DESTRUCTION,
|
||||
};
|
||||
|
||||
virtual EType GetType() const { return TYPE_ROBOT_DESTRUCTION; }
|
||||
|
||||
CTFRobotDestructionLogic();
|
||||
virtual ~CTFRobotDestructionLogic();
|
||||
static CTFRobotDestructionLogic* GetRobotDestructionLogic();
|
||||
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Precache() OVERRIDE;
|
||||
|
||||
float GetRespawnScaleForTeam( int nTeam ) const;
|
||||
int GetScore( int nTeam ) const;
|
||||
int GetTargetScore( int nTeam ) const;
|
||||
int GetMaxPoints() const { return m_nMaxPoints.Get(); }
|
||||
float GetFinaleWinTime( int nTeam ) const;
|
||||
float GetFinaleLength() const { return m_flFinaleLength; }
|
||||
void PlaySoundInfoForScoreEvent( CTFPlayer* pPlayer, bool bPositive, int nNewScore, int nTeam, RDScoreMethod_t eMethod = SCORE_UNDEFINED );
|
||||
RDScoreMethod_t GetLastScoreMethod( int nTeam ) const { return (RDScoreMethod_t)m_eWinningMethod[ nTeam ]; }
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
virtual void OnDataChanged( DataUpdateType_t type ) OVERRIDE;
|
||||
virtual void ClientThink() OVERRIDE;
|
||||
const char* GetResFile() const { return STRING( m_szResFile ); }
|
||||
|
||||
#else
|
||||
DECLARE_DATADESC();
|
||||
|
||||
virtual void Activate() OVERRIDE;
|
||||
virtual void FireGameEvent( IGameEvent * event ) OVERRIDE;
|
||||
|
||||
virtual int UpdateTransmitState() OVERRIDE { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
|
||||
CTFRobotDestruction_Robot * IterateRobots( CTFRobotDestruction_Robot * ) const;
|
||||
void RobotCreated( CTFRobotDestruction_Robot *pRobot );
|
||||
void RobotRemoved( CTFRobotDestruction_Robot *pRobot );
|
||||
void RobotAttacked( CTFRobotDestruction_Robot *pRobot );
|
||||
float GetScoringInterval() const { return m_flRobotScoreInterval; }
|
||||
void ScorePoints( int nTeam, int nPoints, RDScoreMethod_t eMethod, CTFPlayer *pPlayer );
|
||||
void AddRobotGroup( CTFRobotDestruction_RobotGroup* pGroup );
|
||||
void ManageGameState();
|
||||
void FlagCreated( int nTeam );
|
||||
void FlagDestroyed( int nTeam );
|
||||
|
||||
void DBG_SetMaxPoints( int nNewMax ) { m_nMaxPoints.Set( nNewMax ); }
|
||||
void InputRoundActivate( inputdata_t &inputdata );
|
||||
virtual int GetHealDistance( void ) { return 64; }
|
||||
#endif
|
||||
|
||||
virtual void SetCountdownEndTime( float flTime ){ m_flCountdownEndTime = flTime; }
|
||||
virtual float GetCountdownEndTime(){ return m_flCountdownEndTime; }
|
||||
virtual CTFPlayer *GetTeamLeader( int iTeam ) const { return NULL; }
|
||||
virtual string_t GetCountdownImage( void ) { return NULL_STRING; }
|
||||
virtual bool IsUsingCustomCountdownImage( void ) { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual void OnRedScoreChanged() {}
|
||||
virtual void OnBlueScoreChanged() {}
|
||||
void ApproachTargetScoresThink();
|
||||
int ApproachTeamTargetScore( int nTeam, int nApproachScore, int nCurrentScore );
|
||||
void PlaySoundInPlayersEars( CTFPlayer* pPlayer, const EmitSound_t& params ) const;
|
||||
void RedTeamWin();
|
||||
void BlueTeamWin();
|
||||
virtual void TeamWin( int nTeam );
|
||||
|
||||
typedef CUtlMap< int, CTFRobotDestruction_RobotGroup* > RobotSpawnMap_t;
|
||||
CUtlVector< CTFRobotDestruction_Robot* > m_vecRobots;
|
||||
CUtlVector< CTFRobotDestruction_RobotGroup * > m_vecSpawnGroups;
|
||||
float m_flLoserRespawnBonusPerBot;
|
||||
float m_flRobotScoreInterval;
|
||||
float m_flNextRedRobotAttackedAlertTime;
|
||||
float m_flNextBlueRobotAttackedAlertTime;
|
||||
int m_nNumFlagsOut[ TF_TEAM_COUNT ];
|
||||
bool m_bEducateNewConnectors;
|
||||
string_t m_iszResFile;
|
||||
|
||||
TeamSound_t m_AnnouncerProgressSound;
|
||||
CUtlMap< const char *, RateLimitedSound_t * > m_mapRateLimitedSounds;
|
||||
|
||||
CUtlVector< CTFPlayer* > m_vecEducatedPlayers;
|
||||
|
||||
// Outputs
|
||||
COutputEvent m_OnRedFinalePeriodEnd;
|
||||
COutputEvent m_OnBlueFinalePeriodEnd;
|
||||
COutputEvent m_OnBlueHitZeroPoints;
|
||||
COutputEvent m_OnRedHitZeroPoints;
|
||||
COutputEvent m_OnBlueHasPoints;
|
||||
COutputEvent m_OnRedHasPoints;
|
||||
COutputEvent m_OnBlueHitMaxPoints;
|
||||
COutputEvent m_OnRedHitMaxPoints;
|
||||
COutputEvent m_OnBlueLeaveMaxPoints;
|
||||
COutputEvent m_OnRedLeaveMaxPoints;
|
||||
|
||||
COutputEvent m_OnRedFirstFlagStolen;
|
||||
COutputEvent m_OnRedFlagStolen;
|
||||
COutputEvent m_OnRedLastFlagReturned;
|
||||
COutputEvent m_OnBlueFirstFlagStolen;
|
||||
COutputEvent m_OnBlueFlagStolen;
|
||||
COutputEvent m_OnBlueLastFlagReturned;
|
||||
#else
|
||||
float m_flLastTickSoundTime;
|
||||
#endif
|
||||
static CTFRobotDestructionLogic* m_sCTFRobotDestructionLogic;
|
||||
CNetworkVar( int, m_nMaxPoints );
|
||||
CNetworkVar( float, m_flFinaleLength );
|
||||
CNetworkVar( float, m_flBlueFinaleEndTime );
|
||||
CNetworkVar( float, m_flRedFinaleEndTime );
|
||||
CNetworkVar( int, m_nBlueScore );
|
||||
CNetworkVar( int, m_nRedScore );
|
||||
CNetworkVar( int, m_nBlueTargetPoints );
|
||||
CNetworkVar( int, m_nRedTargetPoints );
|
||||
CNetworkVar( float, m_flBlueTeamRespawnScale );
|
||||
CNetworkVar( float, m_flRedTeamRespawnScale );
|
||||
CNetworkString( m_szResFile, MAX_PATH );
|
||||
CNetworkArray( int, m_eWinningMethod, TF_TEAM_COUNT );
|
||||
CNetworkVar( float, m_flCountdownEndTime ); // used for player destruction countdown timers
|
||||
};
|
||||
|
||||
#ifdef GAME_DLL
|
||||
class CRobotDestructionVaultTrigger : public CBaseTrigger
|
||||
{
|
||||
DECLARE_CLASS( CRobotDestructionVaultTrigger, CBaseTrigger );
|
||||
DECLARE_DATADESC();
|
||||
|
||||
public:
|
||||
CRobotDestructionVaultTrigger();
|
||||
virtual void Spawn() OVERRIDE;
|
||||
virtual void Precache() OVERRIDE;
|
||||
|
||||
virtual bool PassesTriggerFilters( CBaseEntity *pOther ) OVERRIDE;
|
||||
virtual void StartTouch(CBaseEntity *pOther) OVERRIDE;
|
||||
virtual void EndTouch(CBaseEntity *pOther) OVERRIDE;
|
||||
|
||||
private:
|
||||
void StealPointsThink();
|
||||
int StealPoints( CTFPlayer *pPlayer );
|
||||
|
||||
bool m_bIsStealing;
|
||||
COutputEvent m_OnPointsStolen;
|
||||
COutputEvent m_OnPointsStartStealing;
|
||||
COutputEvent m_OnPointsEndStealing;
|
||||
};
|
||||
|
||||
#endif// GAME_DLL
|
||||
#endif// LOGIC_ROBOT_DESTRUCTION_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Entity that propagates mann vs machine stats
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TF_MANN_VS_MACHINE_STATS_H
|
||||
#define TF_MANN_VS_MACHINE_STATS_H
|
||||
|
||||
#include "tf_player_shared.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#define CTFPlayer C_TFPlayer
|
||||
#define CMannVsMachineStats C_MannVsMachineStats
|
||||
#define CMannVsMachineWaveStats C_MannVsMachineWaveStats
|
||||
#define CMannVsMachineLocalWaveStats C_MannVsMachineLocalWaveStats
|
||||
#define CMannVsMachineUpgradeEvent C_MannVsMachineUpgradeEvent
|
||||
#define CMannVsMachinePlayerWaveStats C_MannVsMachinePlayerWaveStats
|
||||
class C_TFPlayer;
|
||||
#else
|
||||
class CTFPlayer;
|
||||
#endif
|
||||
|
||||
//class CMannVsMachineStats;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public interface to hide the inner-workings (which allows me to iterate on it and not force everyone to recompile)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The types of events sent down to clients. Add to the end of the appropriate section
|
||||
// for backwards compatibility.
|
||||
enum eMannVsMachineEvent
|
||||
{
|
||||
kMVMEvent_Player_Points,
|
||||
kMVMEvent_Player_Death,
|
||||
kMVMEvent_Player_PickedUpCredits,
|
||||
kMVMEvent_Player_BoughtInstantRespawn,
|
||||
kMVMEvent_Player_BoughtBottle,
|
||||
kMVMEvent_Player_BoughtUpgrade,
|
||||
kMVMEvent_Player_ActiveUpgrades,
|
||||
// max
|
||||
kMVMEvent_Max = 255
|
||||
};
|
||||
|
||||
enum eMvMEnemyTypes
|
||||
{
|
||||
kMvMEnemy_Bot,
|
||||
kMvMEnemy_Giant,
|
||||
kMvMEnemy_Tank
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Stats for a wave
|
||||
struct CMannVsMachineWaveStats
|
||||
{
|
||||
CMannVsMachineWaveStats()
|
||||
{
|
||||
nCreditsDropped = 0;
|
||||
nCreditsAcquired = 0;
|
||||
nCreditsBonus = 0;
|
||||
nPlayerDeaths = 0;
|
||||
nBuyBacks = 0;
|
||||
nAttempts = 0;
|
||||
}
|
||||
|
||||
void ClearStats ()
|
||||
{
|
||||
nCreditsDropped = 0;
|
||||
nCreditsAcquired = 0;
|
||||
nCreditsBonus = 0;
|
||||
nPlayerDeaths = 0;
|
||||
nBuyBacks = 0;
|
||||
nAttempts = 0;
|
||||
}
|
||||
|
||||
void operator+=( const CMannVsMachineWaveStats &rhs )
|
||||
{
|
||||
nCreditsDropped += rhs.nCreditsDropped;
|
||||
nCreditsAcquired += rhs.nCreditsAcquired;
|
||||
nCreditsBonus += rhs.nCreditsBonus;
|
||||
nPlayerDeaths += rhs.nPlayerDeaths;
|
||||
nBuyBacks += rhs.nBuyBacks;
|
||||
nAttempts += rhs.nAttempts;
|
||||
}
|
||||
|
||||
DECLARE_EMBEDDED_NETWORKVAR();
|
||||
DECLARE_CLASS_NOBASE( CMannVsMachineWaveStats );
|
||||
|
||||
CNetworkVar( uint32, nCreditsDropped );
|
||||
CNetworkVar( uint32, nCreditsAcquired );
|
||||
CNetworkVar( uint32, nCreditsBonus );
|
||||
CNetworkVar( uint32, nPlayerDeaths );
|
||||
CNetworkVar( uint32, nBuyBacks );
|
||||
CNetworkVar( uint32, nAttempts );
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Stats for a wave
|
||||
struct CMannVsMachineLocalWaveStats
|
||||
{
|
||||
CMannVsMachineLocalWaveStats()
|
||||
{
|
||||
nCreditsDropped = 0;
|
||||
nCreditsAcquired = 0;
|
||||
nCreditsBonus = 0;
|
||||
nPlayerDeaths = 0;
|
||||
nBuyBacks = 0;
|
||||
nAttempts = 0;
|
||||
}
|
||||
|
||||
CMannVsMachineLocalWaveStats( const CMannVsMachineLocalWaveStats &rhs )
|
||||
{
|
||||
nCreditsDropped = rhs.nCreditsDropped;
|
||||
nCreditsAcquired = rhs.nCreditsAcquired;
|
||||
nCreditsBonus = rhs.nCreditsBonus;
|
||||
nPlayerDeaths = rhs.nPlayerDeaths;
|
||||
nBuyBacks = rhs.nBuyBacks;
|
||||
nAttempts = rhs.nAttempts;
|
||||
}
|
||||
|
||||
CMannVsMachineLocalWaveStats operator=( const CMannVsMachineLocalWaveStats &rhs )
|
||||
{
|
||||
nCreditsDropped = rhs.nCreditsDropped;
|
||||
nCreditsAcquired = rhs.nCreditsAcquired;
|
||||
nCreditsBonus = rhs.nCreditsBonus;
|
||||
nPlayerDeaths = rhs.nPlayerDeaths;
|
||||
nBuyBacks = rhs.nBuyBacks;
|
||||
nAttempts = rhs.nAttempts;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CMannVsMachineLocalWaveStats operator=( const CMannVsMachineWaveStats &rhs )
|
||||
{
|
||||
nCreditsDropped = rhs.nCreditsDropped;
|
||||
nCreditsAcquired = rhs.nCreditsAcquired;
|
||||
nCreditsBonus = rhs.nCreditsBonus;
|
||||
nPlayerDeaths = rhs.nPlayerDeaths;
|
||||
nBuyBacks = rhs.nBuyBacks;
|
||||
nAttempts = rhs.nAttempts;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator+=( const CMannVsMachineWaveStats &rhs )
|
||||
{
|
||||
nCreditsDropped += rhs.nCreditsDropped;
|
||||
nCreditsAcquired += rhs.nCreditsAcquired;
|
||||
nCreditsBonus += rhs.nCreditsBonus;
|
||||
nPlayerDeaths += rhs.nPlayerDeaths;
|
||||
nBuyBacks += rhs.nBuyBacks;
|
||||
nAttempts += rhs.nAttempts;
|
||||
}
|
||||
|
||||
uint32 nCreditsDropped;
|
||||
uint32 nCreditsAcquired;
|
||||
uint32 nCreditsBonus;
|
||||
uint32 nPlayerDeaths;
|
||||
uint32 nBuyBacks;
|
||||
uint32 nAttempts;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Player stats for a wave
|
||||
struct CMannVsMachineUpgradeEvent
|
||||
{
|
||||
uint16 nItemDef;
|
||||
uint16 nAttributeDef;
|
||||
uint16 nQuality;
|
||||
};
|
||||
typedef CUtlVector< CMannVsMachineUpgradeEvent > tMVMUpgradesVector;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CMannVsMachinePlayerStats
|
||||
{
|
||||
CMannVsMachinePlayerStats()
|
||||
: nDeaths( 0 )
|
||||
, nBotDamage( 0 )
|
||||
, nGiantDamage( 0 )
|
||||
, nTankDamage( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
CMannVsMachinePlayerStats( const CMannVsMachinePlayerStats &rhs )
|
||||
{
|
||||
nDeaths = rhs.nDeaths;
|
||||
nBotDamage = rhs.nBotDamage;
|
||||
nGiantDamage = rhs.nGiantDamage;
|
||||
nTankDamage = rhs.nTankDamage;
|
||||
}
|
||||
|
||||
uint32 nDeaths;
|
||||
uint32 nBotDamage;
|
||||
uint32 nGiantDamage;
|
||||
uint32 nTankDamage;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct CPlayerWaveSpendingStats
|
||||
{
|
||||
CPlayerWaveSpendingStats()
|
||||
: nCreditsSpentOnBuyBacks( 0 )
|
||||
, nCreditsSpentOnBottles( 0 )
|
||||
, nCreditsSpentOnUpgrades ( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
CPlayerWaveSpendingStats( const CPlayerWaveSpendingStats &rhs )
|
||||
{
|
||||
nCreditsSpentOnBuyBacks = rhs.nCreditsSpentOnBuyBacks;
|
||||
nCreditsSpentOnBottles = rhs.nCreditsSpentOnBottles;
|
||||
nCreditsSpentOnUpgrades = rhs.nCreditsSpentOnUpgrades;
|
||||
}
|
||||
|
||||
CPlayerWaveSpendingStats operator=( const CPlayerWaveSpendingStats &rhs )
|
||||
{
|
||||
nCreditsSpentOnBuyBacks = rhs.nCreditsSpentOnBuyBacks;
|
||||
nCreditsSpentOnBottles = rhs.nCreditsSpentOnBottles;
|
||||
nCreditsSpentOnUpgrades = rhs.nCreditsSpentOnUpgrades;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator+=( const CPlayerWaveSpendingStats &rhs )
|
||||
{
|
||||
nCreditsSpentOnBuyBacks += rhs.nCreditsSpentOnBuyBacks;
|
||||
nCreditsSpentOnBottles += rhs.nCreditsSpentOnBottles;
|
||||
nCreditsSpentOnUpgrades += rhs.nCreditsSpentOnUpgrades;
|
||||
}
|
||||
|
||||
uint32 nCreditsSpentOnBuyBacks;
|
||||
uint32 nCreditsSpentOnBottles;
|
||||
uint32 nCreditsSpentOnUpgrades; // Bottles are NOT upgrades in this list
|
||||
};
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// initialize the stats for Mann Vs Machine
|
||||
void MannVsMachineStats_Init();
|
||||
|
||||
// get the current wave
|
||||
uint32 MannVsMachineStats_GetCurrentWave();
|
||||
|
||||
// Reporting currency stats for game code
|
||||
uint32 MannVsMachineStats_GetAcquiredCredits( int idxWave = -1, bool bIncludeBonus = true );
|
||||
uint32 MannVsMachineStats_GetDroppedCredits( int idxWave = -1 );
|
||||
uint32 MannVsMachineStats_GetMissedCredits( int idxWave = -1 );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
struct edict_t;
|
||||
|
||||
// Reset the player events associated with the player, such as when they disconnect
|
||||
void MannVsMachineStats_ResetPlayerEvents( CTFPlayer *pTFPlayer );
|
||||
|
||||
// Round events
|
||||
void MannVsMachineStats_RoundEvent_CreditsDropped( uint32 waveIdx, int nAmount );
|
||||
|
||||
// Player events
|
||||
void MannVsMachineStats_PlayerEvent_PointsChanged( CTFPlayer *pTFPlayer, int nPoints );
|
||||
void MannVsMachineStats_PlayerEvent_Died( CTFPlayer *pTFPlayer );
|
||||
void MannVsMachineStats_PlayerEvent_Upgraded( CTFPlayer *pTFPlayer, uint16 nItemDef, uint16 nAttributeDef, uint16 nQuality, int16 nCost, bool bIsBottle );
|
||||
void MannVsMachineStats_PlayerEvent_PickedUpCredits( CTFPlayer *pTFPlayer, uint32 idxWave, int nCreditsAmount );
|
||||
void MannVsMachineStats_PlayerEvent_BoughtInstantRespawn( CTFPlayer *pTFPlayer, int nCost );
|
||||
|
||||
void MannVsMachineStats_SetPopulationFile( const char * pPopulationFile );
|
||||
#endif // GAME_DLL
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
float MannVsMachineStats_GetFirstEventTime();
|
||||
float MannVsMachineStats_GetLastEventTime();
|
||||
#endif // end defined(CLIENT_DLL)
|
||||
|
||||
struct CAllPlayerSpendingStats
|
||||
{
|
||||
CPlayerWaveSpendingStats m_playerStats[MAX_PLAYERS+1];
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Container class for all the mann vs machine stats we track for a round and for a player
|
||||
class CMannVsMachineStats : public CBaseEntity
|
||||
{
|
||||
DECLARE_CLASS( CMannVsMachineStats, CBaseEntity );
|
||||
public:
|
||||
DECLARE_NETWORKCLASS();
|
||||
|
||||
CMannVsMachineStats();
|
||||
virtual ~CMannVsMachineStats();
|
||||
|
||||
uint32 GetCurrentWave() const { return m_iCurrentWaveIdx; }
|
||||
|
||||
// CBaseEntity
|
||||
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_DONT_SAVE; }
|
||||
|
||||
void ResetStats( );
|
||||
void ResetPlayerEvents( CTFPlayer *pTFPlayer );
|
||||
void ResetUpgradeSpending( CTFPlayer *pTFPlayer );
|
||||
|
||||
// Dropped, Acquired stats
|
||||
uint32 GetAcquiredCredits( int iWaveIdx, bool bWithBonus = true );
|
||||
uint32 GetDroppedCredits( int iWaveIdx );
|
||||
uint32 GetMissedCredits( int iWaveIdx );
|
||||
uint32 GetBonusCredits ( int iWaveIdx );
|
||||
|
||||
#ifdef GAME_DLL
|
||||
virtual int UpdateTransmitState( void ) { return SetTransmitState( FL_EDICT_ALWAYS ); }
|
||||
|
||||
void SetMapName ( const char *pMapName ) { m_pMapName = pMapName; }
|
||||
void SetPopFile ( const char *pPopFile ) { m_pPopFileName = pPopFile; }
|
||||
|
||||
// Call when the round is over
|
||||
void RoundOver( bool bHumansWon );
|
||||
|
||||
// Set the current wave that will be updated
|
||||
void SetCurrentWave( uint32 idxWave );
|
||||
|
||||
// Round events.
|
||||
void RoundEvent_WaveStart();
|
||||
void RoundEvent_WaveEnd( bool bSuccess );
|
||||
void RoundEvent_AcquiredCredits( uint32 idxWave, int nAmount, bool bIsBonus );
|
||||
void RoundEvent_CreditsDropped( uint32 waveIdx, int nAmount );
|
||||
|
||||
// player events
|
||||
void PlayerEvent_PointsChanged( CTFPlayer *pTFPlayer, int nPoints );
|
||||
void PlayerEvent_Died( CTFPlayer *pTFPlayer );
|
||||
void PlayerEvent_Upgraded( CTFPlayer *pTFPlayer, uint16 nItemDef, uint16 nAttributeDef, uint8 nQuality, int16 nCost, bool bIsBottle );
|
||||
void PlayerEvent_PickedUpCredits( CTFPlayer *pTFPlayer, uint32 idxWave, int nCreditsAmount );
|
||||
void PlayerEvent_BoughtInstantRespawn( CTFPlayer *pTFPlayer, int nCost );
|
||||
|
||||
void PlayerEvent_DealtDamageToBots( CTFPlayer *pTFPlayer, int damage );
|
||||
void PlayerEvent_DealtDamageToGiants( CTFPlayer *pTFPlayer, int damage );
|
||||
void PlayerEvent_DealtDamageToTanks( CTFPlayer *pTFPlayer, int damage );
|
||||
|
||||
// send a user message to clients so that they can record what's going on a per player basis
|
||||
void NotifyPlayerEvent( CTFPlayer *pTFPlayer, uint32 idxWave, eMannVsMachineEvent eType, int nValue, int nParam = 0 );
|
||||
void NotifyTargetPlayerEvent( CTFPlayer *pTFPlayer, uint32 idxWave, eMannVsMachineEvent eType, int nCost );
|
||||
|
||||
void SendUpgradesToPlayer( CTFPlayer *pTFPlayer, CUtlVector< CUpgradeInfo > *upgrades );
|
||||
|
||||
void NotifyPlayerActiveUpgradeCosts( CTFPlayer *pTFPlayer, int nSpending );
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
// Shared stats
|
||||
void ClearCurrentPlayerWaveSpendingStats( int idxWave );
|
||||
CPlayerWaveSpendingStats *GetSpending( int iWaveIndex, uint64 steamId );
|
||||
int GetUpgradeSpending( CTFPlayer *pTFPlayer = NULL );
|
||||
int GetBottleSpending( CTFPlayer *pTFPlayer = NULL );
|
||||
int GetBuyBackSpending( CTFPlayer *pTFPlayer = NULL );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType );
|
||||
|
||||
// Message from Server about Client Upgrades
|
||||
void ClearLocalPlayerUpgrades ();
|
||||
void AddLocalPlayerUpgrade( int iPlayerClass, item_definition_index_t iItemDef );
|
||||
|
||||
int GetLocalPlayerUpgradeSpending( int idxWave );
|
||||
int GetLocalPlayerBottleSpending( int idxWave );
|
||||
int GetLocalPlayerBuyBackSpending ( int idxWave );
|
||||
|
||||
// Client Side Reporting
|
||||
void SW_ReportClientUpgradePurchase( uint8 waveIdx, uint16 nItemDef, uint16 nAttributeDef, uint8 nQuality, int16 nCost );
|
||||
void SW_ReportClientBuyBackPurchase( uint8 waveIdx, uint16 nCost );
|
||||
void SW_ReportClientWaveSummary( uint16 serverWaveID, CMannVsMachinePlayerStats stats );
|
||||
|
||||
CUtlVector< CUpgradeInfo > *GetLocalPlayerUpgrades() { return &m_vecLocalPlayerUpgrades; }
|
||||
CPlayerWaveSpendingStats *GetLocalSpending ( int iWaveIdx ); // Helper
|
||||
|
||||
void SetPlayerActiveUpgradeCosts( uint64 playerId, int nSpending );
|
||||
int GetPlayerActiveUpgradeCosts( uint64 playerId );
|
||||
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
// Respec
|
||||
uint16 GetNumRespecsEarnedInWave( void ) { return m_nRespecsAwardedInWave; }
|
||||
uint32 GetAcquiredCreditsForRespec( void ) { return m_iCurrencyCollectedForRespec; }
|
||||
#ifdef GAME_DLL
|
||||
void SetNumRespecsEarnedInWave( uint16 nNum ) { m_nRespecsAwardedInWave = nNum; }
|
||||
void SetAcquiredCreditsForRespec( uint16 nNum ) { m_iCurrencyCollectedForRespec = nNum; }
|
||||
#endif // GAME_DLL
|
||||
|
||||
private:
|
||||
// helper
|
||||
CMannVsMachineLocalWaveStats GetWaveStats( int iWaveIdx );
|
||||
|
||||
void OnStatsChanged();
|
||||
|
||||
#ifdef GAME_DLL
|
||||
|
||||
void ResetWaveStats();
|
||||
|
||||
// Submitting Data to OGS
|
||||
void SW_ReportWaveSummary ( int waveIndex, bool bIsSuccess );
|
||||
|
||||
CMannVsMachinePlayerStats m_playerStats[MAX_PLAYERS+1];
|
||||
|
||||
const char *m_pPopFileName;
|
||||
const char *m_pMapName;
|
||||
|
||||
#endif // GAME_DLL
|
||||
|
||||
CMannVsMachineWaveStats m_runningTotalWaveStats;
|
||||
CMannVsMachineWaveStats m_previousWaveStats;
|
||||
CMannVsMachineWaveStats m_currentWaveStats;
|
||||
|
||||
CNetworkVar( uint32, m_iCurrentWaveIdx );
|
||||
CNetworkVar( uint32, m_iServerWaveID );
|
||||
|
||||
// Shared stats
|
||||
CUtlMap< uint64, CPlayerWaveSpendingStats > m_currWaveSpendingStats;
|
||||
CUtlMap< uint64, CPlayerWaveSpendingStats > m_prevWaveSpendingStats; // CurrWave - 1
|
||||
CUtlMap< uint64, CPlayerWaveSpendingStats > m_allPrevWaveSpendingStats; // Total of all previous Waves
|
||||
|
||||
// Respec
|
||||
CNetworkVar( uint32, m_iCurrencyCollectedForRespec ); // Tracks total money collected, regardless of wave status
|
||||
CNetworkVar( uint16, m_nRespecsAwardedInWave );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
CUtlVector< CUpgradeInfo > m_vecLocalPlayerUpgrades;
|
||||
|
||||
CUtlMap< uint64, int > m_teamActiveUpgrades;
|
||||
#endif
|
||||
};
|
||||
|
||||
CMannVsMachineStats *MannVsMachineStats_GetInstance();
|
||||
|
||||
#endif // TF_MANN_VS_MACHINE_STATS_H
|
||||
@@ -0,0 +1,849 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_mapinfo.h"
|
||||
#include <filesystem.h>
|
||||
#include "GameEventListener.h"
|
||||
#include "econ_item_system.h"
|
||||
#include "tf_item_inventory.h"
|
||||
#include "econ_contribution.h"
|
||||
#include "tf_duel_summary.h"
|
||||
#include "gc_clientsystem.h"
|
||||
#include "tf_duckleaderboard.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_matchmaking_shared.h"
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "hud_macros.h"
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include <tier0/memdbgon.h>
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
ConVar tf_duck_upload_rate( "tf_duck_upload_rate", "2400", FCVAR_DEVELOPMENTONLY ); // Make this DevOnly At ship and 60 seconds
|
||||
#endif
|
||||
|
||||
const char *g_szLadderLeaderboardNames[] =
|
||||
{
|
||||
"tf2_ladder_6v6",
|
||||
"tf2_ladder_public",
|
||||
"tf2_ladder_9v9",
|
||||
"tf2_ladder_12v12",
|
||||
};
|
||||
COMPILE_TIME_ASSERT( ARRAYSIZE( g_szLadderLeaderboardNames ) == LADDER_LEADERBOARDS_MAX );
|
||||
|
||||
void __MsgFunc_EOTLDuckEvent( bf_read &msg );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int SortLeaderboardVec( LeaderboardEntry_t * const *p1, LeaderboardEntry_t * const *p2 )
|
||||
{
|
||||
return ( *p2 )->m_nScore - ( *p1 )->m_nScore;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void RetrieveLeaderboardEntries( LeaderboardScoresDownloaded_t &scores, CUtlVector< LeaderboardEntry_t* > &entries )
|
||||
{
|
||||
entries.PurgeAndDeleteElements();
|
||||
entries.EnsureCapacity( scores.m_cEntryCount );
|
||||
for ( int i = 0; i < scores.m_cEntryCount; ++i )
|
||||
{
|
||||
LeaderboardEntry_t *leaderboardEntry = new LeaderboardEntry_t;
|
||||
if ( steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( scores.m_hSteamLeaderboardEntries, i, leaderboardEntry, NULL, 0 ) )
|
||||
{
|
||||
entries.AddToTail( leaderboardEntry );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CLeaderboardInfo::CLeaderboardInfo( const char *pLeaderboardName )
|
||||
{
|
||||
m_pLeaderboardName = pLeaderboardName ? V_strdup( pLeaderboardName ) : NULL;
|
||||
memset( &findLeaderboardResults, 0, sizeof( findLeaderboardResults ) );
|
||||
iNumLeaderboardEntries = 0;
|
||||
m_kLeaderboardType = kMapLeaderboard;
|
||||
m_iMyScore = 0;
|
||||
m_bHasPendingUpdate = false;
|
||||
m_bLeaderboardFound = false;
|
||||
}
|
||||
|
||||
CLeaderboardInfo::~CLeaderboardInfo()
|
||||
{
|
||||
downloadedLeaderboardScoresGlobal.PurgeAndDeleteElements();
|
||||
downloadedLeaderboardScoresGlobalAroundUser.PurgeAndDeleteElements();
|
||||
downloadedLeaderboardScoresFriends.PurgeAndDeleteElements();
|
||||
delete m_pLeaderboardName;
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::RetrieveLeaderboardData()
|
||||
{
|
||||
if ( steamapicontext && steamapicontext->SteamUserStats() )
|
||||
{
|
||||
if ( m_kLeaderboardType == kMapLeaderboard )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( CFmtStr( "contributions_%s", m_pLeaderboardName ) );
|
||||
findLeaderboardCallback.Set( apicall, this, &CLeaderboardInfo::OnFindLeaderboard );
|
||||
}
|
||||
else if ( m_kLeaderboardType == kDuckLeaderboard || m_kLeaderboardType == kDuckStat )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( m_pLeaderboardName );
|
||||
findLeaderboardCallback.Set( apicall, this, &CLeaderboardInfo::OnFindLeaderboard );
|
||||
}
|
||||
else if ( m_kLeaderboardType == kLadderLeaderboard )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( m_pLeaderboardName );
|
||||
findLeaderboardCallback.Set( apicall, this, &CLeaderboardInfo::OnFindLeaderboard );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CLeaderboardInfo::DownloadLeaderboardData()
|
||||
{
|
||||
if ( !findLeaderboardResults.m_bLeaderboardFound )
|
||||
return false;
|
||||
|
||||
if ( m_kLeaderboardType == kMapLeaderboard )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobal, 1, 5 );
|
||||
downloadLeaderboardCallbackGlobal.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedGlobal );
|
||||
apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -2, 2 );
|
||||
downloadLeaderboardCallbackGlobalAroundUser.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedGlobalAroundUser );
|
||||
apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 1, 5 );
|
||||
downloadLeaderboardCallbackFriends.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedFriends );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( m_kLeaderboardType == kDuckLeaderboard )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, -6, 6 );
|
||||
downloadLeaderboardCallbackFriends.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedFriends );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( m_kLeaderboardType == kDuckStat )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 0, 0 );
|
||||
downloadLeaderboardCallbackFriends.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedFriends );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( m_kLeaderboardType == kLadderLeaderboard )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobal, 1, 100 );
|
||||
downloadLeaderboardCallbackGlobal.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedGlobal );
|
||||
apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( findLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, -45, 45 );
|
||||
downloadLeaderboardCallbackFriends.Set( apicall, this, &CLeaderboardInfo::OnLeaderboardScoresDownloadedFriends );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure )
|
||||
{
|
||||
findLeaderboardResults = *pResult;
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::OnLeaderboardScoresDownloadedGlobal( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
|
||||
{
|
||||
RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresGlobal );
|
||||
iNumLeaderboardEntries = steamapicontext->SteamUserStats()->GetLeaderboardEntryCount( findLeaderboardResults.m_hSteamLeaderboard );
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::OnLeaderboardScoresDownloadedGlobalAroundUser( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
|
||||
{
|
||||
RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresGlobalAroundUser );
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::OnLeaderboardScoresDownloadedFriends( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
|
||||
{
|
||||
RetrieveLeaderboardEntries( *pResult, downloadedLeaderboardScoresFriends );
|
||||
iNumLeaderboardEntries = steamapicontext->SteamUserStats()->GetLeaderboardEntryCount( findLeaderboardResults.m_hSteamLeaderboard );
|
||||
CSteamID localID;
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
localID = steamapicontext->SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
FOR_EACH_VEC( downloadedLeaderboardScoresFriends, i )
|
||||
{
|
||||
if ( downloadedLeaderboardScoresFriends[i]->m_steamIDUser == localID )
|
||||
{
|
||||
if ( m_iMyScore < downloadedLeaderboardScoresFriends[i]->m_nScore )
|
||||
{
|
||||
// First update on finding the leaderboard, any gotten kills need to add to accumulate
|
||||
if ( m_bLeaderboardFound == false )
|
||||
{
|
||||
if ( m_iMyScore > 0 )
|
||||
{
|
||||
m_bHasPendingUpdate = true;
|
||||
}
|
||||
m_iMyScore += downloadedLeaderboardScoresFriends[i]->m_nScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iMyScore = downloadedLeaderboardScoresFriends[i]->m_nScore;
|
||||
}
|
||||
}
|
||||
|
||||
// Use My Saved Score
|
||||
downloadedLeaderboardScoresFriends[i]->m_nScore = m_iMyScore;
|
||||
}
|
||||
}
|
||||
|
||||
downloadedLeaderboardScoresFriends.Sort( &SortLeaderboardVec );
|
||||
|
||||
m_bLeaderboardFound = true;
|
||||
}
|
||||
|
||||
void CLeaderboardInfo::SetMyScore( int score )
|
||||
{
|
||||
m_iMyScore = score;
|
||||
|
||||
if ( !m_bLeaderboardFound )
|
||||
return;
|
||||
|
||||
// Update my leaderboard and resort
|
||||
iNumLeaderboardEntries = steamapicontext->SteamUserStats()->GetLeaderboardEntryCount( findLeaderboardResults.m_hSteamLeaderboard );
|
||||
CSteamID localID;
|
||||
if ( steamapicontext && steamapicontext->SteamUser() )
|
||||
{
|
||||
localID = steamapicontext->SteamUser()->GetSteamID();
|
||||
}
|
||||
|
||||
FOR_EACH_VEC( downloadedLeaderboardScoresFriends, i )
|
||||
{
|
||||
if ( downloadedLeaderboardScoresFriends[i]->m_steamIDUser == localID )
|
||||
{
|
||||
if ( m_iMyScore > downloadedLeaderboardScoresFriends[i]->m_nScore )
|
||||
{
|
||||
// Use My Saved Score
|
||||
downloadedLeaderboardScoresFriends[i]->m_nScore = m_iMyScore;
|
||||
downloadedLeaderboardScoresFriends.Sort( &SortLeaderboardVec );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CMapInfoContainer : public CAutoGameSystemPerFrame, public CGameEventListener
|
||||
{
|
||||
public:
|
||||
|
||||
CMapInfoContainer()
|
||||
{
|
||||
memset( &m_findDuelLeaderboardResults, 0, sizeof( m_findDuelLeaderboardResults ) );
|
||||
m_flNextUpdateDuckScoreTime = Plat_FloatTime() + 10.0f;
|
||||
#ifdef CLIENT_DLL
|
||||
m_flNextDuckScoresUploadTime = Plat_FloatTime() + tf_duck_upload_rate.GetFloat();
|
||||
#endif
|
||||
m_flNextLadderUpdateTime = Plat_FloatTime() + 10.f;
|
||||
}
|
||||
|
||||
virtual char const *Name()
|
||||
{
|
||||
return "CMapInfoContainer";
|
||||
}
|
||||
|
||||
~CMapInfoContainer()
|
||||
{
|
||||
m_vecMapInfos.PurgeAndDeleteElements();
|
||||
m_downloadedDuelLeaderboardScores_GlobalAroundUser.PurgeAndDeleteElements();
|
||||
m_downloadedDuelLeaderboardScores_Friends.PurgeAndDeleteElements();
|
||||
|
||||
// For ducks
|
||||
m_vecDuckInfo.PurgeAndDeleteElements();
|
||||
// Ladders
|
||||
m_vecLadderLeaderboards.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
|
||||
virtual void LevelShutdownPreEntity()
|
||||
{
|
||||
// upload scores on level leave
|
||||
//DuckUploadPendingScores();
|
||||
}
|
||||
|
||||
// Gets called each frame
|
||||
virtual void Update( float frametime )
|
||||
{
|
||||
if ( m_flNextUpdateDuckScoreTime > 0 && m_flNextUpdateDuckScoreTime < Plat_FloatTime() )
|
||||
{
|
||||
if ( DownloadDuckLeaderboard() )
|
||||
{
|
||||
m_flNextUpdateDuckScoreTime = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_flNextLadderUpdateTime > 0.f && m_flNextLadderUpdateTime < Plat_FloatTime() )
|
||||
{
|
||||
if ( DownloadLadderLeaderboard() )
|
||||
{
|
||||
m_flNextLadderUpdateTime = -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
// Duck Journal is off, no longer uploading
|
||||
//if ( m_flNextDuckScoresUploadTime < Plat_FloatTime() )
|
||||
//{
|
||||
// // Hard limit the rate players can update scores
|
||||
// float flNextUpdateTime = tf_duck_upload_rate.GetFloat();
|
||||
// if ( DuckUploadPendingScores() )
|
||||
// {
|
||||
// // Request new score
|
||||
// m_flNextUpdateDuckScoreTime = Plat_FloatTime() + 10.0f;
|
||||
// }
|
||||
// // 4x as long if you don't have a Duck Journal
|
||||
// C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
|
||||
// if ( pPlayer )
|
||||
// {
|
||||
// static CSchemaAttributeDefHandle pAttr_DuckLevelBadge( "duck badge level" );
|
||||
// if ( pAttr_DuckLevelBadge )
|
||||
// {
|
||||
// CTFWearable *pActionItem = pPlayer->GetEquippedWearableForLoadoutSlot( LOADOUT_POSITION_ACTION );
|
||||
// // Don't care about the level, just if the attribute is found
|
||||
// if ( pActionItem && FindAttribute( pActionItem->GetAttributeContainer()->GetItem(), pAttr_DuckLevelBadge ) )
|
||||
// {
|
||||
// flNextUpdateTime *= 0.5f;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// m_flNextDuckScoresUploadTime = Plat_FloatTime() + flNextUpdateTime;
|
||||
//}
|
||||
}
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// for duels
|
||||
void DownloadDuelLeaderboard()
|
||||
{
|
||||
if ( m_findDuelLeaderboardResults.m_bLeaderboardFound )
|
||||
{
|
||||
// and start downloading the leaderboards
|
||||
// friends
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findDuelLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestFriends, 1, 10 );
|
||||
m_downloadLeaderboardCallback_Friends.Set( apicall, this, &CMapInfoContainer::OnDuelLeaderboardScoresDownloaded_Friends );
|
||||
// global around user
|
||||
apicall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntries( m_findDuelLeaderboardResults.m_hSteamLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -4, 5 );
|
||||
m_downloadLeaderboardCallback_GlobalAroundUser.Set( apicall, this, &CMapInfoContainer::OnDuelLeaderboardScoresDownloaded_GlobalAroundUser );
|
||||
}
|
||||
}
|
||||
|
||||
void OnFindDuelLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure )
|
||||
{
|
||||
m_findDuelLeaderboardResults = *pResult;
|
||||
DownloadDuelLeaderboard();
|
||||
}
|
||||
|
||||
void OnDuelLeaderboardScoresDownloaded_GlobalAroundUser( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
|
||||
{
|
||||
RetrieveLeaderboardEntries( *pResult, m_downloadedDuelLeaderboardScores_GlobalAroundUser );
|
||||
}
|
||||
|
||||
void OnDuelLeaderboardScoresDownloaded_Friends( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure )
|
||||
{
|
||||
RetrieveLeaderboardEntries( *pResult, m_downloadedDuelLeaderboardScores_Friends );
|
||||
}
|
||||
|
||||
// **************************************************************************************************************************
|
||||
bool DownloadLadderLeaderboard()
|
||||
{
|
||||
bool bDownloading = false;
|
||||
FOR_EACH_VEC( m_vecLadderLeaderboards, i )
|
||||
{
|
||||
bDownloading |= m_vecLadderLeaderboards[i]->DownloadLeaderboardData();
|
||||
}
|
||||
return bDownloading;
|
||||
}
|
||||
|
||||
CLeaderboardInfo *GetLadderLeaderboard( const char *pszName )
|
||||
{
|
||||
FOR_EACH_VEC( m_vecLadderLeaderboards, i )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = m_vecLadderLeaderboards[i];
|
||||
if ( pszName && pInfo && !V_strcmp( pszName, pInfo->GetLeaderboardName() ) )
|
||||
{
|
||||
return pInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// **************************************************************************************************************************
|
||||
bool DownloadDuckLeaderboard()
|
||||
{
|
||||
bool bDownloading = false;
|
||||
FOR_EACH_VEC( m_vecDuckInfo, i )
|
||||
{
|
||||
bDownloading |= m_vecDuckInfo[i]->DownloadLeaderboardData();
|
||||
}
|
||||
return bDownloading;
|
||||
}
|
||||
|
||||
CLeaderboardInfo *GetDuckLeaderboard( const char* kName )
|
||||
{
|
||||
FOR_EACH_VEC( m_vecDuckInfo, i )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = m_vecDuckInfo[i];
|
||||
if ( strstr( kName, pInfo->GetLeaderboardName() ) != NULL )
|
||||
{
|
||||
return pInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DuckUploadPendingScores()
|
||||
{
|
||||
return false;
|
||||
|
||||
//CSteamID localID;
|
||||
|
||||
//if ( !steamapicontext || !steamapicontext->SteamUser() )
|
||||
// return false;
|
||||
|
||||
//localID = steamapicontext->SteamUser()->GetSteamID();
|
||||
|
||||
//bool bUpdatedScores = false;
|
||||
//for ( int i = 0; i < DUCK_NUM_LEADERBOARDS; ++i )
|
||||
//{
|
||||
// CLeaderboardInfo *pLeaderboard = GetDuckLeaderboard( g_szDuckLeaderboardNames[i] );
|
||||
//
|
||||
// if ( pLeaderboard && pLeaderboard->IsLeaderboardFound() && pLeaderboard->HasPendingUpdate() )
|
||||
// {
|
||||
// pLeaderboard->SetHasPendingUpdate( false );
|
||||
// bUpdatedScores = true;
|
||||
|
||||
// int iScoreCheck = RandomInt( INT_MAX / 2, INT_MAX );
|
||||
// // Tell the GC to update our duck contribution
|
||||
// GCSDK::CProtoBufMsg<CGCMsgGC_PlayerDuckLeaderboard_IndividualUpdate> msg( k_EMsgGC_DuckLeaderboard_IndividualUpdate );
|
||||
// msg.Body().set_score( pLeaderboard->GetMyScore() );
|
||||
// msg.Body().set_type( i );
|
||||
|
||||
// MD5Context_t md5Context;
|
||||
// MD5Init( &md5Context );
|
||||
//
|
||||
// AccountID_t unAccountId = localID.GetAccountID();
|
||||
// int nScore = pLeaderboard->GetMyScore();
|
||||
|
||||
// MD5Update( &md5Context, static_cast<const uint8 *>( (void *)&unAccountId ), sizeof( unAccountId ) );
|
||||
// MD5Update( &md5Context, static_cast<const uint8 *>( (void *)&nScore ), sizeof( nScore ) );
|
||||
// MD5Update( &md5Context, static_cast<const uint8 *>( (void *)&i ), sizeof( i ) );
|
||||
// MD5Update( &md5Context, static_cast<const uint8 *>( (void *)&TF_DUCK_ID ), sizeof( TF_DUCK_ID ) );
|
||||
// MD5Update( &md5Context, static_cast<const uint8 *>( (void *)&iScoreCheck ), sizeof( iScoreCheck ) );
|
||||
//
|
||||
// MD5Value_t md5Result;
|
||||
// MD5Final( &md5Result.bits[0], &md5Context );
|
||||
// msg.Body().set_score_id( &md5Result.bits[0], MD5_DIGEST_LENGTH );
|
||||
// msg.Body().set_score_check( iScoreCheck );
|
||||
// GCClientSystem()->BSendMessage( msg );
|
||||
// }
|
||||
//}
|
||||
//return bUpdatedScores;
|
||||
}
|
||||
|
||||
void DuckUpdateScore( int iIncrement, EDuckLeaderboardTypes kLeaderboard )
|
||||
{
|
||||
// Get Current Score
|
||||
CLeaderboardInfo *pLeaderboard = GetDuckLeaderboard( g_szDuckLeaderboardNames[kLeaderboard] );
|
||||
int iCurrentScore = pLeaderboard->GetMyScore();
|
||||
int iNewScore = iCurrentScore + iIncrement;
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
int iOldLevel = iCurrentScore / DUCK_XP_SCALE;
|
||||
int iNewLevel = iNewScore / DUCK_XP_SCALE;
|
||||
|
||||
if ( iNewLevel > iOldLevel )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "duck_xp_level_up" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "level", iNewLevel );
|
||||
gameeventmanager->FireEventClientSide( event );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set my new score
|
||||
pLeaderboard->SetMyScore( iNewScore );
|
||||
pLeaderboard->SetHasPendingUpdate( true );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
virtual bool Init()
|
||||
{
|
||||
ListenForGameEvent( "item_schema_initialized" );
|
||||
#ifdef CLIENT_DLL
|
||||
HOOK_MESSAGE( EOTLDuckEvent );
|
||||
#endif // CLIENT_DLL
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void FireGameEvent( IGameEvent *event )
|
||||
{
|
||||
if ( Q_strcmp( event->GetName(), "item_schema_initialized" ) != 0 )
|
||||
return;
|
||||
|
||||
for ( int i = 0; i < GetItemSchema()->GetMapCount(); i++ )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = new CLeaderboardInfo( GetItemSchema()->GetMasterMapDefByIndex( i )->pszMapName );
|
||||
pInfo->m_kLeaderboardType = kMapLeaderboard;
|
||||
m_vecMapInfos.AddToTail( pInfo );
|
||||
|
||||
const MapDef_t *pMapDef = GetItemSchema()->GetMasterMapDefByName( pInfo->GetLeaderboardName() );
|
||||
if ( pMapDef && pMapDef->IsCommunityMap() )
|
||||
{
|
||||
// retrieve leaderboard info
|
||||
pInfo->RetrieveLeaderboardData();
|
||||
}
|
||||
}
|
||||
|
||||
// find duel leaderboards
|
||||
if ( steamapicontext && steamapicontext->SteamUserStats() )
|
||||
{
|
||||
SteamAPICall_t apicall = steamapicontext->SteamUserStats()->FindLeaderboard( "duel_wins" );
|
||||
m_findLeaderboardCallback.Set( apicall, this, &CMapInfoContainer::OnFindDuelLeaderboard );
|
||||
}
|
||||
|
||||
// find duck leaderboards
|
||||
for ( int i = 0; i < DUCK_NUM_LEADERBOARDS; i++ )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = new CLeaderboardInfo( g_szDuckLeaderboardNames[ i ] );
|
||||
pInfo->m_kLeaderboardType = i == 0 ? kDuckLeaderboard : kDuckStat;
|
||||
m_vecDuckInfo.AddToTail( pInfo );
|
||||
|
||||
// retrieve leaderboard info
|
||||
pInfo->RetrieveLeaderboardData();
|
||||
}
|
||||
|
||||
// Ladder
|
||||
for ( int i = 0; i < LADDER_LEADERBOARDS_MAX; i++ )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = new CLeaderboardInfo( g_szLadderLeaderboardNames[i] );
|
||||
pInfo->m_kLeaderboardType = kLadderLeaderboard;
|
||||
m_vecLadderLeaderboards.AddToTail( pInfo );
|
||||
|
||||
// retrieve leaderboard info
|
||||
pInfo->RetrieveLeaderboardData();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CUtlVector< CLeaderboardInfo* > m_vecMapInfos;
|
||||
// for duels
|
||||
CCallResult< CMapInfoContainer, LeaderboardFindResult_t > m_findLeaderboardCallback;
|
||||
CCallResult< CMapInfoContainer, LeaderboardScoresDownloaded_t > m_downloadLeaderboardCallback_GlobalAroundUser;
|
||||
CCallResult< CMapInfoContainer, LeaderboardScoresDownloaded_t > m_downloadLeaderboardCallback_Friends;
|
||||
LeaderboardFindResult_t m_findDuelLeaderboardResults;
|
||||
CUtlVector< LeaderboardEntry_t* > m_downloadedDuelLeaderboardScores_GlobalAroundUser;
|
||||
CUtlVector< LeaderboardEntry_t* > m_downloadedDuelLeaderboardScores_Friends;
|
||||
|
||||
// For ducks
|
||||
CUtlVector< CLeaderboardInfo* > m_vecDuckInfo;
|
||||
float m_flNextUpdateDuckScoreTime;
|
||||
float m_flNextDuckScoresUploadTime;
|
||||
|
||||
// Ladders
|
||||
CUtlVector< CLeaderboardInfo* > m_vecLadderLeaderboards;
|
||||
float m_flNextLadderUpdateTime;
|
||||
};
|
||||
CMapInfoContainer gMapInfoContainer;
|
||||
|
||||
static CLeaderboardInfo *FindMapInfo( const char *pMapName )
|
||||
{
|
||||
FOR_EACH_VEC( gMapInfoContainer.m_vecMapInfos, i )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = gMapInfoContainer.m_vecMapInfos[i];
|
||||
if ( strstr( pMapName, pInfo->GetLeaderboardName() ) != NULL )
|
||||
return pInfo;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool Leaderboards_GetDuelWins( CUtlVector< LeaderboardEntry_t* > &scores, bool bGlobal )
|
||||
{
|
||||
if ( gMapInfoContainer.m_findDuelLeaderboardResults.m_bLeaderboardFound )
|
||||
{
|
||||
if ( bGlobal )
|
||||
{
|
||||
scores = gMapInfoContainer.m_downloadedDuelLeaderboardScores_GlobalAroundUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
scores = gMapInfoContainer.m_downloadedDuelLeaderboardScores_Friends;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// DUCKS
|
||||
void Leaderboards_GetDuckLeaderboardSteamIDs( CUtlVector< AccountID_t > &vecIds )
|
||||
{
|
||||
vecIds.RemoveAll();
|
||||
FOR_EACH_VEC( gMapInfoContainer.m_vecDuckInfo, i )
|
||||
{
|
||||
FOR_EACH_VEC( gMapInfoContainer.m_vecDuckInfo[i]->downloadedLeaderboardScoresFriends, iEntry )
|
||||
{
|
||||
vecIds.AddToHead( gMapInfoContainer.m_vecDuckInfo[i]->downloadedLeaderboardScoresFriends[iEntry]->m_steamIDUser.GetAccountID() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Leaderboards_GetDuckLeaderboard( CUtlVector< LeaderboardEntry_t* > &scores, const char* kName )
|
||||
{
|
||||
CLeaderboardInfo *pLeaderboard = gMapInfoContainer.GetDuckLeaderboard( kName );
|
||||
if ( pLeaderboard && pLeaderboard->IsLeaderboardFound() )
|
||||
{
|
||||
scores = pLeaderboard->downloadedLeaderboardScoresFriends;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Leaderboards_GetDuckLeaderboardTotalEntryCount( const char* kName )
|
||||
{
|
||||
CLeaderboardInfo *pLeaderboard = gMapInfoContainer.GetDuckLeaderboard( kName );
|
||||
if ( pLeaderboard )
|
||||
{
|
||||
return pLeaderboard->iNumLeaderboardEntries;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DUCK Collected Message from Server
|
||||
void __MsgFunc_EOTLDuckEvent( bf_read &msg )
|
||||
{
|
||||
#ifdef CLIENT_DLL
|
||||
CBasePlayer *pLocalPlayer = CBasePlayer::GetLocalPlayer();
|
||||
if ( !pLocalPlayer )
|
||||
return;
|
||||
|
||||
if ( TFGameRules() && TFGameRules()->HaveCheatsBeenEnabledDuringLevel() )
|
||||
return;
|
||||
|
||||
// IsCreated, ID of Creator, ID of Victim, Count, IsGolden
|
||||
int iIsCreated = (int)msg.ReadByte();
|
||||
int iCreatorId = (int)msg.ReadByte();
|
||||
int iVictimId = (int)msg.ReadByte();
|
||||
int iToucherId = (int)msg.ReadByte();
|
||||
int iDuckTeam = (int)msg.ReadByte();
|
||||
int iCount = (int)msg.ReadByte();
|
||||
int iDuckFlags = (int)msg.ReadByte();
|
||||
|
||||
iDuckTeam = 0;
|
||||
iVictimId = 0;
|
||||
//iDuckFlags = 0;
|
||||
|
||||
CBasePlayer *pCreator = UTIL_PlayerByIndex( iCreatorId );
|
||||
//CBasePlayer *pVictim = UTIL_PlayerByIndex( iVictimId );
|
||||
CBasePlayer *pToucher = UTIL_PlayerByIndex( iToucherId );
|
||||
|
||||
if ( !pCreator )
|
||||
{
|
||||
iDuckFlags |= DUCK_FLAG_OBJECTIVE;
|
||||
}
|
||||
// If you were picked up, you need a toucher
|
||||
if ( iIsCreated == 0 && pToucher )
|
||||
{
|
||||
// if I picked them up
|
||||
if ( pToucher == pLocalPlayer )
|
||||
{
|
||||
// Offense
|
||||
if ( pCreator && pCreator->GetTeamNumber() == pLocalPlayer->GetTeamNumber() )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_PERSONAL_PICKUP_OFFENSE );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount * DUCK_XP_WEIGHT_OFFENSE, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
//defense
|
||||
else if ( pCreator && pCreator->GetTeamNumber() != pLocalPlayer->GetTeamNumber() )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_PERSONAL_PICKUP_DEFENDED );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount * DUCK_XP_WEIGHT_DEFENSE, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
|
||||
// objective
|
||||
if ( iDuckFlags & DUCK_FLAG_OBJECTIVE )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_PERSONAL_PICKUP_OBJECTIVE );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount* DUCK_XP_WEIGHT_OBJECTIVE, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
|
||||
// bonus
|
||||
if ( iDuckFlags & DUCK_FLAG_BONUS )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_PERSONAL_BONUS_PICKUP );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount* DUCK_XP_WEIGHT_BONUS, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
}
|
||||
// Teammate picks up a duck I made
|
||||
else if ( pCreator && pCreator == pLocalPlayer && pCreator->GetTeamNumber() == pToucher->GetTeamNumber() )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_TEAM_PICKUP_MY_DUCKS );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount * DUCK_XP_WEIGHT_TEAMMATE, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
}
|
||||
// Duck Created
|
||||
else if ( iIsCreated != 0 )
|
||||
{
|
||||
// If this is the same as local player
|
||||
if ( pLocalPlayer == pCreator )
|
||||
{
|
||||
gMapInfoContainer.DuckUpdateScore( iCount, TF_DUCK_SCORING_PERSONAL_GENERATION );
|
||||
gMapInfoContainer.DuckUpdateScore( iCount * DUCK_XP_WEIGHT_GENERATION, TF_DUCK_SCORING_OVERALL_RATING );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Leaderboards_Refresh()
|
||||
{
|
||||
gMapInfoContainer.DownloadDuelLeaderboard();
|
||||
gMapInfoContainer.DownloadDuckLeaderboard();
|
||||
gMapInfoContainer.DownloadLadderLeaderboard();
|
||||
}
|
||||
|
||||
void MapInfo_RefreshLeaderboard( const char *pMapName )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = FindMapInfo( pMapName );
|
||||
if ( pInfo )
|
||||
{
|
||||
pInfo->DownloadLeaderboardData();
|
||||
}
|
||||
}
|
||||
|
||||
bool MapInfo_GetLeaderboardInfo( const char *pMapName, CUtlVector< LeaderboardEntry_t* > &scores, int &iNumLeaderboardEntries, uint32 unMinScores )
|
||||
{
|
||||
CLeaderboardInfo *pInfo = FindMapInfo( pMapName );
|
||||
if ( pInfo && pInfo->findLeaderboardResults.m_bLeaderboardFound )
|
||||
{
|
||||
if ( (uint32)pInfo->downloadedLeaderboardScoresFriends.Count() >= unMinScores )
|
||||
{
|
||||
scores = pInfo->downloadedLeaderboardScoresFriends;
|
||||
}
|
||||
else if ( (uint32)pInfo->downloadedLeaderboardScoresGlobalAroundUser.Count() >= unMinScores )
|
||||
{
|
||||
scores = pInfo->downloadedLeaderboardScoresGlobalAroundUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
scores = pInfo->downloadedLeaderboardScoresGlobal;
|
||||
}
|
||||
iNumLeaderboardEntries = pInfo->iNumLeaderboardEntries;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *FindMapNameForContributionDefinitionIndex( item_definition_index_t unContribDefIndex )
|
||||
{
|
||||
for ( int i = 0; i < GetItemSchema()->GetMapCount(); i++ )
|
||||
{
|
||||
const MapDef_t* pMapDef = GetItemSchema()->GetMasterMapDefByIndex( i );
|
||||
if ( pMapDef->mapStampDef && pMapDef->mapStampDef->GetDefinitionIndex() == unContribDefIndex )
|
||||
return pMapDef->pszMapName;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool MapInfo_DidPlayerDonate( uint32 unAccountID, const char *pLevelName )
|
||||
{
|
||||
if ( steamapicontext == NULL || steamapicontext->SteamUser() == NULL )
|
||||
return false;
|
||||
|
||||
CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
CSteamID steamID = localSteamID;
|
||||
steamID.SetAccountID( unAccountID );
|
||||
|
||||
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
|
||||
if ( pSOCache == NULL )
|
||||
return false;
|
||||
|
||||
GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CTFMapContribution::k_nTypeID );
|
||||
if ( pTypeCache == NULL )
|
||||
return false;
|
||||
|
||||
char pchBaseMapName[ MAX_PATH ];
|
||||
Q_FileBase( pLevelName, pchBaseMapName, sizeof(pchBaseMapName) );
|
||||
|
||||
for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
|
||||
{
|
||||
CTFMapContribution *pMapContribution = (CTFMapContribution*)( pTypeCache->GetObject( i ) );
|
||||
|
||||
const char *pszMapName = FindMapNameForContributionDefinitionIndex( pMapContribution->Obj().def_index() );
|
||||
if ( pszMapName && FStrEq( pszMapName, pchBaseMapName ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int MapInfo_GetDonationAmount( uint32 unAccountID, const char *pLevelName )
|
||||
{
|
||||
if ( steamapicontext == NULL || steamapicontext->SteamUser() == NULL )
|
||||
return 0;
|
||||
|
||||
CSteamID localSteamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
CSteamID steamID = localSteamID;
|
||||
steamID.SetAccountID( unAccountID );
|
||||
|
||||
GCSDK::CGCClientSharedObjectCache *pSOCache = GCClientSystem()->GetSOCache( steamID );
|
||||
if ( pSOCache == NULL )
|
||||
return 0;
|
||||
|
||||
GCSDK::CGCClientSharedObjectTypeCache *pTypeCache = pSOCache->FindTypeCache( CTFMapContribution::k_nTypeID );
|
||||
if ( pTypeCache == NULL )
|
||||
return 0;
|
||||
|
||||
char pchBaseMapName[ MAX_PATH ];
|
||||
Q_FileBase( pLevelName, pchBaseMapName, sizeof(pchBaseMapName) );
|
||||
|
||||
for ( uint32 i = 0; i < pTypeCache->GetCount(); ++i )
|
||||
{
|
||||
CTFMapContribution *pMapContribution = (CTFMapContribution*)( pTypeCache->GetObject( i ) );
|
||||
|
||||
const char *pszMapName = FindMapNameForContributionDefinitionIndex( pMapContribution->Obj().def_index() );
|
||||
if ( pszMapName && FStrEq( pszMapName, pchBaseMapName ) )
|
||||
return pMapContribution->Obj().contribution_level();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Ladders
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Leaderboards_GetLadderLeaderboard( CUtlVector< LeaderboardEntry_t* > &scores, const char *pszName, bool bGlobal )
|
||||
{
|
||||
CLeaderboardInfo *pLeaderboard = gMapInfoContainer.GetLadderLeaderboard( pszName );
|
||||
if ( pLeaderboard && pLeaderboard->IsLeaderboardFound() )
|
||||
{
|
||||
scores = bGlobal ? pLeaderboard->downloadedLeaderboardScoresGlobal : pLeaderboard->downloadedLeaderboardScoresFriends;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void Leaderboards_LadderRefresh( void )
|
||||
{
|
||||
gMapInfoContainer.DownloadLadderLeaderboard();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_MAPINFO_H
|
||||
#define TF_MAPINFO_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// Formally Mapinfo
|
||||
enum ETFLeaderboardType
|
||||
{
|
||||
kMapLeaderboard,
|
||||
kDuckLeaderboard,
|
||||
kDuckStat,
|
||||
kLadderLeaderboard,
|
||||
};
|
||||
|
||||
class CLeaderboardInfo
|
||||
{
|
||||
public:
|
||||
CCallResult< CLeaderboardInfo, LeaderboardFindResult_t > findLeaderboardCallback;
|
||||
CCallResult< CLeaderboardInfo, LeaderboardScoresDownloaded_t > downloadLeaderboardCallbackGlobal;
|
||||
CCallResult< CLeaderboardInfo, LeaderboardScoresDownloaded_t > downloadLeaderboardCallbackGlobalAroundUser;
|
||||
CCallResult< CLeaderboardInfo, LeaderboardScoresDownloaded_t > downloadLeaderboardCallbackFriends;
|
||||
LeaderboardFindResult_t findLeaderboardResults;
|
||||
CUtlVector< LeaderboardEntry_t* > downloadedLeaderboardScoresGlobal;
|
||||
CUtlVector< LeaderboardEntry_t* > downloadedLeaderboardScoresGlobalAroundUser;
|
||||
CUtlVector< LeaderboardEntry_t* > downloadedLeaderboardScoresFriends;
|
||||
int iNumLeaderboardEntries;
|
||||
ETFLeaderboardType m_kLeaderboardType;
|
||||
|
||||
CLeaderboardInfo( const char *pLeaderboardName );
|
||||
~CLeaderboardInfo();
|
||||
|
||||
const char *GetLeaderboardName() { return m_pLeaderboardName; }
|
||||
|
||||
void RetrieveLeaderboardData();
|
||||
bool DownloadLeaderboardData();
|
||||
void OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure );
|
||||
void OnLeaderboardScoresDownloadedGlobal( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure );
|
||||
void OnLeaderboardScoresDownloadedGlobalAroundUser( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure );
|
||||
void OnLeaderboardScoresDownloadedFriends( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure );
|
||||
|
||||
void SetMyScore ( int score );
|
||||
int GetMyScore() { return m_iMyScore; }
|
||||
|
||||
bool HasPendingUpdate() { return m_bHasPendingUpdate; }
|
||||
void SetHasPendingUpdate( bool bStatus ) { m_bHasPendingUpdate = bStatus; }
|
||||
|
||||
bool IsLeaderboardFound() { return m_bLeaderboardFound; }
|
||||
|
||||
private:
|
||||
const char *m_pLeaderboardName;
|
||||
int m_iMyScore;
|
||||
bool m_bHasPendingUpdate;
|
||||
bool m_bLeaderboardFound;
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh the leaderboard for the given map
|
||||
* @param pMapName
|
||||
*/
|
||||
void MapInfo_RefreshLeaderboard( const char *pMapName );
|
||||
|
||||
/**
|
||||
* Retrieve the leaderboard for the given map
|
||||
* @param pMapName
|
||||
* @param scores
|
||||
* @return true if the leaderboard info was retrieved, false otherwise
|
||||
*/
|
||||
bool MapInfo_GetLeaderboardInfo( const char *pMapName, CUtlVector< LeaderboardEntry_t* > &scores, int &iNumLeaderboardEntries, uint32 unMinScores );
|
||||
|
||||
/**
|
||||
* @param unAccountID
|
||||
* @param pLevelName
|
||||
* @return how many times the player has donated
|
||||
*/
|
||||
int MapInfo_GetDonationAmount( uint32 unAccountID, const char *pLevelName );
|
||||
|
||||
/**
|
||||
* @param unAccountID
|
||||
* @param pLevelName
|
||||
* return true if the player donated to the current map, false otherwise
|
||||
*/
|
||||
bool MapInfo_DidPlayerDonate( uint32 unAccountID, const char *pLevelName );
|
||||
|
||||
/**
|
||||
* Retrieve the duel wins leaderboard
|
||||
* @param scores
|
||||
* @param bGlobal
|
||||
* return true if the duel wins leaderboard were retrieved, false otherwise
|
||||
*/
|
||||
bool Leaderboards_GetDuelWins( CUtlVector< LeaderboardEntry_t* > &scores, bool bGlobal );
|
||||
|
||||
// Get a list of AccountID's for all people on the Duck Leaderboards
|
||||
void Leaderboards_GetDuckLeaderboardSteamIDs( CUtlVector< AccountID_t > &vecIds );
|
||||
|
||||
/**
|
||||
* Retrieve the duel wins leaderboard
|
||||
* @param scores
|
||||
* @param bGlobal
|
||||
* return true if the duel wins leaderboard were retrieved, false otherwise
|
||||
*/
|
||||
bool Leaderboards_GetDuckLeaderboard( CUtlVector< LeaderboardEntry_t* > &scores, const char* kName );
|
||||
|
||||
// Get total number of entries for a leaderboard type
|
||||
int Leaderboards_GetDuckLeaderboardTotalEntryCount( const char* kName );
|
||||
|
||||
/**
|
||||
* Refreshes leaderboards not associated with maps
|
||||
*/
|
||||
void Leaderboards_Refresh();
|
||||
|
||||
/**
|
||||
* Retrieve the competitive ladder ratings leaderboard
|
||||
* @param scores
|
||||
* @param nType
|
||||
* return true if the leaderboard were retrieved, false otherwise
|
||||
*/
|
||||
bool Leaderboards_GetLadderLeaderboard( CUtlVector< LeaderboardEntry_t* > &scores, const char *pszName, bool bGlobal );
|
||||
void Leaderboards_LadderRefresh( void );
|
||||
#endif // TF_MAPINFO_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,258 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_MATCH_DESCRIPTION_H
|
||||
#define TF_MATCH_DESCRIPTION_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tf_matchmaking_shared.h"
|
||||
#ifdef CLIENT_DLL
|
||||
#include "basemodel_panel.h"
|
||||
#endif // CLIENT_DLL
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// Can't foward declare CMatchInfo::PlayerMatchData_t because C++. Bummer.
|
||||
#include "tf_gc_server.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef GC_DLL
|
||||
class CTFLobby;
|
||||
class CTFParty;
|
||||
struct MatchDescription_t;
|
||||
struct MatchParty_t;
|
||||
#endif
|
||||
|
||||
class CSOTFLadderData;
|
||||
|
||||
enum EMatchType_t
|
||||
{
|
||||
MATCH_TYPE_NONE = 0,
|
||||
MATCH_TYPE_MVM,
|
||||
MATCH_TYPE_COMPETITIVE,
|
||||
MATCH_TYPE_CASUAL
|
||||
};
|
||||
|
||||
struct LevelInfo_t
|
||||
{
|
||||
uint32 m_nLevelNum;
|
||||
uint32 m_nStartXP; // Inclusive
|
||||
uint32 m_nEndXP; // Non-inclusive
|
||||
const char* m_pszLevelIcon; // Kill this when we do models
|
||||
const char* m_pszLevelTitle;
|
||||
const char* m_pszLevelUpSound;
|
||||
const char* m_pszLobbyBackgroundImage;
|
||||
};
|
||||
|
||||
struct XPSourceDef_t
|
||||
{
|
||||
const char* m_pszSoundName;
|
||||
const char* m_pszFormattingLocToken;
|
||||
const char* m_pszTypeLocToken;
|
||||
float m_flValueMultiplier;
|
||||
};
|
||||
|
||||
extern const XPSourceDef_t g_XPSourceDefs[ CMsgTFXPSource_XPSourceType_NUM_SOURCE_TYPES ];
|
||||
|
||||
class IProgressionDesc
|
||||
{
|
||||
public:
|
||||
IProgressionDesc( EMatchGroup eMatchGroup
|
||||
, const char* pszBadgeName
|
||||
, const char* pszProgressionResFile
|
||||
, const char* pszLevelToken );
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual void SetupBadgePanel( CBaseModelPanel *pModelPanel, const LevelInfo_t& level ) const = 0;
|
||||
virtual const uint32 GetLocalPlayerLastAckdExperience() const = 0;
|
||||
virtual const uint32 GetPlayerExperienceBySteamID( CSteamID steamid ) const = 0;
|
||||
virtual const LevelInfo_t& YieldingGetLevelForSteamID( const CSteamID& steamID ) const;
|
||||
#endif // CLIENT_DLL
|
||||
#if defined GC_DLL
|
||||
// XXX(JohnS): This should go away once XP is just a rating type, no need for match description to have different
|
||||
// implementations of how the job does it.
|
||||
virtual bool BYldAcknowledgePlayerXPOnTransaction( CSQLAccess &transaction, CTFSharedObjectCache *pLockedSOCache ) const = 0;
|
||||
// XXX(JohnS): Same, this is super specific and hacky.
|
||||
virtual const bool BRankXPIsActuallyPrimaryMMRating() const = 0;
|
||||
#endif // defined GC_DLL
|
||||
virtual const LevelInfo_t& GetLevelForExperience( uint32 nExperience ) const;
|
||||
const LevelInfo_t& GetLevelByNumber( uint32 nNumber ) const;
|
||||
uint32 GetNumLevels() const { return m_vecLevels.Count(); }
|
||||
|
||||
#if defined GC_DLL || ( defined STAGING_ONLY && defined CLIENT_DLL )
|
||||
virtual void DebugSpewLevels() const = 0;
|
||||
#endif
|
||||
|
||||
const CUtlString m_strBadgeName;
|
||||
const char* m_pszLevelToken;
|
||||
const char* m_pszProgressionResFile;
|
||||
|
||||
protected:
|
||||
#ifdef CLIENT_DLL
|
||||
void EnsureBadgePanelModel( CBaseModelPanel *pModelPanel ) const;
|
||||
#endif
|
||||
|
||||
const EMatchGroup m_eMatchGroup;
|
||||
CUtlVector< LevelInfo_t > m_vecLevels;
|
||||
};
|
||||
|
||||
struct MatchDesc_t
|
||||
{
|
||||
EMatchMode m_eLateJoinMode;
|
||||
EMMPenaltyPool m_ePenaltyPool;
|
||||
bool m_bUsesSkillRatings;
|
||||
bool m_bSupportsLowPriorityQueue;
|
||||
bool m_bRequiresMatchID;
|
||||
const ConVar* m_pmm_required_score;
|
||||
bool m_bUseMatchHud;
|
||||
const char* m_pszExecFileName;
|
||||
const ConVar* m_pmm_match_group_size;
|
||||
const ConVar* m_pmm_match_group_size_minimum; // Optional
|
||||
EMatchType_t m_eMatchType;
|
||||
bool m_bShowPreRoundDoors;
|
||||
bool m_bShowPostRoundDoors;
|
||||
const char* m_pszMatchEndKickWarning;
|
||||
const char* m_pszMatchStartSound;
|
||||
bool m_bAutoReady;
|
||||
bool m_bShowRankIcons;
|
||||
bool m_bUseMatchSummaryStage;
|
||||
bool m_bDistributePerformanceMedals;
|
||||
bool m_bIsCompetitiveMode;
|
||||
bool m_bUseFirstBlood;
|
||||
bool m_bUseReducedBonusTime;
|
||||
bool m_bUseAutoBalance;
|
||||
bool m_bAllowTeamChange;
|
||||
bool m_bRandomWeaponCrits;
|
||||
bool m_bFixedWeaponSpread;
|
||||
// If we should not allow match to complete without a complete set of players.
|
||||
bool m_bRequireCompleteMatch;
|
||||
bool m_bTrustedServersOnly;
|
||||
bool m_bForceClientSettings;
|
||||
bool m_bAllowDrawingAtMatchSummary;
|
||||
bool m_bAllowSpecModeChange;
|
||||
bool m_bAutomaticallyRequeueAfterMatchEnds;
|
||||
bool m_bUsesMapVoteOnRoundEnd;
|
||||
bool m_bUsesXP;
|
||||
bool m_bUsesDashboardOnRoundEnd;
|
||||
bool m_bUsesSurveys;
|
||||
// Be strict about finding quality matches, for more-competitive matchgroups that want to prioritize match quality
|
||||
// over speed.
|
||||
bool m_bStrictMatchmakerScoring;
|
||||
};
|
||||
|
||||
class IMatchGroupDescription
|
||||
{
|
||||
public:
|
||||
|
||||
IMatchGroupDescription( EMatchGroup eMatchGroup, const MatchDesc_t& params )
|
||||
: m_eMatchGroup( eMatchGroup )
|
||||
, m_params( params )
|
||||
, m_pProgressionDesc( NULL )
|
||||
{}
|
||||
|
||||
|
||||
#ifdef GC_DLL
|
||||
// What rating the matchmaker should use to evaluate players in this matchgroup
|
||||
virtual EMMRating PrimaryMMRatingBackend() const = 0;
|
||||
|
||||
// What ratings match results in this ladder group should run updates on
|
||||
virtual const std::vector< EMMRating > &MatchResultRatingBackends() const = 0;
|
||||
|
||||
// When creating a match from the first party, what to copy over
|
||||
virtual bool InitMatchFromParty( MatchDescription_t* pMatch, const MatchParty_t* pParty ) const = 0;
|
||||
|
||||
// When finding late joiners for an already-in-play lobby
|
||||
virtual bool InitMatchFromLobby( MatchDescription_t* pMatch, CTFLobby* pLobby ) const = 0;
|
||||
|
||||
// Sync a match party with a CTFParty
|
||||
virtual void SyncMatchParty( const CTFParty *pParty, MatchParty_t *pMatchParty ) const = 0;
|
||||
|
||||
// A match has formed, what game mode paremeters do we want to set? (ie. MvM Popfile, 12v12 map, etc)
|
||||
virtual void SelectModeSpecificParameters( const MatchDescription_t* pMatch, CTFLobby* pLobby ) const = 0;
|
||||
|
||||
// Get which server pool to use
|
||||
virtual int GetServerPoolIndex( EMatchGroup eGroup, EMMServerMode eMode ) const;
|
||||
|
||||
// Get server details
|
||||
virtual void GetServerDetails( const CMsgGameServerMatchmakingStatus& msg, int& nChallengeIndex, const char* pszMap ) const = 0;
|
||||
|
||||
virtual const char* GetUnauthorizedPartyReason( CTFParty* pParty ) const = 0;
|
||||
|
||||
virtual void Dump( const char *pszLeader, int nSpewLevel, int nLogLevel, const MatchParty_t* pMatch ) const = 0;
|
||||
|
||||
//
|
||||
// Threaded calls. These are called in work items, and should be pure functions
|
||||
//
|
||||
|
||||
// Check if pMatch is compatible with pCandidateParty -- that is, if BIntersectMatchWithParty would succeed.
|
||||
virtual bool BThreadedPartyCompatibleWithMatch( const MatchDescription_t* pMatch, const MatchParty_t *pCurrentParty ) const = 0;
|
||||
|
||||
// When adding a party to a match, what intersection of current state with the incoming party gets copied to the
|
||||
// match. This returns false if the party isn't compatible, e.g. if !BThreadedPartyCompatibleWithMatch
|
||||
virtual bool BThreadedIntersectMatchWithParty( MatchDescription_t* pMatch, const MatchParty_t* pParty ) const = 0;
|
||||
|
||||
// Check if two parties are compatible, that is, if they could be added to the same match absent other criteria
|
||||
virtual bool BThreadedPartiesCompatible( const MatchParty_t *pLeftParty, const MatchParty_t *pRightParty ) const = 0;
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
virtual bool BGetRoundStartBannerParameters( int& nSkin, int& nBodyGroup ) const = 0;
|
||||
virtual bool BGetRoundDoorParameters( int& nSkin, int& nLogoBodyGroup ) const = 0;
|
||||
virtual const char *GetMapLoadBackgroundOverride( bool bWideScreen ) const = 0;
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
// ! Check return, we might fail to setup
|
||||
virtual bool InitServerSettingsForMatch( const CTFGSLobby* pLobby ) const;
|
||||
virtual void InitGameRulesSettings() const = 0;
|
||||
virtual void InitGameRulesSettingsPostEntity() const = 0;
|
||||
virtual void PostMatchClearServerSettings() const = 0;
|
||||
virtual bool ShouldRequestLateJoin() const = 0;
|
||||
virtual bool BMatchIsSafeToLeaveForPlayer( const CMatchInfo* pMatchInfo, const CMatchInfo::PlayerMatchData_t *pMatchPlayer ) const = 0;
|
||||
virtual bool BPlayWinMusic( int nWinningTeam, bool bGameOver ) const = 0;
|
||||
#endif
|
||||
|
||||
// Accessors for param values
|
||||
inline int GetMatchSize() const { return m_params.m_pmm_match_group_size->GetInt(); }
|
||||
inline bool BShouldAutomaticallyRequeueOnMatchEnd() const { return m_params.m_bAutomaticallyRequeueAfterMatchEnds; }
|
||||
inline bool BUsesMapVoteAfterMatchEnds() const { return m_params.m_bUsesMapVoteOnRoundEnd; }
|
||||
inline bool BUsesXP() const { return m_params.m_bUsesXP; }
|
||||
inline bool BUsesDashboard() const { return m_params.m_bUsesDashboardOnRoundEnd; }
|
||||
inline bool BUsesStrictMatchmakerScoring() const { return m_params.m_bStrictMatchmakerScoring; }
|
||||
inline bool BRequiresCompleteMatches() const { return m_params.m_bRequireCompleteMatch; }
|
||||
inline bool BRequiresMatchID() const { return m_params.m_bRequiresMatchID; }
|
||||
|
||||
// Meta-permissions that are based on other set flags
|
||||
//
|
||||
// Only match-vote modes need this ability right now
|
||||
inline bool BCanServerRequestNewMatchForLobby() const { return BUsesMapVoteAfterMatchEnds(); }
|
||||
// Auto-balance and anything that is allowed to roll new match lobbies needs to have this ability (for speculative
|
||||
// matches if the GC is unavailable). It should be possible to add a mode where we do rolling matches, but only
|
||||
// when the GC is responding, which would not need the unilateral-team-assignment ability
|
||||
inline bool BCanServerChangeMatchPlayerTeams() const { return BCanServerRequestNewMatchForLobby() || m_params.m_bUseAutoBalance; }
|
||||
|
||||
#ifdef GC_DLL
|
||||
inline bool BUsesSkillRatings() const { return m_params.m_bUsesSkillRatings; }
|
||||
inline int GetMinimumMatchSize() const
|
||||
{
|
||||
int min = m_params.m_pmm_match_group_size_minimum ? m_params.m_pmm_match_group_size_minimum->GetInt() : -1;
|
||||
return ( min >= 0 ) ? min : GetMatchSize();
|
||||
}
|
||||
inline bool BUsesSurveys() const { return m_params.m_bUsesSurveys; }
|
||||
#endif
|
||||
|
||||
inline bool BIsTrustedServersOnly() const { return m_params.m_bTrustedServersOnly; }
|
||||
|
||||
const EMatchGroup m_eMatchGroup;
|
||||
const MatchDesc_t m_params;
|
||||
const IProgressionDesc* m_pProgressionDesc;
|
||||
};
|
||||
|
||||
const IMatchGroupDescription* GetMatchGroupDescription( const EMatchGroup& eGroup );
|
||||
|
||||
#endif //TF_MATCH_DESCRIPTION_H
|
||||
@@ -0,0 +1,205 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
#include "tf_quickplay_shared.h"
|
||||
|
||||
//
|
||||
// NOTE: This actually declares global variables and is intended to
|
||||
// only be included ONCE on the client, and ONCE on the GC.
|
||||
//
|
||||
|
||||
#ifdef GC
|
||||
#define TF2SCORECONVAR(name, defaultval, desc) GCConVar name(#name, defaultval, FCVAR_REPLICATED, desc)
|
||||
#else
|
||||
#define TF2SCORECONVAR(name, defaultval, desc) ConVar name(#name, defaultval, FCVAR_NONE, desc)
|
||||
#endif
|
||||
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_serverfull_headroom, "1", "Scoring will consider the server 'full' when this many slots are available" );
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_hrs_a, "8.00", "Valve server scoring bonus: hours played A" );
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_pts_a, "0.30", "Valve server scoring bonus: bonus points A" );
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_hrs_b, "16.00", "Valve server scoring bonus: hours played B" );
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_valve_bonus_pts_b, "0.00", "Valve server scoring bonus: bonus points B" );
|
||||
TF2SCORECONVAR(tf_matchmaking_numbers_increase_maxplayers_penalty, "0.50", "Max scoring penalty to servers that have increased the max number of players" );
|
||||
TF2SCORECONVAR(tf_matchmaking_retry_cooldown_seconds, "300", "Time to remember quickplay join attempt, and apply scoring penalty to rejoin the same server" );
|
||||
TF2SCORECONVAR(tf_matchmaking_retry_max_penalty, "1.0", "Max scoring penalty to rejoin a server previously matched. (Decays linearly over the cooldown period)" );
|
||||
TF2SCORECONVAR(tf_matchmaking_noob_map_score_boost, "0.75", "Boost added for quick-plaay scoring purposes if you are a noob and the map is considered noob-friendly" );
|
||||
TF2SCORECONVAR(tf_matchmaking_noob_hours_played, "8.0", "Number of hours played to determine 'noob' status for quickplay scoring purposes" );
|
||||
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_a, "50.0f", "Quickplay scoring ping time data point A" );
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_a_score, "0.9", "Quickplay scoring ping score data point A" );
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_b, "150.0f", "Quickplay scoring ping time data point B" );
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_b_score, "0.0", "Quickplay scoring ping score data point B" );
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_c, "300.0f", "Quickplay scoring ping time data point C" );
|
||||
TF2SCORECONVAR(tf_matchmaking_ping_c_score, "-1.0", "Quickplay scoring ping score data point C" );
|
||||
|
||||
TF2SCORECONVAR(tf_matchmaking_goodenough_score_start, "8.5", "Good enough score at start of search" );
|
||||
TF2SCORECONVAR(tf_matchmaking_goodenough_count_start, "20", "Good enough count at start of search" );
|
||||
TF2SCORECONVAR(tf_matchmaking_goodenough_score_end, "7.0", "Good enough score at end of search" );
|
||||
TF2SCORECONVAR(tf_matchmaking_goodenough_count_end, "5", "Good enough count at end of search" );
|
||||
|
||||
TF2SCORECONVAR( tf_mm_options_bonus, "0.5", "Scoring bonus when approaching tobor rating." );
|
||||
TF2SCORECONVAR( tf_mm_options_penalty, "-0.25", "Scoring penalty when options score is too far outside an acceptable range." );
|
||||
|
||||
TF2SCORECONVAR( tf_matchmaking_server_player_count_score, "1.5", "Maximum score when server is at/near optimal player count." );
|
||||
|
||||
//ConVar tf_matchmaking_goodenough_hi_score_start( "tf_matchmaking_goodenough_hi_score_start", "6.0", FCVAR_NONE );
|
||||
//ConVar tf_matchmaking_goodenough_hi_count_start( "tf_matchmaking_goodenough_hi_count_start", "5", FCVAR_NONE );
|
||||
//ConVar tf_matchmaking_goodenough_hi_score_end( "tf_matchmaking_goodenough_hi_score_end", "3.0", FCVAR_NONE );
|
||||
//ConVar tf_matchmaking_goodenough_hi_count_end( "tf_matchmaking_goodenough_hi_count_end", "2", FCVAR_NONE );
|
||||
|
||||
ConVar tf_matchmaking_max_search_time( "tf_matchmaking_max_search_time", "45", FCVAR_NONE );
|
||||
|
||||
#undef TF2SCORECONVAR
|
||||
|
||||
static inline float lerp( float inA, float outA, float inB, float outB, float x )
|
||||
{
|
||||
Assert( inA != inB );
|
||||
return outA + ( outB - outA ) * ( x - inA ) / ( inB - inA );
|
||||
}
|
||||
|
||||
struct TF2ScoringNumbers_t
|
||||
{
|
||||
|
||||
//
|
||||
// If we do further experiments, we should make distinct enum values, so we can easily
|
||||
// compare the stats on the backend
|
||||
//
|
||||
enum ExperimentGroup_t
|
||||
{
|
||||
k_ExperimentGroup_None, // no experiment active
|
||||
|
||||
//
|
||||
// Experiment 1
|
||||
//
|
||||
k_ExperimentGroup_Experiment1_Control = 1,
|
||||
k_ExperimentGroup_Experiment1_ValveBias = 2,
|
||||
k_ExperimentGroup_Experiment1_ValveBiasInactive = 3,
|
||||
k_ExperimentGroup_Experiment1_CommunityBias = 4,
|
||||
k_ExperimentGroup_Experiment1_CommunityBiasInactive = 5,
|
||||
};
|
||||
|
||||
ExperimentGroup_t m_eExperimentGroup;
|
||||
|
||||
TF2ScoringNumbers_t( CSteamID whosAsking )
|
||||
{
|
||||
m_eExperimentGroup = k_ExperimentGroup_None;
|
||||
|
||||
//
|
||||
// Assign experiment group for experiment 1
|
||||
//
|
||||
switch ( whosAsking.GetAccountID() & 3 )
|
||||
{
|
||||
case 0:
|
||||
// 80% chance to adjust behaviour
|
||||
if ( RandomFloat( 0.0f, 1.0f) < 0.8f )
|
||||
{
|
||||
m_eExperimentGroup = k_ExperimentGroup_Experiment1_ValveBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eExperimentGroup = k_ExperimentGroup_Experiment1_ValveBiasInactive;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// 80% chance to adjust behaviour
|
||||
if ( RandomFloat( 0.0f, 1.0f) < 0.8f )
|
||||
{
|
||||
m_eExperimentGroup = k_ExperimentGroup_Experiment1_CommunityBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eExperimentGroup = k_ExperimentGroup_Experiment1_CommunityBiasInactive;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
m_eExperimentGroup = k_ExperimentGroup_Experiment1_Control;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
float QuickplayCalculateServerScore( int numHumans, int numBots, int maxPlayers, int nNumInSearchParty )
|
||||
{
|
||||
Assert( nNumInSearchParty > 0 );
|
||||
|
||||
// Safety check against a degenerate case with invalid max number of players.
|
||||
// Protects against some bad math below
|
||||
if ( maxPlayers < kTFQuickPlayMinMaxNumberOfPlayers )
|
||||
{
|
||||
return -100.0f;
|
||||
}
|
||||
if ( maxPlayers > kTFQuickPlayMaxPlayers )
|
||||
{
|
||||
// Server should have been filtered, but in case we get here...
|
||||
maxPlayers = kTFQuickPlayMaxPlayers;
|
||||
}
|
||||
|
||||
float score = 0.0;
|
||||
|
||||
// Check for completely full server
|
||||
int newNumHumans = numHumans + nNumInSearchParty;
|
||||
int newNumTotalPlayers = newNumHumans + numBots;
|
||||
if ( newNumTotalPlayers + tf_matchmaking_numbers_serverfull_headroom.GetInt() > maxPlayers )
|
||||
{
|
||||
// Server full! Huge penalty!
|
||||
score += -100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data points for piecewise linear interpolation.
|
||||
// First point is implied: empty server is a score of zero.
|
||||
//
|
||||
// Then we increase up to point A
|
||||
int playerCountA = maxPlayers / 3;
|
||||
float scoreA = 0.20f;
|
||||
|
||||
// Next data point is when the score peaks at 100%
|
||||
int idealPlayerCount = maxPlayers * 5 / 6;
|
||||
|
||||
// Finally, the last data point when the server is full.
|
||||
// This choice reflects a pretty steep dropoff. This server
|
||||
// is already in a good state, we should begin to send players
|
||||
// to other servers so that they can start to fill up, and reduce
|
||||
// the race condition with players trying to join nearly full
|
||||
// servers and being too late and getting rejected.
|
||||
float scoreFull = scoreA;
|
||||
float flMaxScore = Max( tf_matchmaking_server_player_count_score.GetFloat(), 0.1f );
|
||||
|
||||
// Do the piecewise linear interpolation
|
||||
if ( newNumHumans <= playerCountA )
|
||||
{
|
||||
score += lerp( 0, 0.0f, playerCountA, scoreA, float( newNumHumans ) );
|
||||
}
|
||||
else if ( newNumHumans <= idealPlayerCount )
|
||||
{
|
||||
// Interpolate from point A up to 100%
|
||||
score += lerp( float( playerCountA ), scoreA, idealPlayerCount, flMaxScore, float( newNumHumans ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Greater than ideal. Interpolate back down to the score when the server is full
|
||||
score += lerp( idealPlayerCount, flMaxScore, maxPlayers, scoreFull, float( newNumHumans ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Don't apply a penalty anymore. Instead, we just let players express their preference
|
||||
// // Give a penalty for servers that increase the max player
|
||||
// // number above the ideal.
|
||||
// if ( maxPlayers > kTFQuickPlayIdealMaxNumberOfPlayers )
|
||||
// {
|
||||
// // Max penalty, if they increased it up all the way to
|
||||
// // kTFQuickPlayMaxPlayers. (Above this, we reject them completely)
|
||||
// int nExcessPlayers = maxPlayers - kTFQuickPlayIdealMaxNumberOfPlayers;
|
||||
// const int kMaxExcessPlayers = kTFQuickPlayMaxPlayers - kTFQuickPlayIdealMaxNumberOfPlayers;
|
||||
// float penalty = tf_matchmaking_numbers_increase_maxplayers_penalty.GetFloat() * (float)nExcessPlayers / (float)kMaxExcessPlayers;
|
||||
// score -= penalty;
|
||||
// }
|
||||
|
||||
//// being tagged as quickplay is roughly the same weight as best ping and best ratio of player numbers
|
||||
//if ( bHasQuickplayTag )
|
||||
//{
|
||||
// item.score += 2.0f;
|
||||
//}
|
||||
|
||||
return score;
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Matchmaking stuff shared between GC and gameserver / client
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "cbase.h"
|
||||
#include "tf_matchmaking_shared.h"
|
||||
#include "tf_match_description.h"
|
||||
#include "tf_ladder_data.h"
|
||||
|
||||
#ifdef GC_DLL
|
||||
#include "tf_lobbymanager.h"
|
||||
#include "tf_partymanager.h"
|
||||
#include "tf_party.h"
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT_DLL
|
||||
#include "tf_gc_client.h"
|
||||
#include "tf_gamerules.h"
|
||||
#endif
|
||||
|
||||
#ifdef GAME_DLL
|
||||
#include "tf_gc_server.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "tf_party.h"
|
||||
#endif
|
||||
|
||||
const char *s_pszMatchGroups[] =
|
||||
{
|
||||
"MatchGroup_MvM_Practice",
|
||||
"MatchGroup_MvM_MannUp",
|
||||
|
||||
"MatchGroup_Ladder_6v6",
|
||||
"MatchGroup_Ladder_9v9",
|
||||
"MatchGroup_Ladder_12v12",
|
||||
|
||||
"MatchGroup_Casual_6v6",
|
||||
"MatchGroup_Casual_9v9",
|
||||
"MatchGroup_Casual_12v12",
|
||||
};
|
||||
|
||||
COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszMatchGroups ) == k_nMatchGroup_Count );
|
||||
|
||||
#ifdef GC_DLL
|
||||
void On6v6MatchSizeChanged( IConVar *pConVar, const char *pOldString, float flOldValue )
|
||||
{
|
||||
if ( !GGCBase()->BIsInLogonSurge() && !GGCTF()->GetIsShuttingDown() && TFLobbyManager()->GetMatchmaker() )
|
||||
{
|
||||
TFLobbyManager()->GetMatchmaker()->timeRemove6v6LadderGroupsExpire = CRTime::RTime32DateAdd( CRTime::RTime32TimeCur(), 10.f, k_ETimeUnitSecond );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined GC_DLL
|
||||
#define GCConVar ConVar
|
||||
#define FCVAR_MATCHSIZE_THING ( FCVAR_REPLICATED )
|
||||
#else
|
||||
#define FCVAR_MATCHSIZE_THING 0
|
||||
#endif
|
||||
|
||||
GCConVar tf_mm_match_size_mvm( "tf_mm_match_size_mvm", "6", FCVAR_MATCHSIZE_THING,
|
||||
"How many players in an MvM matchmade group?" );
|
||||
GCConVar tf_mm_match_size_ladder_6v6( "tf_mm_match_size_ladder_6v6", "12", FCVAR_MATCHSIZE_THING,
|
||||
"Number of players required to play a 6v6 ladder game.", true, 1, true, 12
|
||||
#ifdef GC_DLL
|
||||
, On6v6MatchSizeChanged
|
||||
#endif
|
||||
);
|
||||
|
||||
GCConVar tf_mm_match_size_ladder_9v9( "tf_mm_match_size_ladder_9v9", "18", FCVAR_MATCHSIZE_THING,
|
||||
"Number of players required to play a 9v9 ladder game." );
|
||||
GCConVar tf_mm_match_size_ladder_12v12( "tf_mm_match_size_ladder_12v12", "24", FCVAR_MATCHSIZE_THING,
|
||||
"Number of players required to play a 12v12 ladder game." );
|
||||
GCConVar tf_mm_match_size_ladder_12v12_minimum( "tf_mm_match_size_ladder_12v12_minimum", "12", FCVAR_MATCHSIZE_THING,
|
||||
"Specifies the minimum number of players needed to launch a 12v12 match. Set to -1 to disable." );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Init internal bitvec with ints from the protobuf message
|
||||
//-----------------------------------------------------------------------------
|
||||
CCasualCriteriaHelper::CCasualCriteriaHelper( const CMsgCasualMatchmakingSearchCriteria& criteria )
|
||||
{
|
||||
m_mapsBits.Resize( GetItemSchema()->GetMasterMapsList().Count(), true );
|
||||
Assert( m_mapsBits.GetNumDWords() >= criteria.selected_maps_bits_size() );
|
||||
|
||||
for( int i=0; i < criteria.selected_maps_bits_size() && i < m_mapsBits.GetNumDWords(); ++i )
|
||||
{
|
||||
m_mapsBits.SetDWord( i , criteria.selected_maps_bits( i ) );
|
||||
}
|
||||
|
||||
// validate all of the bits to make sure the maps are in valid categories
|
||||
int nNumBits = m_mapsBits.GetNumBits();
|
||||
for( int i=0; i < nNumBits; ++i )
|
||||
{
|
||||
if ( m_mapsBits.IsBitSet( i ) == false )
|
||||
continue;
|
||||
|
||||
if ( !IsMapInValidCategory( i ) )
|
||||
{
|
||||
const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( i );
|
||||
if ( pMap )
|
||||
{
|
||||
DevMsg( "CCasualCriteriaHelper: Map %s is selected, but not in any valid game modes!\n", pMap->pszMapName );
|
||||
}
|
||||
SetMapSelected( i, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCasualCriteriaHelper::IsMapSelected( const MapDef_t* pMapDef ) const
|
||||
{
|
||||
if ( !pMapDef )
|
||||
return false;
|
||||
|
||||
return IsMapSelected( pMapDef->m_nDefIndex );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Check if bit is selected
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCasualCriteriaHelper::IsMapSelected( const uint32 nMapDefIndex ) const
|
||||
{
|
||||
return m_mapsBits.IsBitSet( nMapDefIndex );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCasualCriteriaHelper::IsMapInValidCategory( uint32 nMapDefIndex ) const
|
||||
{
|
||||
Assert( (int)nMapDefIndex < m_mapsBits.GetNumBits() );
|
||||
|
||||
const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( nMapDefIndex );
|
||||
if ( !pMap || pMap->m_vecAssociatedGameCategories.Count() == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure this map is in at least one category that's in a casual matchmaking group
|
||||
FOR_EACH_VEC( pMap->m_vecAssociatedGameCategories, j )
|
||||
{
|
||||
const SchemaGameCategory_t* pCategory = GetItemSchema()->GetGameCategory( pMap->m_vecAssociatedGameCategories[j] );
|
||||
const SchemaMMGroup_t* pMMGroup = pCategory->m_pMMGroup;
|
||||
|
||||
// Need to have active maps in a match making group
|
||||
if ( !pMMGroup || ( pCategory->m_vecEnabledMaps.Count() == 0 ) || ( !pCategory->PassesRestrictions() ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// and the matchmaking group needs to be Special Events, Core or Alternative (not Comp)
|
||||
if ( pMMGroup->m_eMMGroup == kMatchmakingType_SpecialEvents || pMMGroup->m_eMMGroup == kMatchmakingType_Core || pMMGroup->m_eMMGroup == kMatchmakingType_Alternative )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Check if this criteria is well formed
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCasualCriteriaHelper::IsValid() const
|
||||
{
|
||||
bool bValidMapSeen = false;
|
||||
|
||||
int nNumBits = m_mapsBits.GetNumBits();
|
||||
for( int i=0; i < nNumBits; ++i )
|
||||
{
|
||||
if ( m_mapsBits.IsBitSet( i ) == false )
|
||||
continue;
|
||||
|
||||
if ( IsMapInValidCategory( i ) )
|
||||
{
|
||||
bValidMapSeen = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bValidMapSeen;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Turn helper back into protobuf message
|
||||
//-----------------------------------------------------------------------------
|
||||
CMsgCasualMatchmakingSearchCriteria CCasualCriteriaHelper::GetCasualCriteria() const
|
||||
{
|
||||
CMsgCasualMatchmakingSearchCriteria outCriteria;
|
||||
for( int i=0; i < m_mapsBits.GetNumDWords(); ++i )
|
||||
{
|
||||
outCriteria.add_selected_maps_bits( m_mapsBits.GetDWord( i ) );
|
||||
}
|
||||
|
||||
return outCriteria;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Intersection of this criteria and another
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCasualCriteriaHelper::Intersect( const CMsgCasualMatchmakingSearchCriteria& otherCriteria )
|
||||
{
|
||||
CCasualCriteriaHelper otherHelper( otherCriteria );
|
||||
m_mapsBits.And( otherHelper.m_mapsBits, &m_mapsBits );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Flip a specific map bit
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCasualCriteriaHelper::SetMapSelected( uint32 nMapDefIndex, bool bSelected )
|
||||
{
|
||||
Assert( (int)nMapDefIndex < m_mapsBits.GetNumBits() );
|
||||
|
||||
if ( bSelected && !IsMapInValidCategory( nMapDefIndex ) )
|
||||
{
|
||||
const MapDef_t* pMap = GetItemSchema()->GetMasterMapDefByIndex( nMapDefIndex );
|
||||
if ( pMap )
|
||||
{
|
||||
DevMsg( "CCasualCriteriaHelper: Attempting to set map %s as selected, but not in any valid game modes!\n", pMap->pszMapName );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_mapsBits.Set( (int)nMapDefIndex, bSelected );
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets all bits to zero
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCasualCriteriaHelper::Clear( void )
|
||||
{
|
||||
m_mapsBits.ClearAll();
|
||||
}
|
||||
|
||||
//
|
||||
// MvM Missions
|
||||
//
|
||||
|
||||
CMvMMissionSet::CMvMMissionSet() { Clear(); }
|
||||
CMvMMissionSet::CMvMMissionSet( const CMvMMissionSet &x ) { m_bits = x.m_bits; }
|
||||
CMvMMissionSet::~CMvMMissionSet() {}
|
||||
void CMvMMissionSet::operator=( const CMvMMissionSet &x ) { m_bits = x.m_bits; }
|
||||
void CMvMMissionSet::Clear() { m_bits = 0; }
|
||||
bool CMvMMissionSet::operator==( const CMvMMissionSet &x ) const { return m_bits == x.m_bits; }
|
||||
|
||||
void CMvMMissionSet::SetMissionBySchemaIndex( int idxMission, bool flag )
|
||||
{
|
||||
Assert( idxMission >= 0 && idxMission < GetItemSchema()->GetMvmMissions().Count() );
|
||||
uint64 mask = ( (uint64)1 << (unsigned)idxMission );
|
||||
if ( flag )
|
||||
m_bits |= mask;
|
||||
else
|
||||
m_bits &= ~mask;
|
||||
}
|
||||
|
||||
bool CMvMMissionSet::GetMissionBySchemaIndex( int idxMission ) const
|
||||
{
|
||||
// Bogus index?
|
||||
if ( idxMission == k_iMvmMissionIndex_NotInSchema )
|
||||
return false;
|
||||
if ( idxMission < 0 || idxMission >= GetItemSchema()->GetMvmMissions().Count() )
|
||||
{
|
||||
Assert( idxMission >= 0 );
|
||||
Assert( idxMission < GetItemSchema()->GetMvmMissions().Count() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the bit
|
||||
uint64 mask = ( (uint64)1 << (unsigned)idxMission );
|
||||
return ( m_bits & mask ) != 0;
|
||||
}
|
||||
|
||||
void CMvMMissionSet::Intersect( const CMvMMissionSet &x )
|
||||
{
|
||||
m_bits &= x.m_bits;
|
||||
}
|
||||
|
||||
bool CMvMMissionSet::HasIntersection( const CMvMMissionSet &x ) const
|
||||
{
|
||||
return ( m_bits & x.m_bits ) != 0;
|
||||
}
|
||||
|
||||
bool CMvMMissionSet::IsEmpty() const
|
||||
{
|
||||
return ( m_bits == 0 );
|
||||
}
|
||||
|
||||
const char *GetMatchGroupName( EMatchGroup eMatchGroup )
|
||||
{
|
||||
switch ( eMatchGroup )
|
||||
{
|
||||
case k_nMatchGroup_Invalid: return "(Invalid)";
|
||||
case k_nMatchGroup_MvM_Practice: return "MvM Practice";
|
||||
case k_nMatchGroup_MvM_MannUp: return "MvM MannUp";
|
||||
case k_nMatchGroup_Ladder_6v6: return "6v6 Ladder Match";
|
||||
case k_nMatchGroup_Ladder_9v9: return "9v9 Ladder Match";
|
||||
case k_nMatchGroup_Ladder_12v12: return "12v12 Ladder Match";
|
||||
case k_nMatchGroup_Casual_6v6: return "6v6 Casual Match";
|
||||
case k_nMatchGroup_Casual_9v9: return "9v9 Casual Match";
|
||||
case k_nMatchGroup_Casual_12v12: return "12v12 Casual Match";
|
||||
}
|
||||
|
||||
AssertMsg1( false, "Invalid match group %d", eMatchGroup );
|
||||
return "(Invalid match group)";
|
||||
}
|
||||
|
||||
const char *GetServerPoolName( int iServerPool )
|
||||
{
|
||||
switch ( iServerPool )
|
||||
{
|
||||
case k_nGameServerPool_MvM_Practice_Incomplete_Match: return "MvM Boot Camp Active";
|
||||
case k_nGameServerPool_MvM_MannUp_Incomplete_Match: return "MvM MannUp Active";
|
||||
case k_nGameServerPool_Casual_6v6_Incomplete_Match: return "Casual 6v6 Active";
|
||||
case k_nGameServerPool_Casual_9v9_Incomplete_Match: return "Casual 9v9 Active";
|
||||
case k_nGameServerPool_Casual_12v12_Incomplete_Match: return "Casual 12v12 Active";
|
||||
|
||||
case k_nGameServerPool_MvM_Practice_Full: return "MvM Boot Camp Full";
|
||||
case k_nGameServerPool_MvM_MannUp_Full: return "MvM MannUp Full";
|
||||
case k_nGameServerPool_Casual_6v6_Full: return "Casual 6v6 Full";
|
||||
case k_nGameServerPool_Casual_9v9_Full: return "Casual 9v9 Full";
|
||||
case k_nGameServerPool_Casual_12v12_Full: return "Casual 12v12 Full";
|
||||
}
|
||||
|
||||
AssertMsg1( false, "Invalid server pool %d", iServerPool );
|
||||
return "(Invalid pool index)";
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Matchmaking stuff shared between GC and gameserver / client
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef TF_MATCHMAKING_SHARED_H
|
||||
#define TF_MATCHMAKING_SHARED_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tf_gcmessages.pb.h"
|
||||
|
||||
class IMatchGroupDescription;
|
||||
|
||||
#define NEXT_MAP_VOTE_OPTIONS 3
|
||||
|
||||
// Replace this hard-coded value concept in order to support all bracket types (i.e. anything higher than 6v6, etc)
|
||||
// This increases a number of hard-coded data structures and so on, so beware
|
||||
#define MATCH_SIZE_MAX 24
|
||||
|
||||
// Similarly, some hot path MM structures use this for fixed arrays. It should be the maximum number of players that
|
||||
// will ever be in a party and no larger.
|
||||
#define MAX_PARTY_SIZE 6
|
||||
|
||||
// Range clients are allowed to pass up for custom ping tolerance
|
||||
// Currently matches CS:GO
|
||||
#define CUSTOM_PING_TOLERANCE_MIN 25
|
||||
#define CUSTOM_PING_TOLERANCE_MAX 350
|
||||
|
||||
// XXX(JohnS): Before we can actually use other rating backends for matchmaking or display purposes, there are remaining
|
||||
// hard coded assumptions about where the primary rating is, and issues with e.g. Match_Result assuming the
|
||||
// backend in use *now* is what the match was created for, etc. I've sprinkled this around at all the
|
||||
// landmines I found while implementing the new rating backend, so... comment this out and fix everything
|
||||
// that breaks.
|
||||
static inline void FixmeMMRatingBackendSwapping() {}
|
||||
|
||||
// Backend-agnostic storage type for rating data, so we're not manually passing pairs around to every rating-specific
|
||||
// interface when we inevitably decide to add a third.
|
||||
//
|
||||
// Keep in mind that it is much easier to query well structured data for reports & otherwise, so we should prefer
|
||||
// e.g. adding another value to packing two 16-bit ints in RatingSecondary for a new backend that needs more 16-bit
|
||||
// values. (The calculus may change if we end up with a backend that has eight 8-bit values, however)
|
||||
//
|
||||
// Schema objects that embed rating data: RatingHistory, RatingData
|
||||
// Proto objects that embed rating data: CSOTFRatingData
|
||||
struct MMRatingData_t {
|
||||
uint32_t unRatingPrimary;
|
||||
uint32_t unRatingSecondary;
|
||||
uint32_t unRatingTertiary;
|
||||
|
||||
inline bool operator==(const MMRatingData_t &b) const
|
||||
{ return this->unRatingPrimary == b.unRatingPrimary &&
|
||||
this->unRatingSecondary == b.unRatingSecondary &&
|
||||
this->unRatingTertiary == b.unRatingTertiary; }
|
||||
};
|
||||
|
||||
// Stored value, don't re-order
|
||||
enum EMatchGroup
|
||||
{
|
||||
k_nMatchGroup_Invalid = -1,
|
||||
k_nMatchGroup_First = 0,
|
||||
|
||||
k_nMatchGroup_MvM_Practice = 0,
|
||||
k_nMatchGroup_MvM_MannUp,
|
||||
|
||||
k_nMatchGroup_Ladder_6v6,
|
||||
k_nMatchGroup_Ladder_9v9,
|
||||
k_nMatchGroup_Ladder_12v12,
|
||||
|
||||
k_nMatchGroup_Casual_6v6,
|
||||
k_nMatchGroup_Casual_9v9,
|
||||
k_nMatchGroup_Casual_12v12,
|
||||
|
||||
k_nMatchGroup_Count,
|
||||
// When adding a new matchgroup, add case handling to GetMatchSizeForMatchGroup(), GetMatchGroupName(), GetServerPoolName(), GetMaxLobbySizeForMatchGroup(), YldWebAPIServersByDataCenter()
|
||||
};
|
||||
|
||||
// Stored value, don't re-order
|
||||
//
|
||||
// If you add a new backend, see ITFMMRatingBackend::GetRatingBackend -- you need to at least provide a GetDefault()
|
||||
enum EMMRating
|
||||
{
|
||||
k_nMMRating_LowestValue = -1,
|
||||
k_nMMRating_Invalid = -1,
|
||||
|
||||
k_nMMRating_First = 0,
|
||||
k_nMMRating_6v6_DRILLO = 0,
|
||||
k_nMMRating_6v6_DRILLO_PlayerAcknowledged = 1,
|
||||
k_nMMRating_6v6_GLICKO = 2,
|
||||
k_nMMRating_12v12_DRILLO = 3,
|
||||
k_nMMRating_12v12_GLICKO = 4,
|
||||
k_nMMRating_Last = 4,
|
||||
};
|
||||
|
||||
// This must be in the range of an int16 for database serialization
|
||||
COMPILE_TIME_ASSERT( k_nMMRating_LowestValue >= INT16_MIN );
|
||||
COMPILE_TIME_ASSERT( k_nMMRating_Last <= INT16_MAX );
|
||||
|
||||
// Stored value, don't re-order
|
||||
enum EMMRatingSource
|
||||
{
|
||||
k_nMMRatingSource_LowestValue = -1,
|
||||
k_nMMRatingSource_Invalid = -1,
|
||||
|
||||
k_nMMRatingSource_Match = 0, // Match result. Source ID is match ID
|
||||
k_nMMRatingSource_Admin = 1, // Admin command/manual adjustment. Source ID is probably 0 or something.
|
||||
k_nMMRatingSource_PlayerAcknowledge = 2, // For 'acknowledge' type ratings, the player acknowledged the value
|
||||
k_nMMRatingSource_ImportedOldSystem = 3, // For pre-history ratings, this source is used once for 'initial rating from old system'
|
||||
k_nMMRatingSource_Last = 3,
|
||||
};
|
||||
|
||||
// This must be in the range of an int16 for database serialization
|
||||
COMPILE_TIME_ASSERT( k_nMMRatingSource_LowestValue >= INT16_MIN );
|
||||
COMPILE_TIME_ASSERT( k_nMMRatingSource_Last <= INT16_MAX );
|
||||
|
||||
// Also update this guy if you do the thing
|
||||
const char *GetMatchGroupName( EMatchGroup eMatchGroup );
|
||||
|
||||
// Probably a better place for this...
|
||||
enum ELadderLeaderboardTypes
|
||||
{
|
||||
LADDER_LEADERBOARDS_6V6 = 0,
|
||||
LADDER_LEADERBOARDS_PUBLIC,
|
||||
LADDER_LEADERBOARDS_9V9,
|
||||
LADDER_LEADERBOARDS_12V12,
|
||||
LADDER_LEADERBOARDS_MAX
|
||||
};
|
||||
|
||||
// Late join modes
|
||||
enum EMatchMode
|
||||
{
|
||||
// Uninitialized/unknown
|
||||
eMatchMode_Invalid,
|
||||
// Not late join / don't use late join
|
||||
eMatchMode_MatchMaker_CompleteFromQueue,
|
||||
// The add-one-player-at-a-time mode that doesn't work with the new scoring system, but still used for MvM and other
|
||||
// old-scoring-system stuff.
|
||||
eMatchMode_MatchMaker_LateJoinDropIn,
|
||||
// The new late join mode that re-evaulates complete matches with the missing spot(s) filled.
|
||||
eMatchMode_MatchMaker_LateJoinMatchBased,
|
||||
// A match that is being manually crafted
|
||||
eMatchMode_Manual,
|
||||
};
|
||||
|
||||
const EMatchGroup k_nMatchGroup_Ladder_First = k_nMatchGroup_Ladder_6v6;
|
||||
const EMatchGroup k_nMatchGroup_Ladder_Last = k_nMatchGroup_Ladder_12v12;
|
||||
|
||||
const EMatchGroup k_nMatchGroup_Casual_First = k_nMatchGroup_Casual_6v6;
|
||||
const EMatchGroup k_nMatchGroup_Casual_Last = k_nMatchGroup_Casual_12v12;
|
||||
|
||||
inline bool IsMvMMatchGroup( EMatchGroup eMatchGroup )
|
||||
{
|
||||
return ( eMatchGroup == k_nMatchGroup_MvM_Practice ) || ( eMatchGroup == k_nMatchGroup_MvM_MannUp );
|
||||
}
|
||||
|
||||
inline bool IsLadderGroup( EMatchGroup eMatchGroup )
|
||||
{
|
||||
return ( eMatchGroup >= k_nMatchGroup_Ladder_First && eMatchGroup <= k_nMatchGroup_Ladder_Last )
|
||||
|| ( eMatchGroup >= k_nMatchGroup_Casual_First && eMatchGroup <= k_nMatchGroup_Casual_Last );
|
||||
}
|
||||
|
||||
inline bool IsCasualGroup( EMatchGroup eMatchGroup )
|
||||
{
|
||||
return ( eMatchGroup >= k_nMatchGroup_Casual_First ) && ( eMatchGroup <= k_nMatchGroup_Casual_Last );
|
||||
}
|
||||
|
||||
inline bool IsMannUpGroup( EMatchGroup eMatchGroup )
|
||||
{
|
||||
switch ( eMatchGroup )
|
||||
{
|
||||
case k_nMatchGroup_MvM_Practice:
|
||||
return false;
|
||||
case k_nMatchGroup_MvM_MannUp:
|
||||
return true;
|
||||
case k_nMatchGroup_Ladder_6v6:
|
||||
case k_nMatchGroup_Ladder_9v9:
|
||||
case k_nMatchGroup_Ladder_12v12:
|
||||
return false;
|
||||
case k_nMatchGroup_Invalid:
|
||||
default:
|
||||
Assert( !"IsMannUpGroup called with invalid match group" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum EMMServerMode
|
||||
{
|
||||
eMMServerMode_Idle,
|
||||
eMMServerMode_Incomplete_Match,
|
||||
eMMServerMode_Full,
|
||||
eMMServerMode_Count
|
||||
};
|
||||
|
||||
// Separate penalty pools (and rules) for different classes of modes
|
||||
enum EMMPenaltyPool
|
||||
{
|
||||
eMMPenaltyPool_Invalid,
|
||||
eMMPenaltyPool_Casual, // Pool with lenient penalties for most casual/mainstream gamemodes
|
||||
eMMPenaltyPool_Ranked // Pool with strict and cumulative penalties for ranked gamemodes where abandons tank matches
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// !! This should match up with GetServerPoolIndex to map these between match groups and server pools
|
||||
// eMMServerMode_NotParticipating
|
||||
k_nGameServerPool_NotParticipating = -1,
|
||||
|
||||
// eMMServerMode_Incomplete_Match
|
||||
k_nGameServerPool_MvM_Practice_Incomplete_Match = 0,
|
||||
k_nGameServerPool_MvM_MannUp_Incomplete_Match,
|
||||
k_nGameServerPool_Ladder_6v6_Incomplete_Match,
|
||||
k_nGameServerPool_Ladder_9v9_Incomplete_Match,
|
||||
k_nGameServerPool_Ladder_12v12_Incomplete_Match,
|
||||
k_nGameServerPool_Casual_6v6_Incomplete_Match,
|
||||
k_nGameServerPool_Casual_9v9_Incomplete_Match,
|
||||
k_nGameServerPool_Casual_12v12_Incomplete_Match,
|
||||
|
||||
// eMMServerMode_Full
|
||||
k_nGameServerPool_MvM_Practice_Full,
|
||||
k_nGameServerPool_MvM_MannUp_Full,
|
||||
k_nGameServerPool_Ladder_6v6_Full,
|
||||
k_nGameServerPool_Ladder_9v9_Full,
|
||||
k_nGameServerPool_Ladder_12v12_Full,
|
||||
k_nGameServerPool_Casual_6v6_Full,
|
||||
k_nGameServerPool_Casual_9v9_Full,
|
||||
k_nGameServerPool_Casual_12v12_Full,
|
||||
|
||||
// eMMServerMode_Idle
|
||||
k_nGameServerPool_Idle,
|
||||
// When adding a new matchgroup, add case handling to GetMatchSizeForMatchGroup(), GetMatchGroupName(), GetServerPoolName(), GetMaxLobbySizeForMatchGroup(), YldWebAPIServersByDataCenter()
|
||||
|
||||
k_nGameServerPoolCountTotal,
|
||||
};
|
||||
|
||||
// Also update this guy if you touch pools
|
||||
const char *GetServerPoolName( int iServerPool );
|
||||
|
||||
const int k_nGameServerPool_Incomplete_Match_First = k_nGameServerPool_MvM_Practice_Incomplete_Match;
|
||||
const int k_nGameServerPool_Incomplete_Match_Last = k_nGameServerPool_Casual_12v12_Incomplete_Match;
|
||||
const int k_nGameServerPool_Full_First = k_nGameServerPool_MvM_Practice_Full;
|
||||
const int k_nGameServerPool_Full_Last = k_nGameServerPool_Casual_12v12_Full;
|
||||
|
||||
COMPILE_TIME_ASSERT( k_nGameServerPool_Incomplete_Match_First + k_nMatchGroup_Count - 1 == k_nGameServerPool_Incomplete_Match_Last );
|
||||
COMPILE_TIME_ASSERT( k_nGameServerPool_Full_First + k_nMatchGroup_Count - 1 == k_nGameServerPool_Full_Last );
|
||||
|
||||
inline bool IsIncompleteMatchPool( int nGameServerPool )
|
||||
{
|
||||
return nGameServerPool >= k_nGameServerPool_Incomplete_Match_First && nGameServerPool <= k_nGameServerPool_Incomplete_Match_Last;
|
||||
}
|
||||
|
||||
// Stuff is simpler if we can set a max number of challenges in the schema
|
||||
#define MAX_MVM_CHALLENGES 64
|
||||
|
||||
// Store a set of MvM challenges (search criteria, etc)
|
||||
class CMvMMissionSet
|
||||
{
|
||||
public:
|
||||
CMvMMissionSet();
|
||||
CMvMMissionSet( const CMvMMissionSet &x );
|
||||
~CMvMMissionSet();
|
||||
void operator=( const CMvMMissionSet &x );
|
||||
bool operator==( const CMvMMissionSet &x ) const;
|
||||
|
||||
/// Set to the empty set
|
||||
void Clear();
|
||||
|
||||
/// get/set individual bits, based on index of the challenge in the schema
|
||||
void SetMissionBySchemaIndex( int iChallengeSchemaIndex, bool flag );
|
||||
bool GetMissionBySchemaIndex( int iChallengeSchemaIndex ) const;
|
||||
|
||||
/// Intersect this set with the other set. Use IsEmpty()
|
||||
/// to see if this produced the empty set.
|
||||
void Intersect( const CMvMMissionSet &x );
|
||||
|
||||
/// Return true if the two sets have a nonzero intersection. (Neither object is modified)
|
||||
bool HasIntersection( const CMvMMissionSet &x ) const;
|
||||
|
||||
/// Return true if any challenges are selected
|
||||
bool IsEmpty() const;
|
||||
private:
|
||||
|
||||
COMPILE_TIME_ASSERT( MAX_MVM_CHALLENGES <= 64 );
|
||||
|
||||
// Just use a plain old uint64 for now. We can make this into a proper bitfield class at some point
|
||||
uint64 m_bits;
|
||||
};
|
||||
|
||||
// Player Skill Ratings
|
||||
const int k_nDrilloRating_MinRatingAdjust = 1;
|
||||
const int k_nDrilloRating_MaxRatingAdjust = 100;
|
||||
const int k_nDrilloRating_Ladder_MaxRatingAdjust = 500;
|
||||
const int k_nDrilloRating_Ladder_MaxLossAdjust_LowRank = 100;
|
||||
const uint32 k_unDrilloRating_MaxDifference = 25000;
|
||||
const uint32 k_unDrilloRating_Min = 1;
|
||||
const uint32 k_unDrilloRating_Ladder_Min = 10000;
|
||||
const uint32 k_unDrilloRating_Max = 50000;
|
||||
const uint32 k_unDrilloRating_Avg = 20000;
|
||||
const uint32 k_unDrilloRating_Ladder_Start = 10000;
|
||||
const uint32 k_unDrilloRating_Ladder_LowSkill = 19500; // First 6 ranks ceiling
|
||||
const uint32 k_unDrilloRating_Ladder_HighSkill = 33001; // Last 6 ranks floor
|
||||
|
||||
struct MapDef_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Wrapper class to make dealing with CMsgCasualMatchmakingSearchCriteria
|
||||
// much easier.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCasualCriteriaHelper
|
||||
{
|
||||
public:
|
||||
CCasualCriteriaHelper( const CMsgCasualMatchmakingSearchCriteria& criteria );
|
||||
|
||||
bool IsMapSelected( const MapDef_t* pMapDef ) const;
|
||||
bool IsMapSelected( const uint32 nMapDefIndex ) const;
|
||||
bool IsValid() const;
|
||||
bool AnySelected() const { return !m_mapsBits.IsAllClear(); }
|
||||
CMsgCasualMatchmakingSearchCriteria GetCasualCriteria() const;
|
||||
|
||||
void Intersect( const CMsgCasualMatchmakingSearchCriteria& otherCriteria );
|
||||
bool SetMapSelected( uint32 nMapDefIndex, bool bSelected );
|
||||
|
||||
void Clear( void );
|
||||
|
||||
private:
|
||||
bool IsMapInValidCategory( uint32 nMapDefIndex ) const;
|
||||
|
||||
private:
|
||||
CLargeVarBitVec m_mapsBits;
|
||||
};
|
||||
|
||||
// CSOTFLobby flags
|
||||
#define LOBBY_FLAG_LOWPRIORITY ( 1 << 0 )
|
||||
#define LOBBY_FLAG_REMATCH ( 1 << 1 )
|
||||
|
||||
// CMsgGC_Match_Result match flags
|
||||
#define MATCH_FLAG_LOWPRIORITY ( 1 << 0 )
|
||||
#define MATCH_FLAG_REMATCH ( 1 << 1 )
|
||||
|
||||
// CMsgGC_Match_Result player flags
|
||||
#define MATCH_FLAG_PLAYER_LEAVER ( 1 << 0 )
|
||||
#define MATCH_FLAG_PLAYER_LATEJOIN ( 1 << 1 )
|
||||
// Separate from LEAVER - was marked as an abandon and issued a penalty. You can be a leaver without being an
|
||||
// abandoner.
|
||||
#define MATCH_FLAG_PLAYER_ABANDONER ( 1 << 2 )
|
||||
#define MATCH_FLAG_PLAYER_PLAYED ( 1 << 3 ) // Did they stay long enough for the game to start?
|
||||
|
||||
#endif // #ifndef TF_MATCHMAKING_SHARED_H
|
||||
@@ -0,0 +1,176 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_notification.h"
|
||||
#include "gcsdk/enumutils.h"
|
||||
#include "schemainitutils.h"
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#ifdef GC
|
||||
IMPLEMENT_CLASS_MEMPOOL( CTFNotification, 1000, UTLMEMORYPOOL_GROW_SLOW );
|
||||
#endif
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
using namespace GCSDK;
|
||||
|
||||
#ifdef GC
|
||||
// See LOCALIZED NOTIFICATIONS note in header
|
||||
static bool BIsLocalizedNotificationType( CMsgGCNotification_NotificationType eType )
|
||||
{
|
||||
switch ( eType )
|
||||
{
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_SUPPORT_MESSAGE:
|
||||
return true;
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_REPORTED_PLAYER_BANNED:
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_CUSTOM_STRING:
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_MM_BAN_DUE_TO_EXCESSIVE_REPORTS:
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_REPORTED_PLAYER_WAS_BANNED:
|
||||
case CMsgGCNotification_NotificationType_NOTIFICATION_NUM_TYPES:
|
||||
return false;
|
||||
default:
|
||||
Assert( !"Unknown notification type" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CTFNotification::CTFNotification()
|
||||
: m_eLocalizedToLanguage( k_Lang_None )
|
||||
{
|
||||
Obj().set_notification_id( 0 );
|
||||
Obj().set_account_id( 0 );
|
||||
Obj().set_expiration_time( 0 );
|
||||
Obj().set_type( CMsgGCNotification_NotificationType_NOTIFICATION_NUM_TYPES ); // Invalid
|
||||
Obj().set_notification_string( "" );
|
||||
}
|
||||
|
||||
CTFNotification::CTFNotification( CMsgGCNotification msg, const char *pUnlocalizedString, ELanguage eLang )
|
||||
: m_eLocalizedToLanguage( k_Lang_None )
|
||||
{
|
||||
Obj() = msg;
|
||||
|
||||
if ( BIsLocalizedNotificationType( Obj().type() ) )
|
||||
{
|
||||
m_strUnlocalizedString = pUnlocalizedString;
|
||||
BLocalize( eLang );
|
||||
}
|
||||
else
|
||||
{
|
||||
Obj().set_notification_string( pUnlocalizedString );
|
||||
}
|
||||
}
|
||||
|
||||
bool CTFNotification::BLocalize( ELanguage eLang )
|
||||
{
|
||||
if ( !BIsLocalizedNotificationType( Obj().type() ) || eLang == m_eLocalizedToLanguage )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_eLocalizedToLanguage = eLang;
|
||||
const char *pLocalized = GGCGameBase()->LocalizeToken( m_strUnlocalizedString.Get(), eLang, false );
|
||||
|
||||
// Try english fallback
|
||||
const ELanguage eFallback = k_Lang_English;
|
||||
if ( !pLocalized && eLang != eFallback )
|
||||
{
|
||||
pLocalized = GGCGameBase()->LocalizeToken( m_strUnlocalizedString.Get(), eFallback, false );
|
||||
if ( pLocalized )
|
||||
{
|
||||
EmitError( SPEW_GC,
|
||||
"Notification has localized token \"%s\" that is not available in language %s, falling back to %s.\n",
|
||||
m_strUnlocalizedString.Get(), GetLanguageShortName( eLang ), GetLanguageShortName( eFallback ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !pLocalized )
|
||||
{
|
||||
EmitError( SPEW_GC, "Notification has localized token \"%s\" that was not found in primary language %s *or* fallback to %s.\n",
|
||||
m_strUnlocalizedString.Get(), GetLanguageShortName( eLang ), GetLanguageShortName( eFallback ) );
|
||||
pLocalized = m_strUnlocalizedString.Get();
|
||||
}
|
||||
|
||||
Obj().set_notification_string( pLocalized ? pLocalized : m_strUnlocalizedString.Get() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTFNotification::BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchNotification schNotification;
|
||||
WriteToRecord( &schNotification );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddInsertToTransaction( sqlAccess, &schNotification );
|
||||
}
|
||||
|
||||
bool CTFNotification::BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields )
|
||||
{
|
||||
CSchNotification schNotification;
|
||||
WriteToRecord( &schNotification );
|
||||
CColumnSet csDatabaseDirty( schNotification.GetPSchema()->GetRecordInfo() );
|
||||
csDatabaseDirty.MakeEmpty();
|
||||
FOR_EACH_VEC( fields, nField )
|
||||
{
|
||||
switch ( fields[nField] )
|
||||
{
|
||||
case CMsgGCNotification::kNotificationIdFieldNumber : csDatabaseDirty.BAddColumn( CSchNotification::k_iField_ulNotificationID ); break;
|
||||
case CMsgGCNotification::kAccountIdFieldNumber : csDatabaseDirty.BAddColumn( CSchNotification::k_iField_unAccountID ); break;
|
||||
case CMsgGCNotification::kExpirationTimeFieldNumber : csDatabaseDirty.BAddColumn( CSchNotification::k_iField_RTime32Expiration ); break;
|
||||
case CMsgGCNotification::kTypeFieldNumber : csDatabaseDirty.BAddColumn( CSchNotification::k_iField_unNotificationType ); break;
|
||||
case CMsgGCNotification::kNotificationStringFieldNumber : csDatabaseDirty.BAddColumn( CSchNotification::k_iField_VarCharData ); break;
|
||||
default:
|
||||
Assert( false );
|
||||
}
|
||||
}
|
||||
return CSchemaSharedObjectHelper::BYieldingAddWriteToTransaction( sqlAccess, &schNotification, csDatabaseDirty );
|
||||
}
|
||||
|
||||
bool CTFNotification::BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess )
|
||||
{
|
||||
CSchNotification schNotification;
|
||||
WriteToRecord( &schNotification );
|
||||
return CSchemaSharedObjectHelper::BYieldingAddRemoveToTransaction( sqlAccess, &schNotification );
|
||||
}
|
||||
|
||||
void CTFNotification::WriteToRecord( CSchNotification *pNotification ) const
|
||||
{
|
||||
// Important: For localized notifications, the DB value is the localization key (#TF_Some_Thing), but the in-memory
|
||||
// shared object value is localized. See LOCALIZED NOTIFICATIONS note in header.
|
||||
pNotification->m_ulNotificationID = Obj().notification_id();
|
||||
pNotification->m_unAccountID = Obj().account_id();
|
||||
pNotification->m_RTime32Expiration = Obj().expiration_time();
|
||||
pNotification->m_unNotificationType = Obj().type();
|
||||
if ( BIsLocalizedNotificationType( Obj().type() ) )
|
||||
{
|
||||
WRITE_VAR_CHAR_FIELD_TRUNC( *pNotification, VarCharData, m_strUnlocalizedString.Get() );
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITE_VAR_CHAR_FIELD_TRUNC( *pNotification, VarCharData, Obj().notification_string().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void CTFNotification::ReadFromRecord( const CSchNotification & schNotification )
|
||||
{
|
||||
// Important: For localized notifications, the DB value is the localization key (#TF_Some_Thing), but the in-memory
|
||||
// shared object value is localized. See LOCALIZED NOTIFICATIONS note in header.
|
||||
m_strUnlocalizedString.Clear();
|
||||
m_eLocalizedToLanguage = k_Lang_None;
|
||||
|
||||
Obj().set_notification_id( schNotification.m_ulNotificationID );
|
||||
Obj().set_account_id( schNotification.m_unAccountID );
|
||||
Obj().set_expiration_time( schNotification.m_RTime32Expiration );
|
||||
Obj().set_type( ( CMsgGCNotification::NotificationType ) schNotification.m_unNotificationType );
|
||||
const char *pchNotificationString = READ_VAR_CHAR_FIELD( schNotification, m_VarCharData );
|
||||
Obj().set_notification_string( pchNotificationString );
|
||||
if ( BIsLocalizedNotificationType( Obj().type() ) )
|
||||
{
|
||||
m_strUnlocalizedString = pchNotificationString;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Enables sending of notifications (custom messages of various kinds) to the client
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TF_NOTIFICATION_H
|
||||
#define TF_NOTIFICATION_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/protobufsharedobject.h"
|
||||
#include "tf_gcmessages.h"
|
||||
|
||||
#ifdef GC
|
||||
#include "tf_gc.h"
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Purpose: Send a notification to the client
|
||||
//---------------------------------------------------------------------------------
|
||||
|
||||
// LOCALIZED NOTIFICATIONS
|
||||
//
|
||||
// Some types of notification are on-the-fly localized. These notifications are stored in the database as unlocalized
|
||||
// strings (#TF_Foo), but on-the-fly localized when loaded as a shared-object. Clients only see the final localized
|
||||
// version. BLocalizeAndMaybeDirty() will update the localization for a newer language.
|
||||
|
||||
class CTFNotification : public GCSDK::CProtoBufSharedObject< CMsgGCNotification, k_EEconTypeNotification >
|
||||
{
|
||||
public:
|
||||
// If using this form, ensure you call BLocalize after filling fields for localized types.
|
||||
|
||||
#ifdef GC
|
||||
CTFNotification();
|
||||
CTFNotification( CMsgGCNotification msg, const char *pUnlocalizedString, ELanguage eLang );
|
||||
DECLARE_CLASS_MEMPOOL( CTFNotification );
|
||||
|
||||
// For on-the-fly localized notification types, update the localization to this language now. If true, object
|
||||
// changed. See LOCALIZED NOTIFICATIONS above.
|
||||
//
|
||||
// !! Caller is responsible for dirtying and sending network updates if this results in a change.
|
||||
bool BLocalize( ELanguage eLang );
|
||||
|
||||
virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
|
||||
virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
|
||||
virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess );
|
||||
|
||||
void WriteToRecord( CSchNotification *pNotification ) const;
|
||||
void ReadFromRecord( const CSchNotification & pNotification );
|
||||
|
||||
private:
|
||||
// For notifications that are on-the-fly localized, this holds the localization token (which is stored in the DB),
|
||||
// whereas the in-memory object is a localized representation.
|
||||
CUtlString m_strUnlocalizedString;
|
||||
ELanguage m_eLocalizedToLanguage;
|
||||
#endif // GC
|
||||
};
|
||||
|
||||
#endif // TF_NOTIFICATION_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user