This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+97
View File
@@ -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
+187
View File
@@ -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
+723
View File
@@ -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 );
}
+43
View File
@@ -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
+188
View File
@@ -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
+69
View File
@@ -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
+453
View File
@@ -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
+760
View File
@@ -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
+231
View File
@@ -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
+54
View File
@@ -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
+77
View File
@@ -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." );
+86
View File
@@ -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
+249
View File
@@ -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 );
}
}
+119
View File
@@ -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
+251
View File
@@ -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 )
+156
View File
@@ -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
+550
View File
@@ -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
+566
View File
@@ -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
+172
View File
@@ -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
+281
View File
@@ -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();
}
}
+103
View File
@@ -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
+20
View File
@@ -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"
+36
View File
@@ -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
+397
View File
@@ -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()
{
}
+111
View File
@@ -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
+649
View File
@@ -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 &params )
{
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
+102
View File
@@ -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 &params ) 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
+343
View File
@@ -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
+107
View File
@@ -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
+90
View File
@@ -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
+64
View File
@@ -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
+368
View File
@@ -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();
}
+31
View File
@@ -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
+637
View File
@@ -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
+706
View File
@@ -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
+162
View File
@@ -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
+34
View File
@@ -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;
}
+162
View File
@@ -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
+30
View File
@@ -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"
}
}
}
}
}
+251
View File
@@ -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
+69
View File
@@ -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
+110
View File
@@ -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
+48
View File
@@ -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
+204
View File
@@ -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
+304
View File
@@ -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
+786
View File
@@ -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
+131
View File
@@ -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
+946
View File
@@ -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
+41
View File
@@ -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;
}
+40
View File
@@ -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
+34
View File
@@ -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
};
+789
View File
@@ -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
+113
View File
@@ -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
+519
View File
@@ -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
+81
View File
@@ -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
+73
View File
@@ -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
+55
View File
@@ -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
+286
View File
@@ -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
+319
View File
@@ -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
+425
View File
@@ -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
+849
View File
@@ -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();
}
+127
View File
@@ -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
+258
View File
@@ -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
+205
View File
@@ -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;
}
+330
View File
@@ -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)";
}
+351
View File
@@ -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
+176
View File
@@ -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
+62
View File
@@ -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