//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

#include "cbase.h"
#include "tf_shareddefs.h"
#include "KeyValues.h"
#include "takedamageinfo.h"
#include "tf_gamerules.h"
#include "filesystem.h"
#include "tf_matchmaking_shared.h"

//-----------------------------------------------------------------------------
// Teams.
//-----------------------------------------------------------------------------
const char *g_aTeamNames[TF_TEAM_COUNT] =
{
	"Unassigned",
	"Spectator",
	"Red",
	"Blue"
};

color32 g_aTeamColors[TF_TEAM_COUNT] = 
{
	{ 0, 0, 0, 0 },
	{ 0, 0, 0, 0 },
	{ 255, 0, 0, 0 },
	{ 0, 0, 255, 0 }
};

//-----------------------------------------------------------------------------
// Classes.
//-----------------------------------------------------------------------------

const char *g_aPlayerClassNames[TF_CLASS_MENU_BUTTONS] =
{
	"#TF_Class_Name_Undefined",
	"#TF_Class_Name_Scout",
	"#TF_Class_Name_Sniper",
	"#TF_Class_Name_Soldier",
	"#TF_Class_Name_Demoman",
	"#TF_Class_Name_Medic",
	"#TF_Class_Name_HWGuy",
	"#TF_Class_Name_Pyro",
	"#TF_Class_Name_Spy",
	"#TF_Class_Name_Engineer",
	"#TF_Class_Name_Civilian",
	"",
	"#TF_Random"
};

const char *g_aPlayerClassNames_NonLocalized[TF_CLASS_MENU_BUTTONS] =
{
	"Undefined",
	"Scout",
	"Sniper",
	"Soldier",
	"Demoman",
	"Medic",
	"Heavy",
	"Pyro",
	"Spy",
	"Engineer",
	"Civilian",
	"",
	"Random"
};

const char *g_aRawPlayerClassNamesShort[TF_CLASS_MENU_BUTTONS] =
{
	"undefined",
	"scout",
	"sniper",
	"soldier",
	"demo",	// short
	"medic",
	"heavy",// short
	"pyro",
	"spy",
	"engineer",
	"civilian",
	"",
	"random"
};

const char *g_aRawPlayerClassNames[TF_CLASS_MENU_BUTTONS] =
{
	"undefined",
	"scout",
	"sniper",
	"soldier",
	"demoman",
	"medic",
	"heavyweapons",
	"pyro",
	"spy",
	"engineer",
	"civilian",
	"",
	"random"
};

const char g_szBotModels[][ MAX_PATH ] = 
{
	"", //TF_CLASS_UNDEFINED

	"models/bots/scout/bot_scout.mdl",
	"models/bots/sniper/bot_sniper.mdl",
	"models/bots/soldier/bot_soldier.mdl",
	"models/bots/demo/bot_demo.mdl",
	"models/bots/medic/bot_medic.mdl",
	"models/bots/heavy/bot_heavy.mdl",
	"models/bots/pyro/bot_pyro.mdl",
	"models/bots/spy/bot_spy.mdl",
	"models/bots/engineer/bot_engineer.mdl",
};

const char g_szPlayerRobotModels[][MAX_PATH] =
{
	"", //TF_CLASS_UNDEFINED

	"models/bots/scout/bot_scout_human_anim.mdl",
	"models/bots/sniper/bot_sniper_human_anim.mdl",
	"models/bots/soldier/bot_soldier_human_anim.mdl",
	"models/bots/demo/bot_demo_human_anim.mdl",
	"models/bots/medic/bot_medic_human_anims.mdl",
	"models/bots/heavy/bot_heavy_human_anims.mdl",
	"models/bots/pyro/bot_pyro_human_anim.mdl",
	"models/bots/spy/bot_spy_human_anims.mdl",
	"models/bots/engineer/bot_engineer_human_anim.mdl",
};

const char g_szBotBossModels[][ MAX_PATH ] = 
{
	"", //TF_CLASS_UNDEFINED

	"models/bots/scout_boss/bot_scout_boss.mdl",
	"models/bots/sniper/bot_sniper.mdl",
	"models/bots/soldier_boss/bot_soldier_boss.mdl",
	"models/bots/demo_boss/bot_demo_boss.mdl",
	"models/bots/medic/bot_medic.mdl",
	"models/bots/heavy_boss/bot_heavy_boss.mdl",
	"models/bots/pyro_boss/bot_pyro_boss.mdl",
	"models/bots/spy/bot_spy.mdl",
	"models/bots/engineer/bot_engineer.mdl",
};

const char g_szBotBossSentryBusterModel[ MAX_PATH ] = "models/bots/demo/bot_sentry_buster.mdl";

// Rome 2 promo models
const char g_szRomePromoItems_Hat[][ MAX_PATH ] = 
{
	"", //TF_CLASS_UNDEFINED

	"tw_scoutbot_hat",
	"tw_sniperbot_helmet",
	"tw_soldierbot_helmet",
	"tw_demobot_helmet",
	"tw_medibot_hat",
	"tw_heavybot_helmet",
	"tw_pyrobot_helmet",
	"tw_spybot_hood",
	"tw_engineerbot_helmet",
};

const char g_szRomePromoItems_Misc[][ MAX_PATH ] = 
{
	"", //TF_CLASS_UNDEFINED

	"tw_scoutbot_armor",
	"tw_sniperbot_armor",
	"tw_soldierbot_armor",
	"tw_demobot_armor",
	"tw_medibot_chariot",
	"tw_heavybot_armor",
	"tw_pyrobot_armor",
	"tw_spybot_armor",
	"tw_engineerbot_armor",
};

const char *g_pszBreadModels[] = 
{
	"models/weapons/c_models/c_bread/c_bread_baguette.mdl",		// Spy
	"models/weapons/c_models/c_bread/c_bread_burnt.mdl",		// Pyro
	"models/weapons/c_models/c_bread/c_bread_cinnamon.mdl",		// Demo?
	"models/weapons/c_models/c_bread/c_bread_cornbread.mdl",	// Engineer
	"models/weapons/c_models/c_bread/c_bread_crumpet.mdl",		// Sniper?
	"models/weapons/c_models/c_bread/c_bread_plainloaf.mdl",	// Scout
	"models/weapons/c_models/c_bread/c_bread_pretzel.mdl",		// Medic
	"models/weapons/c_models/c_bread/c_bread_ration.mdl",		// Soldier
	"models/weapons/c_models/c_bread/c_bread_russianblack.mdl",	// Heavy?
};

int GetClassIndexFromString( const char *pClassName, int nLastClassIndex/*=TF_LAST_NORMAL_CLASS*/ )
{
	for ( int i = TF_FIRST_NORMAL_CLASS; i <= nLastClassIndex; ++i )
	{
		// compare first N characters to allow matching both "heavy" and "heavyweapons"
		int classnameLength = V_strlen( g_aPlayerClassNames_NonLocalized[i] );

		if ( V_strlen( pClassName ) < classnameLength )
			continue;

		if ( !V_strnicmp( g_aPlayerClassNames_NonLocalized[i], pClassName, classnameLength ) )
		{
			return i;
		}
	}

	return TF_CLASS_UNDEFINED;
}

int iRemapIndexToClass[TF_CLASS_MENU_BUTTONS] =
{
	0,
		TF_CLASS_SCOUT,
		TF_CLASS_SOLDIER,
		TF_CLASS_PYRO,
		TF_CLASS_DEMOMAN,
		TF_CLASS_HEAVYWEAPONS,
		TF_CLASS_ENGINEER,
		TF_CLASS_MEDIC,
		TF_CLASS_SNIPER,
		TF_CLASS_SPY,
		0,
		0,
		TF_CLASS_RANDOM
};

int GetRemappedMenuIndexForClass( int iClass )
{
	int iIndex = 0;

	for ( int i = 0 ; i < TF_CLASS_MENU_BUTTONS ; i++ )
	{
		if ( iRemapIndexToClass[i] == iClass )
		{
			iIndex = i;
			break;
		}
	}

	return iIndex;
}

ETFCond condition_to_attribute_translation[]  =
{
	TF_COND_BURNING,					// 1 (1<<0)
	TF_COND_AIMING,						// 2 (1<<1)
	TF_COND_ZOOMED,						// 4 (1<<2)
	TF_COND_DISGUISING,					// 8 (...)
	TF_COND_DISGUISED,					// 16
	TF_COND_STEALTHED,					// 32
	TF_COND_INVULNERABLE,				// 64
	TF_COND_TELEPORTED,					// 128
	TF_COND_TAUNTING,					// 256
	TF_COND_INVULNERABLE_WEARINGOFF,	// 512
	TF_COND_STEALTHED_BLINK,			// 1024
	TF_COND_SELECTED_TO_TELEPORT,		// 2048
	TF_COND_CRITBOOSTED,				// 4096
	TF_COND_TMPDAMAGEBONUS,				// 8192
	TF_COND_FEIGN_DEATH,				// 16384
	TF_COND_PHASE,						// 32768
	TF_COND_STUNNED,					// 65536
	TF_COND_HEALTH_BUFF,				// 131072
	TF_COND_HEALTH_OVERHEALED,			// 262144
	TF_COND_URINE,						// 524288
	TF_COND_ENERGY_BUFF,				// 1048576

	TF_COND_LAST				// sentinel value checked against when iterating
};

ETFCond g_aDebuffConditions[] =
{
	TF_COND_BURNING,
	TF_COND_URINE,
	TF_COND_BLEEDING,
	TF_COND_MAD_MILK,
	TF_COND_LAST
};

bool ConditionExpiresFast( ETFCond eCond )
{
	return eCond == TF_COND_BURNING
		|| eCond == TF_COND_URINE
		|| eCond == TF_COND_BLEEDING
		|| eCond == TF_COND_MAD_MILK;
}

static const char *g_aConditionNames[] =
{
	"TF_COND_AIMING",		// Sniper aiming, Heavy minigun.
	"TF_COND_ZOOMED",
	"TF_COND_DISGUISING",
	"TF_COND_DISGUISED",
	"TF_COND_STEALTHED",		// Spy specific
	"TF_COND_INVULNERABLE",
	"TF_COND_TELEPORTED",
	"TF_COND_TAUNTING",
	"TF_COND_INVULNERABLE_WEARINGOFF",
	"TF_COND_STEALTHED_BLINK",
	"TF_COND_SELECTED_TO_TELEPORT",
	"TF_COND_CRITBOOSTED",	// DO NOT RE-USE THIS -- THIS IS FOR KRITZKRIEG AND REVENGE CRITS ONLY
	"TF_COND_TMPDAMAGEBONUS",
	"TF_COND_FEIGN_DEATH",
	"TF_COND_PHASE",
	"TF_COND_STUNNED",		// Any type of stun. Check iStunFlags for more info.
	"TF_COND_OFFENSEBUFF",
	"TF_COND_SHIELD_CHARGE",
	"TF_COND_DEMO_BUFF",
	"TF_COND_ENERGY_BUFF",
	"TF_COND_RADIUSHEAL",
	"TF_COND_HEALTH_BUFF",
	"TF_COND_BURNING",
	"TF_COND_HEALTH_OVERHEALED",
	"TF_COND_URINE",
	"TF_COND_BLEEDING",
	"TF_COND_DEFENSEBUFF",	// 35% defense! No crit damage.
	"TF_COND_MAD_MILK",
	"TF_COND_MEGAHEAL",
	"TF_COND_REGENONDAMAGEBUFF",
	"TF_COND_MARKEDFORDEATH",
	"TF_COND_NOHEALINGDAMAGEBUFF",
	"TF_COND_SPEED_BOOST",				// = 32
	"TF_COND_CRITBOOSTED_PUMPKIN",		// Brandon hates bits
	"TF_COND_CRITBOOSTED_USER_BUFF",
	"TF_COND_CRITBOOSTED_DEMO_CHARGE",
	"TF_COND_SODAPOPPER_HYPE",
	"TF_COND_CRITBOOSTED_FIRST_BLOOD",	// arena mode first blood
	"TF_COND_CRITBOOSTED_BONUS_TIME",
	"TF_COND_CRITBOOSTED_CTF_CAPTURE",
	"TF_COND_CRITBOOSTED_ON_KILL",		// =40. KGB, etc.
	"TF_COND_CANNOT_SWITCH_FROM_MELEE",
	"TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK",	// 35% defense! Still damaged by crits.
	"TF_COND_REPROGRAMMED",				// Bots only
	"TF_COND_CRITBOOSTED_RAGE_BUFF",
	"TF_COND_DEFENSEBUFF_HIGH",			// 75% defense! Still damaged by crits.
	"TF_COND_SNIPERCHARGE_RAGE_BUFF",		// Sniper Rage - Charge time speed up
	"TF_COND_DISGUISE_WEARINGOFF",		// Applied for half-second post-disguise
	"TF_COND_MARKEDFORDEATH_SILENT",		// Sans sound
	"TF_COND_DISGUISED_AS_DISPENSER",
	"TF_COND_SAPPED",						// =50. Bots only
	"TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED",
	"TF_COND_INVULNERABLE_USER_BUFF",
	"TF_COND_HALLOWEEN_BOMB_HEAD",
	"TF_COND_HALLOWEEN_THRILLER",
	"TF_COND_RADIUSHEAL_ON_DAMAGE",
	"TF_COND_CRITBOOSTED_CARD_EFFECT",
	"TF_COND_INVULNERABLE_CARD_EFFECT",
	"TF_COND_MEDIGUN_UBER_BULLET_RESIST",
	"TF_COND_MEDIGUN_UBER_BLAST_RESIST",
	"TF_COND_MEDIGUN_UBER_FIRE_RESIST",		// =60
	"TF_COND_MEDIGUN_SMALL_BULLET_RESIST",
	"TF_COND_MEDIGUN_SMALL_BLAST_RESIST",
	"TF_COND_MEDIGUN_SMALL_FIRE_RESIST",
	"TF_COND_STEALTHED_USER_BUFF",			// Any class can have this
	"TF_COND_MEDIGUN_DEBUFF",
	"TF_COND_STEALTHED_USER_BUFF_FADING",
	"TF_COND_BULLET_IMMUNE",
	"TF_COND_BLAST_IMMUNE",
	"TF_COND_FIRE_IMMUNE",
	"TF_COND_PREVENT_DEATH",					// =70
	"TF_COND_MVM_BOT_STUN_RADIOWAVE", 		// Bots only
	"TF_COND_HALLOWEEN_SPEED_BOOST",
	"TF_COND_HALLOWEEN_QUICK_HEAL",
	"TF_COND_HALLOWEEN_GIANT",
	"TF_COND_HALLOWEEN_TINY",
	"TF_COND_HALLOWEEN_IN_HELL",
	"TF_COND_HALLOWEEN_GHOST_MODE",			// =77
	"TF_COND_MINICRITBOOSTED_ON_KILL",
	"TF_COND_OBSCURED_SMOKE",
	"TF_COND_PARACHUTE_DEPLOYED",				// =80
	"TF_COND_BLASTJUMPING",
	"TF_COND_HALLOWEEN_KART",
	"TF_COND_HALLOWEEN_KART_DASH",
	"TF_COND_BALLOON_HEAD",					// =84 larger head, lower-gravity-feeling jumps
	"TF_COND_MELEE_ONLY",						// =85 melee only
	"TF_COND_SWIMMING_CURSE",					// player movement become swimming movement
	"TF_COND_FREEZE_INPUT",					// freezes player input
	"TF_COND_HALLOWEEN_KART_CAGE",			// attach cage model to player while in kart
	"TF_COND_DONOTUSE_0",
	"TF_COND_RUNE_STRENGTH",
	"TF_COND_RUNE_HASTE",
	"TF_COND_RUNE_REGEN",
	"TF_COND_RUNE_RESIST",
	"TF_COND_RUNE_VAMPIRE",
	"TF_COND_RUNE_REFLECT",
	"TF_COND_RUNE_PRECISION",
	"TF_COND_RUNE_AGILITY",
	"TF_COND_GRAPPLINGHOOK",
	"TF_COND_GRAPPLINGHOOK_SAFEFALL",
	"TF_COND_GRAPPLINGHOOK_LATCHED",
	"TF_COND_GRAPPLINGHOOK_BLEEDING",
	"TF_COND_AFTERBURN_IMMUNE",
	"TF_COND_RUNE_KNOCKOUT",
	"TF_COND_RUNE_IMBALANCE",
	"TF_COND_CRITBOOSTED_RUNE_TEMP",
	"TF_COND_PASSTIME_INTERCEPTION",
	"TF_COND_SWIMMING_NO_EFFECTS",			// =107_DNOC_FT
	"TF_COND_PURGATORY",
	"TF_COND_RUNE_KING",
	"TF_COND_RUNE_PLAGUE",
	"TF_COND_RUNE_SUPERNOVA",
	"TF_COND_PLAGUE",
	"TF_COND_KING_BUFFED",
	"TF_COND_TEAM_GLOWS",					// used to show team glows to living players
	"TF_COND_KNOCKED_INTO_AIR",
	"TF_COND_COMPETITIVE_WINNER",
	"TF_COND_COMPETITIVE_LOSER",
	"TF_COND_HEALING_DEBUFF",
	"TF_COND_PASSTIME_PENALTY_DEBUFF",
	"TF_COND_GRAPPLED_TO_PLAYER",
	"TF_COND_GRAPPLED_BY_PLAYER",

	//
	// ADD NEW ITEMS HERE TO AVOID BREAKING DEMOS
	//

	// ******** Keep this block last! ********
	// Keep experimental conditions below and graduate out of it before shipping
#ifdef STAGING_ONLY
	"TF_COND_NO_COMBAT_SPEED_BOOST",		// STAGING_ENGY
	"TF_COND_TRANQ_SPY_BOOST",			// STAGING_SPY
	"TF_COND_TRANQ_MARKED",
//	"TF_COND_SPACE_GRAVITY",
//	"TF_COND_SELF_CONC",
	"TF_COND_ROCKETPACK",
	"TF_COND_STEALTHED_PHASE",  	
	"TF_COND_CLIP_OVERLOAD",
	"TF_COND_SPY_CLASS_STEAL",
#endif // STAGING_ONLY
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_aConditionNames ) == TF_COND_LAST );

const char *GetTFConditionName( ETFCond eCond )
{
	if ( ( eCond >= ARRAYSIZE( g_aConditionNames ) ) || ( eCond < 0 ) )
		return NULL;

	return g_aConditionNames[eCond];
}


ETFCond GetTFConditionFromName( const char *pszCondName )
{
	for( uint i=0; i<TF_COND_LAST; i++ )
	{ 
		ETFCond eCond = (ETFCond)i;
		if ( !V_stricmp( GetTFConditionName( eCond ), pszCondName ) ) 
			return eCond;
	} 

	Assert( !!"Invalid Condition Name" );
	return TF_COND_INVALID;
}


//-----------------------------------------------------------------------------
// Gametypes.
//-----------------------------------------------------------------------------
static const char *s_aGameTypeNames[] =
{
	"Undefined",
	"#Gametype_CTF",
	"#Gametype_CP",
	"#Gametype_Escort",
	"#Gametype_Arena",
	"#Gametype_MVM",
	"#Gametype_RobotDestruction",
	"#GameType_Passtime",
	"#GameType_PlayerDestruction",
};
COMPILE_TIME_ASSERT( TF_GAMETYPE_COUNT == ARRAYSIZE( s_aGameTypeNames ) );

const char *GetGameTypeName( ETFGameType gameType )
{
	return s_aGameTypeNames[ gameType ];
}

static const char *s_aEnumGameTypeName[] =
{
	"TF_GAMETYPE_UNDEFINED",
	"TF_GAMETYPE_CTF",
	"TF_GAMETYPE_CP",
	"TF_GAMETYPE_ESCORT",
	"TF_GAMETYPE_ARENA",
	"TF_GAMETYPE_MVM",
	"TF_GAMETYPE_RD",
	"TF_GAMETYPE_PASSTIME",
	"TF_GAMETYPE_PD"
};
COMPILE_TIME_ASSERT( TF_GAMETYPE_COUNT == ARRAYSIZE( s_aEnumGameTypeName ) );

const char *GetEnumGameTypeName( ETFGameType gameType )
{
	return s_aEnumGameTypeName[ gameType ];
}

ETFGameType GetGameTypeFromName( const char *pszGameType )
{
	for ( int i=0; i<TF_GAMETYPE_COUNT; ++i )
	{
		if ( FStrEq( pszGameType, s_aEnumGameTypeName[i] ) )
			return ETFGameType(i);
	}

	return TF_GAMETYPE_UNDEFINED;
}

//-----------------------------------------------------------------------------
// Ammo.
//-----------------------------------------------------------------------------
const char *g_aAmmoNames[] =
{
	"DUMMY AMMO",
	"TF_AMMO_PRIMARY",
	"TF_AMMO_SECONDARY",
	"TF_AMMO_METAL",
	"TF_AMMO_GRENADES1",
	"TF_AMMO_GRENADES2",
	"TF_AMMO_GRENADES3"
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_aAmmoNames ) == TF_AMMO_COUNT );

const char *GetAmmoName( int iAmmoType )
{
	ETFAmmoType eAmmoType = (ETFAmmoType)iAmmoType;
	return g_aAmmoNames[ eAmmoType ];
}

const char *g_aCTFEventNames[] =
{
	"",
	"TF_FLAGEVENT_PICKUP",
	"TF_FLAGEVENT_CAPTURE",
	"TF_FLAGEVENT_DEFEND",
	"TF_FLAGEVENT_DROPPED",
	"TF_FLAGEVENT_RETURNED",
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_aCTFEventNames ) == TF_NUM_FLAG_EVENTS );

const char *GetCTFEventName( ETFFlagEventTypes iEventType )
{
	return g_aCTFEventNames[ iEventType ];
}

ETFFlagEventTypes GetCTFEventTypeFromName( const char *pszName )
{
	for( int i=TF_FLAGEVENT_PICKUP; i < TF_NUM_FLAG_EVENTS; ++i )
	{
		if ( FStrEq( pszName, GetCTFEventName( (ETFFlagEventTypes)i ) ) )
		{
			return (ETFFlagEventTypes)i;
		}
	}
	
	Assert( false );
	return TF_NUM_FLAG_EVENTS;
}



const char *GetRDScoreMethodName( RDScoreMethod_t iScoreMethod )
{
	static const char *aRDScoreMethodNames[] =
	{
		"SCORE_UNDEFINED", // -1
		"SCORE_REACTOR_CAPTURED", // 0
		"SCORE_CORES_COLLECTED",
		"SCORE_REACTOR_RETURNED",
		"SCORE_REACTOR_STEAL",
		
		"NUM_SCORE_TYPES"
	};

	return aRDScoreMethodNames[ iScoreMethod + 1 ];
}

RDScoreMethod_t GetRDScoreMethodFromName( const char *pszName )
{
	for( int i=SCORE_UNDEFINED; i < NUM_SCORE_TYPES; ++i )
	{
		if ( FStrEq( pszName, GetRDScoreMethodName( (RDScoreMethod_t)i ) ) )
		{
			return (RDScoreMethod_t)i;
		}
	}
	
	Assert( false );
	return SCORE_UNDEFINED;
}

//-----------------------------------------------------------------------------
// Weapons.
//-----------------------------------------------------------------------------
const char *g_aWeaponNames[] =
{
	"TF_WEAPON_NONE",
	"TF_WEAPON_BAT",
	"TF_WEAPON_BAT_WOOD",
	"TF_WEAPON_BOTTLE", 
	"TF_WEAPON_FIREAXE",
	"TF_WEAPON_CLUB",
	"TF_WEAPON_CROWBAR",
	"TF_WEAPON_KNIFE",
	"TF_WEAPON_FISTS",
	"TF_WEAPON_SHOVEL",
	"TF_WEAPON_WRENCH",
	"TF_WEAPON_BONESAW",
	"TF_WEAPON_SHOTGUN_PRIMARY",
	"TF_WEAPON_SHOTGUN_SOLDIER",
	"TF_WEAPON_SHOTGUN_HWG",
	"TF_WEAPON_SHOTGUN_PYRO",
	"TF_WEAPON_SCATTERGUN",
	"TF_WEAPON_SNIPERRIFLE",
	"TF_WEAPON_MINIGUN",
	"TF_WEAPON_SMG",
	"TF_WEAPON_SYRINGEGUN_MEDIC",
	"TF_WEAPON_TRANQ",
	"TF_WEAPON_ROCKETLAUNCHER",
	"TF_WEAPON_GRENADELAUNCHER",
	"TF_WEAPON_PIPEBOMBLAUNCHER",
	"TF_WEAPON_FLAMETHROWER",
	"TF_WEAPON_GRENADE_NORMAL",
	"TF_WEAPON_GRENADE_CONCUSSION",
	"TF_WEAPON_GRENADE_NAIL",
	"TF_WEAPON_GRENADE_MIRV",
	"TF_WEAPON_GRENADE_MIRV_DEMOMAN",
	"TF_WEAPON_GRENADE_NAPALM",
	"TF_WEAPON_GRENADE_GAS",
	"TF_WEAPON_GRENADE_EMP",
	"TF_WEAPON_GRENADE_CALTROP",
	"TF_WEAPON_GRENADE_PIPEBOMB",
	"TF_WEAPON_GRENADE_SMOKE_BOMB",
	"TF_WEAPON_GRENADE_HEAL",
	"TF_WEAPON_GRENADE_STUNBALL",
	"TF_WEAPON_GRENADE_JAR",
	"TF_WEAPON_GRENADE_JAR_MILK",
	"TF_WEAPON_PISTOL",
	"TF_WEAPON_PISTOL_SCOUT",
	"TF_WEAPON_REVOLVER",
	"TF_WEAPON_NAILGUN",
	"TF_WEAPON_PDA",
	"TF_WEAPON_PDA_ENGINEER_BUILD",
	"TF_WEAPON_PDA_ENGINEER_DESTROY",
	"TF_WEAPON_PDA_SPY",
	"TF_WEAPON_BUILDER",
	"TF_WEAPON_MEDIGUN",
	"TF_WEAPON_GRENADE_MIRVBOMB",
	"TF_WEAPON_FLAMETHROWER_ROCKET",
	"TF_WEAPON_GRENADE_DEMOMAN",
	"TF_WEAPON_SENTRY_BULLET",
	"TF_WEAPON_SENTRY_ROCKET",
	"TF_WEAPON_DISPENSER",
	"TF_WEAPON_INVIS",
	"TF_WEAPON_FLAREGUN",
	"TF_WEAPON_LUNCHBOX",
	"TF_WEAPON_JAR",
	"TF_WEAPON_COMPOUND_BOW",
	"TF_WEAPON_BUFF_ITEM",
	"TF_WEAPON_PUMPKIN_BOMB",
	"TF_WEAPON_SWORD",
	"TF_WEAPON_ROCKETLAUNCHER_DIRECTHIT",
	"TF_WEAPON_LIFELINE",
	"TF_WEAPON_LASER_POINTER",
	"TF_WEAPON_DISPENSER_GUN",
	"TF_WEAPON_SENTRY_REVENGE",
	"TF_WEAPON_JAR_MILK",
	"TF_WEAPON_HANDGUN_SCOUT_PRIMARY",
	"TF_WEAPON_BAT_FISH",
	"TF_WEAPON_CROSSBOW",
	"TF_WEAPON_STICKBOMB",
	"TF_WEAPON_HANDGUN_SCOUT_SECONDARY",
	"TF_WEAPON_SODA_POPPER",
	"TF_WEAPON_SNIPERRIFLE_DECAP",
	"TF_WEAPON_RAYGUN",
	"TF_WEAPON_PARTICLE_CANNON",
	"TF_WEAPON_MECHANICAL_ARM",
	"TF_WEAPON_DRG_POMSON",
	"TF_WEAPON_BAT_GIFTWRAP",
	"TF_WEAPON_GRENADE_ORNAMENT_BALL",
	"TF_WEAPON_FLAREGUN_REVENGE",
	"TF_WEAPON_PEP_BRAWLER_BLASTER",
	"TF_WEAPON_CLEAVER",
	"TF_WEAPON_GRENADE_CLEAVER",
	"TF_WEAPON_STICKY_BALL_LAUNCHER",
	"TF_WEAPON_GRENADE_STICKY_BALL",
	"TF_WEAPON_SHOTGUN_BUILDING_RESCUE",
	"TF_WEAPON_CANNON",
	"TF_WEAPON_THROWABLE",
	"TF_WEAPON_GRENADE_THROWABLE",
	"TF_WEAPON_PDA_SPY_BUILD",
	"TF_WEAPON_GRENADE_WATERBALLOON",
	"TF_WEAPON_HARVESTER_SAW",
	"TF_WEAPON_SPELLBOOK",
	"TF_WEAPON_SPELLBOOK_PROJECTILE",
	"TF_WEAPON_SNIPERRIFLE_CLASSIC",
	"TF_WEAPON_PARACHUTE",
	"TF_WEAPON_GRAPPLINGHOOK",
	"TF_WEAPON_PASSTIME_GUN",
#ifdef STAGING_ONLY
	"TF_WEAPON_SNIPERRIFLE_REVOLVER",
#endif
	"TF_WEAPON_CHARGED_SMG",
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_aWeaponNames ) == TF_WEAPON_COUNT );

int g_aWeaponDamageTypes[] =
{
	DMG_GENERIC,	// TF_WEAPON_NONE
	DMG_CLUB,		// TF_WEAPON_BAT,
	DMG_CLUB,		// TF_WEAPON_BAT_WOOD,
	DMG_CLUB,		// TF_WEAPON_BOTTLE, 
	DMG_CLUB,		// TF_WEAPON_FIREAXE,
	DMG_CLUB,		// TF_WEAPON_CLUB,
	DMG_CLUB,		// TF_WEAPON_CROWBAR,
	DMG_SLASH,		// TF_WEAPON_KNIFE,
	DMG_CLUB,		// TF_WEAPON_FISTS,
	DMG_CLUB,		// TF_WEAPON_SHOVEL,
	DMG_CLUB,		// TF_WEAPON_WRENCH,
	DMG_SLASH,		// TF_WEAPON_BONESAW,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,	// TF_WEAPON_SHOTGUN_PRIMARY,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,	// TF_WEAPON_SHOTGUN_SOLDIER,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,	// TF_WEAPON_SHOTGUN_HWG,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,	// TF_WEAPON_SHOTGUN_PYRO,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,  // TF_WEAPON_SCATTERGUN,
	DMG_BULLET | DMG_USE_HITLOCATIONS,	// TF_WEAPON_SNIPERRIFLE,
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_MINIGUN,
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_SMG,
	DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE,		// TF_WEAPON_SYRINGEGUN_MEDIC,
	DMG_BULLET | DMG_USEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE | DMG_PARALYZE,		// TF_WEAPON_TRANQ,
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD,		// TF_WEAPON_ROCKETLAUNCHER,
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD,		// TF_WEAPON_GRENADELAUNCHER,
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_NOCLOSEDISTANCEMOD,		// TF_WEAPON_PIPEBOMBLAUNCHER,
	DMG_IGNITE | DMG_PREVENT_PHYSICS_FORCE | DMG_PREVENT_PHYSICS_FORCE,		// TF_WEAPON_FLAMETHROWER,
	DMG_BLAST | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_NORMAL,
	DMG_SONIC | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_CONCUSSION,
	DMG_BULLET | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_NAIL,
	DMG_BLAST | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_MIRV,
	DMG_BLAST | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_MIRV_DEMOMAN,
	DMG_BURN | DMG_RADIUS_MAX,		// TF_WEAPON_GRENADE_NAPALM,
	DMG_POISON | DMG_HALF_FALLOFF,		// TF_WEAPON_GRENADE_GAS,
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_PREVENT_PHYSICS_FORCE,		// TF_WEAPON_GRENADE_EMP,
	DMG_GENERIC,	// TF_WEAPON_GRENADE_CALTROP,
	DMG_BLAST | DMG_HALF_FALLOFF  | DMG_NOCLOSEDISTANCEMOD,		// TF_WEAPON_GRENADE_PIPEBOMB,
	DMG_GENERIC,	// TF_WEAPON_GRENADE_SMOKE_BOMB,
	DMG_GENERIC,	// TF_WEAPON_GRENADE_HEAL
	DMG_CLUB,		// TF_WEAPON_GRENADE_STUNBALL
	DMG_CLUB,		// TF_WEAPON_GRENADE_JAR
	DMG_CLUB,		// TF_WEAPON_GRENADE_JAR_MILK
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_PISTOL,
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_PISTOL_SCOUT,
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_REVOLVER,
	DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD | DMG_PREVENT_PHYSICS_FORCE,		// TF_WEAPON_NAILGUN,
	DMG_BULLET,		// TF_WEAPON_PDA,
	DMG_BULLET,		// TF_WEAPON_PDA_ENGINEER_BUILD,
	DMG_BULLET,		// TF_WEAPON_PDA_ENGINEER_DESTROY,
	DMG_BULLET,		// TF_WEAPON_PDA_SPY,
	DMG_BULLET,		// TF_WEAPON_BUILDER
	DMG_BULLET,		// TF_WEAPON_MEDIGUN
	DMG_BLAST,		// TF_WEAPON_GRENADE_MIRVBOMB
	DMG_BLAST | DMG_IGNITE | DMG_RADIUS_MAX,		// TF_WEAPON_FLAMETHROWER_ROCKET
	DMG_BLAST | DMG_HALF_FALLOFF,					// TF_WEAPON_GRENADE_DEMOMAN
	DMG_BULLET,	// TF_WEAPON_SENTRY_BULLET
	DMG_BLAST,	// TF_WEAPON_SENTRY_ROCKET
	DMG_GENERIC,	// TF_WEAPON_DISPENSER
	DMG_GENERIC,	// TF_WEAPON_INVIS
	DMG_BULLET | DMG_IGNITE,		// TF_WEAPON_FLAREGUN
	DMG_GENERIC,	// TF_WEAPON_LUNCHBOX
	DMG_GENERIC,	// TF_WEAPON_JAR
	DMG_BULLET | DMG_USE_HITLOCATIONS,		// TF_WEAPON_COMPOUND_BOW
	DMG_GENERIC,	// TF_WEAPON_BUFF_ITEM
	DMG_CLUB,		// TF_WEAPON_PUMPKIN_BOMB
	DMG_CLUB,		// TF_WEAPON_SWORD
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD,		// TF_WEAPON_ROCKETLAUNCHER_DIRECTHIT,
	DMG_CLUB,		// TF_WEAPON_LIFELINE
	DMG_CLUB,		// TF_WEAPON_LASER_POINTER
	DMG_BULLET,		// TF_WEAPON_DISPENSER_GUN
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD, // TF_WEAPON_SENTRY_REVENGE
	DMG_GENERIC,	// TF_WEAPON_JAR_MILK
	DMG_BUCKSHOT | DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_HANDGUN_SCOUT_PRIMARY
	DMG_CLUB,		// TF_WEAPON_BAT_FISH
	DMG_BULLET | DMG_USE_HITLOCATIONS,
	DMG_CLUB, // TF_WEAPON_STICKBOMB
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_HANDGUN_SCOUT_SECONDARY
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,  // TF_WEAPON_SODA_POPPER,
	DMG_BULLET | DMG_USE_HITLOCATIONS,	// TF_WEAPON_SNIPERRIFLE_DECAP,
	DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD,	// TF_WEAPON_RAYGUN,
	DMG_BLAST | DMG_HALF_FALLOFF | DMG_USEDISTANCEMOD,		// TF_WEAPON_PARTICLE_CANNON,
	DMG_BULLET | DMG_USEDISTANCEMOD,	// TF_WEAPON_MECHANICAL_ARM,
	DMG_BULLET | DMG_USEDISTANCEMOD | DMG_NOCLOSEDISTANCEMOD,	// TF_WEAPON_DRG_POMSON,
	DMG_CLUB,		// TF_WEAPON_BAT_GIFTWRAP,
	DMG_CLUB,		// TF_WEAPON_GRENADE_ORNAMENT_BALL
	DMG_BULLET | DMG_IGNITE,	// TF_WEAPON_FLAREGUN_REVENGE,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,  // TF_WEAPON_PEP_BRAWLER_BLASTER,
	DMG_GENERIC,	// TF_WEAPON_CLEAVER
	DMG_SLASH,		// TF_WEAPON_GRENADE_CLEAVER
	DMG_GENERIC,	// TF_WEAPON_STICKY_BALL_LAUNCHER,
	DMG_GENERIC,	// TF_WEAPON_GRENADE_STICKY_BALL,
	DMG_BUCKSHOT | DMG_USEDISTANCEMOD,	// TF_WEAPON_SHOTGUN_BUILDING_RESCUE,
	DMG_BLAST | DMG_HALF_FALLOFF,	// TF_WEAPON_CANNON
	DMG_BULLET,		// TF_WEAPON_THROWABLE
	DMG_BULLET,		// TF_WEAPON_GRENADE_THROWABLE
	DMG_BULLET,		// TF_WEAPON_PDA_SPY_BUILD
	DMG_BULLET,		// TF_WEAPON_GRENADE_WATERBALLOON
	DMG_SLASH,		// TF_WEAPON_HARVESTER_SAW
	DMG_GENERIC,	// TF_WEAPON_SPELLBOOK
	DMG_GENERIC,	// TF_WEAPON_SPELLBOOK_PROJECTILE
	DMG_BULLET | DMG_USE_HITLOCATIONS,	// TF_WEAPON_SNIPERRIFLE_CLASSIC,
	DMG_GENERIC, // TF_WEAPON_PARACHUTE,
	DMG_GENERIC, // TF_WEAPON_GRAPPLINGHOOK,
	DMG_GENERIC, // TF_WEAPON_PASSTIME_GUN
#ifdef STAGING_ONLY
	DMG_BULLET | DMG_USE_HITLOCATIONS,	// TF_WEAPON_SNIPERRIFLE_REVOLVER,
#endif
	DMG_BULLET | DMG_USEDISTANCEMOD,		// TF_WEAPON_CHARGED_SMG,
};

const char *g_szSpecialDamageNames[] =
{
	"",
	"TF_DMG_CUSTOM_HEADSHOT",
	"TF_DMG_CUSTOM_BACKSTAB",
	"TF_DMG_CUSTOM_BURNING",
	"TF_DMG_WRENCH_FIX",
	"TF_DMG_CUSTOM_MINIGUN",
	"TF_DMG_CUSTOM_SUICIDE",
	"TF_DMG_CUSTOM_TAUNTATK_HADOUKEN",
	"TF_DMG_CUSTOM_BURNING_FLARE",
	"TF_DMG_CUSTOM_TAUNTATK_HIGH_NOON",
	"TF_DMG_CUSTOM_TAUNTATK_GRAND_SLAM",
	"TF_DMG_CUSTOM_PENETRATE_MY_TEAM",
	"TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS",
	"TF_DMG_CUSTOM_TAUNTATK_FENCING",
	"TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE",
	"TF_DMG_CUSTOM_TAUNTATK_ARROW_STAB",
	"TF_DMG_CUSTOM_TELEFRAG",
	"TF_DMG_CUSTOM_BURNING_ARROW",
	"TF_DMG_CUSTOM_FLYINGBURN",
	"TF_DMG_CUSTOM_PUMPKIN_BOMB",
	"TF_DMG_CUSTOM_DECAPITATION",
	"TF_DMG_CUSTOM_TAUNTATK_GRENADE",
	"TF_DMG_CUSTOM_BASEBALL",
	"TF_DMG_CUSTOM_CHARGE_IMPACT",
	"TF_DMG_CUSTOM_TAUNTATK_BARBARIAN_SWING",
	"TF_DMG_CUSTOM_AIR_STICKY_BURST",
	"TF_DMG_CUSTOM_DEFENSIVE_STICKY",
	"TF_DMG_CUSTOM_PICKAXE",
	"TF_DMG_CUSTOM_ROCKET_DIRECTHIT",
	"TF_DMG_CUSTOM_TAUNTATK_UBERSLICE",
	"TF_DMG_CUSTOM_PLAYER_SENTRY",
	"TF_DMG_CUSTOM_STANDARD_STICKY",
	"TF_DMG_CUSTOM_SHOTGUN_REVENGE_CRIT",
	"TF_DMG_CUSTOM_TAUNTATK_ENGINEER_GUITAR_SMASH",
	"TF_DMG_CUSTOM_BLEEDING",
	"TF_DMG_CUSTOM_GOLD_WRENCH",
	"TF_DMG_CUSTOM_CARRIED_BUILDING",
	"TF_DMG_CUSTOM_COMBO_PUNCH",
	"TF_DMG_CUSTOM_TAUNTATK_ENGINEER_ARM_KILL",
	"TF_DMG_CUSTOM_FISH_KILL",
	"TF_DMG_CUSTOM_TRIGGER_HURT",
	"TF_DMG_CUSTOM_DECAPITATION_BOSS",
	"TF_DMG_CUSTOM_STICKBOMB_EXPLOSION",
	"TF_DMG_CUSTOM_AEGIS_ROUND",
	"TF_DMG_CUSTOM_FLARE_EXPLOSION",
	"TF_DMG_CUSTOM_BOOTS_STOMP",
	"TF_DMG_CUSTOM_PLASMA",
	"TF_DMG_CUSTOM_PLASMA_CHARGED",
	"TF_DMG_CUSTOM_PLASMA_GIB",
	"TF_DMG_CUSTOM_PRACTICE_STICKY",
	"TF_DMG_CUSTOM_EYEBALL_ROCKET",
	"TF_DMG_CUSTOM_HEADSHOT_DECAPITATION",
	"TF_DMG_CUSTOM_TAUNTATK_ARMAGEDDON",
	"TF_DMG_CUSTOM_FLARE_PELLET",
	"TF_DMG_CUSTOM_CLEAVER",
	"TF_DMG_CUSTOM_CLEAVER_CRIT",
	"TF_DMG_CUSTOM_SAPPER_RECORDER_DEATH",
	"TF_DMG_CUSTOM_MERASMUS_PLAYER_BOMB",
	"TF_DMG_CUSTOM_MERASMUS_GRENADE",
	"TF_DMG_CUSTOM_MERASMUS_ZAP",
	"TF_DMG_CUSTOM_MERASMUS_DECAPITATION",
	"TF_DMG_CUSTOM_CANNONBALL_PUSH",
	"TF_DMG_CUSTOM_TAUNTATK_ALLCLASS_GUITAR_RIFF",
	"TF_DMG_CUSTOM_THROWABLE",
	"TF_DMG_CUSTOM_THROWABLE_KILL",
	"TF_DMG_CUSTOM_SPELL_TELEPORT",
	"TF_DMG_CUSTOM_SPELL_SKELETON",
	"TF_DMG_CUSTOM_SPELL_MIRV",
	"TF_DMG_CUSTOM_SPELL_METEOR",
	"TF_DMG_CUSTOM_SPELL_LIGHTNING",
	"TF_DMG_CUSTOM_SPELL_FIREBALL",
	"TF_DMG_CUSTOM_SPELL_MONOCULUS",
	"TF_DMG_CUSTOM_SPELL_BLASTJUMP",
	"TF_DMG_CUSTOM_SPELL_BATS",
	"TF_DMG_CUSTOM_SPELL_TINY",
	"TF_DMG_CUSTOM_KART",
	"TF_DMG_CUSTOM_GIANT_HAMMER",
	"TF_DMG_CUSTOM_RUNE_REFLECT",
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_szSpecialDamageNames ) == TF_DMG_CUSTOM_END );

const char *GetCustomDamageName( ETFDmgCustom eDmgCustom )
{
	if ( ( eDmgCustom >= ARRAYSIZE( g_szSpecialDamageNames ) ) || ( eDmgCustom < 0 ) )
		return NULL;

	return g_szSpecialDamageNames[eDmgCustom];
}

ETFDmgCustom GetCustomDamageFromName( const char *pszCustomDmgName )
{
	for( uint i=0; i<TF_DMG_CUSTOM_END; i++ )
	{ 
		ETFDmgCustom eDmgCustom = (ETFDmgCustom)i;
		if ( !V_stricmp( GetCustomDamageName( eDmgCustom ), pszCustomDmgName ) ) 
			return eDmgCustom;
	} 

	Assert( !!"Invalid Custom Damage Name" );
	return TF_DMG_CUSTOM_NONE;
}

const char *g_szProjectileNames[] =
{
	"",
	"projectile_bullet",
	"projectile_rocket",
	"projectile_pipe",
	"projectile_pipe_remote",
	"projectile_syringe",
	"projectile_flare",
	"projectile_jar",
	"projectile_arrow",
	"projectile_flame_rocket",
	"projectile_jar_milk",
	"projectile_healing_bolt",
	"projectile_energy_ball",
	"projectile_energy_ring",
	"projectile_pipe_remote_practice",
	"projectile_cleaver",
	"projectile_sticky_ball",
	"projectile_cannonball",
	"projectile_building_repair_bolt",
	"projectile_festive_arrow",
	"projectile_throwable",
	"projectile_spellfireball",
	"projectile_festive_urine",
	"projectile_festive_healing_bolt",
	"projectfile_breadmonster_jarate",
	"projectfile_breadmonster_madmilk",

	"projectile_grapplinghook",
	"projectile_sentry_rocket",
	"projectile_bread_monster",

#ifdef STAGING_ONLY	
	// Staging
	"projectile_tranq",
	"projectile_sniperbullet",
	"projectile_throwing_knife",
	"projectile_grenade_concussion",
	"projectile_grenade_teleport",
	"projectile_jarate_bolt",
#endif
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_szProjectileNames ) == TF_NUM_PROJECTILES );

// these map to the projectiles named in g_szProjectileNames
int g_iProjectileWeapons[] = 
{
	TF_WEAPON_NONE,
	TF_WEAPON_PISTOL,
	TF_WEAPON_ROCKETLAUNCHER,
	TF_WEAPON_PIPEBOMBLAUNCHER,
	TF_WEAPON_GRENADELAUNCHER,
	TF_WEAPON_SYRINGEGUN_MEDIC,
	TF_WEAPON_FLAREGUN,
	TF_WEAPON_JAR,
	TF_WEAPON_COMPOUND_BOW,
	TF_PROJECTILE_FLAME_ROCKET,
	TF_WEAPON_JAR_MILK,
	TF_WEAPON_CROSSBOW,
	TF_WEAPON_PARTICLE_CANNON,
	TF_WEAPON_RAYGUN,
	TF_WEAPON_GRENADELAUNCHER,			// practice pipes should never kill anyone anyway
	TF_WEAPON_CLEAVER,
	TF_WEAPON_STICKY_BALL_LAUNCHER,
	TF_WEAPON_CANNON,
	TF_WEAPON_SHOTGUN_BUILDING_RESCUE,
	TF_WEAPON_COMPOUND_BOW,
	TF_WEAPON_THROWABLE,
	TF_WEAPON_SPELLBOOK,
	TF_WEAPON_JAR,
	TF_WEAPON_CROSSBOW,
	TF_WEAPON_JAR,
	TF_WEAPON_JAR,

	TF_PROJECTILE_GRAPPLINGHOOK,
	TF_WEAPON_SENTRY_ROCKET,
	TF_WEAPON_THROWABLE,

#ifdef STAGING_ONLY
	// Staging
	TF_WEAPON_REVOLVER,
	TF_WEAPON_SNIPERRIFLE,
	TF_WEAPON_THROWABLE,
	TF_WEAPON_THROWABLE,
	TF_WEAPON_THROWABLE,
	TF_WEAPON_CROSSBOW,
#endif
};

COMPILE_TIME_ASSERT( ARRAYSIZE( g_szProjectileNames ) == ARRAYSIZE( g_iProjectileWeapons ) );

//-----------------------------------------------------------------------------
// Taunt attacks
//-----------------------------------------------------------------------------
static const char* taunt_attack_name[] =
{
	"TAUNTATK_NONE",
	"TAUNTATK_PYRO_HADOUKEN",
	"TAUNTATK_HEAVY_EAT",
	"TAUNTATK_HEAVY_RADIAL_BUFF",
	"TAUNTATK_HEAVY_HIGH_NOON",
	"TAUNTATK_SCOUT_DRINK",
	"TAUNTATK_SCOUT_GRAND_SLAM",
	"TAUNTATK_MEDIC_INHALE",
	"TAUNTATK_SPY_FENCING_SLASH_A",
	"TAUNTATK_SPY_FENCING_SLASH_B",
	"TAUNTATK_SPY_FENCING_STAB",
	"TAUNTATK_RPS_KILL",
	"TAUNTATK_SNIPER_ARROW_STAB_IMPALE",
	"TAUNTATK_SNIPER_ARROW_STAB_KILL",
	"TAUNTATK_SOLDIER_GRENADE_KILL",
	"TAUNTATK_DEMOMAN_BARBARIAN_SWING",
	"TAUNTATK_MEDIC_UBERSLICE_IMPALE",
	"TAUNTATK_MEDIC_UBERSLICE_KILL",
	"TAUNTATK_FLIP_LAND_PARTICLE",
	"TAUNTATK_RPS_PARTICLE",
	"TAUNTATK_HIGHFIVE_PARTICLE",
	"TAUNTATK_ENGINEER_GUITAR_SMASH",
	"TAUNTATK_ENGINEER_ARM_IMPALE",
	"TAUNTATK_ENGINEER_ARM_KILL",
	"TAUNTATK_ENGINEER_ARM_BLEND",
	"TAUNTATK_SOLDIER_GRENADE_KILL_WORMSIGN",
	"TAUNTATK_SHOW_ITEM",
	"TAUNTATK_MEDIC_RELEASE_DOVES",
	"TAUNTATK_PYRO_ARMAGEDDON",
	"TAUNTATK_PYRO_SCORCHSHOT",
	"TAUNTATK_ALLCLASS_GUITAR_RIFF",
	"TAUNTATK_MEDIC_HEROIC_TAUNT",

	//
	// INSERT NEW ITEMS HERE TO AVOID BREAKING DEMOS
	//
};

COMPILE_TIME_ASSERT( ARRAYSIZE( taunt_attack_name ) == TAUNTATK_COUNT );

taunt_attack_t GetTauntAttackByName( const char* pszTauntAttackName )
{
	if ( pszTauntAttackName )
	{
		for ( int i=0; i<ARRAYSIZE( taunt_attack_name ); ++i )
		{
			if ( !V_stricmp( pszTauntAttackName, taunt_attack_name[i] ) )
			{
				return (taunt_attack_t)i;
			}
		}
	}

	return TAUNTATK_NONE;
}

const char *g_pszHintMessages[] =
{
	"#Hint_spotted_a_friend",
	"#Hint_spotted_an_enemy",
	"#Hint_killing_enemies_is_good",
	"#Hint_out_of_ammo",
	"#Hint_turn_off_hints",
	"#Hint_pickup_ammo",
	"#Hint_Cannot_Teleport_With_Flag",
	"#Hint_Cannot_Cloak_With_Flag",
	"#Hint_Cannot_Disguise_With_Flag",
	"#Hint_Cannot_Attack_While_Feign_Armed",
	"#Hint_ClassMenu",

// Grenades
	"#Hint_gren_caltrops",
	"#Hint_gren_concussion",
	"#Hint_gren_emp",
	"#Hint_gren_gas",
	"#Hint_gren_mirv",
	"#Hint_gren_nail",
	"#Hint_gren_napalm",
	"#Hint_gren_normal",

// Altfires
	"#Hint_altfire_sniperrifle",
	"#Hint_altfire_flamethrower",
	"#Hint_altfire_grenadelauncher",
	"#Hint_altfire_pipebomblauncher",
	"#Hint_altfire_rotate_building",

// Soldier
	"#Hint_Soldier_rpg_reload",

// Engineer
	"#Hint_Engineer_use_wrench_onown",
	"#Hint_Engineer_use_wrench_onother",
	"#Hint_Engineer_use_wrench_onfriend",
	"#Hint_Engineer_build_sentrygun",
	"#Hint_Engineer_build_dispenser",
	"#Hint_Engineer_build_teleporters",
	"#Hint_Engineer_pickup_metal",
	"#Hint_Engineer_repair_object",
	"#Hint_Engineer_metal_to_upgrade",
	"#Hint_Engineer_upgrade_sentrygun",

	"#Hint_object_has_sapper",

	"#Hint_object_your_object_sapped",
	"#Hint_enemy_using_dispenser",
	"#Hint_enemy_using_tp_entrance",
	"#Hint_enemy_using_tp_exit",

	"#Hint_Cannot_Phase_With_Flag",

	"#Hint_Cannot_Attack_While_Cloaked",

	"#Hint_Cannot_Arm_Feign_Now",
};

const char *g_pszArrowModels[] = 
{
	"models/weapons/w_models/w_arrow.mdl",
	"models/weapons/w_models/w_repair_claw.mdl",
	"models/weapons/w_models/w_baseball.mdl",
	"models/weapons/w_models/w_arrow_xmas.mdl",
	"models/weapons/w_models/w_syringe_proj.mdl",
#ifdef STAGING_ONLY
	"models/workshop/weapons/c_models/c_crusaders_crossbow/c_crusaders_crossbow_xmas_proj.mdl",
#else
	"models/weapons/c_models/c_crusaders_crossbow/c_crusaders_crossbow_xmas_proj.mdl",
#endif
	"models/weapons/w_models/w_breadmonster/w_breadmonster.mdl",
	"models/weapons/c_models/c_grapple_proj/c_grapple_proj.mdl",

#ifdef STAGING_ONLY
	"models/workshop_partner/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl"
#else
	"models/weapons/c_models/c_sd_cleaver/c_sd_cleaver.mdl"
#endif
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszArrowModels ) == TF_ARROW_MODEL_COUNT );

const char *g_pszCampaignMedalIcons[] =
{
	"",
	// Gun Mettle Campaign
	"../hud/coin_summer2015_gravel",
	"../hud/coin_summer2015_bronze",
	"../hud/coin_summer2015_silver",
	"../hud/coin_summer2015_gold",

	// Invasion Community Update
	"../HUD/scoreboard_invasion",

	// Halloween
	"../HUD/coin_halloween2015_gravel",
	"../HUD/coin_halloween2015_bronze",
	"../HUD/coin_halloween2015_silver",
	"../HUD/coin_halloween2015_gold",

	// Tough Break Campaign
	"../HUD/stamp_winter2016_gravel1",
	"../HUD/stamp_winter2016_bronze1",
	"../HUD/stamp_winter2016_silver1",
	"../HUD/stamp_winter2016_gold1",

	"../HUD/stamp_winter2016_gravel2",
	"../HUD/stamp_winter2016_bronze2",
	"../HUD/stamp_winter2016_silver2",
	"../HUD/stamp_winter2016_gold2",
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszCampaignMedalIcons ) == CAMPAIGN_MEDAL_DISPLAY_TYPE_COUNT );

//-----------------------------------------------------------------------------
// Dead Calling Cards
const char *g_pszDeathCallingCardModels[] =
{
	"",			// Empty at zero
	"models/props_gameplay/tombstone_specialdelivery.mdl",		// Scout PolyCount Set
	"models/props_gameplay/tombstone_crocostyle.mdl",		// Sniper PolyCount Set
	"models/props_gameplay/tombstone_tankbuster.mdl",		// Solider PolyCount Set
	"models/props_gameplay/tombstone_gasjockey.mdl",		// Pyro PolyCount Set
};

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int GetWeaponId( const char *pszWeaponName )
{
	// if this doesn't match, you need to add missing weapons to the array
	COMPILE_TIME_ASSERT( TF_WEAPON_COUNT == ARRAYSIZE( g_aWeaponNames ) );

	for ( int iWeapon = 0; iWeapon < ARRAYSIZE( g_aWeaponNames ); ++iWeapon )
	{
		if ( !Q_stricmp( pszWeaponName, g_aWeaponNames[iWeapon] ) )
			return iWeapon;
	}

	return TF_WEAPON_NONE;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char *WeaponIdToAlias( int iWeapon )
{
	// if this doesn't match, you need to add missing weapons to the array
	COMPILE_TIME_ASSERT( TF_WEAPON_COUNT == ARRAYSIZE( g_aWeaponNames ) );

	if ( ( iWeapon >= ARRAYSIZE( g_aWeaponNames ) ) || ( iWeapon < 0 ) )
		return NULL;

	return g_aWeaponNames[iWeapon];
}

#ifdef GAME_DLL

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int GetWeaponFromDamage( const CTakeDamageInfo &info )
{
	int iWeapon = TF_WEAPON_NONE;

	const char *killer_weapon_name = "";

	// Find the killer & the scorer
	CBaseEntity *pInflictor = info.GetInflictor();
	CBaseEntity *pKiller = info.GetAttacker();
	CBasePlayer *pScorer = TFGameRules()->GetDeathScorer( pKiller, pInflictor, NULL );

	// find the weapon the killer used

	if ( pScorer )	// Is the killer a client?
	{
		if ( pInflictor )
		{
			if ( pInflictor == pScorer )
			{
				// If the inflictor is the killer,  then it must be their current weapon doing the damage
				if ( pScorer->GetActiveWeapon() )
				{
					killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname();
				}
			}
			else
			{
				killer_weapon_name = STRING( pInflictor->m_iClassname );  // it's just that easy
			}
		}
	}
	else if ( pInflictor )
	{
		killer_weapon_name = STRING( pInflictor->m_iClassname );
	}

	if ( !Q_strnicmp( killer_weapon_name, "tf_projectile", 13 ) )
	{
		for( int i = 0; i < ARRAYSIZE( g_szProjectileNames ); i++ )
		{
			if ( !Q_stricmp( &killer_weapon_name[ 3 ], g_szProjectileNames[ i ] ) )
			{
				iWeapon = g_iProjectileWeapons[ i ];
				break;
			}
		}
	}
	else
	{
		int iLen = Q_strlen( killer_weapon_name );

		// strip off _projectile from projectiles shot from other projectiles
		if ( ( iLen < 256 ) && ( iLen > 11 ) && !Q_stricmp( &killer_weapon_name[ iLen - 11 ], "_projectile" ) )
		{
			char temp[ 256 ];
			V_strcpy_safe( temp, killer_weapon_name );
			temp[ iLen - 11 ] = 0;

			// set the weapon used
			iWeapon = GetWeaponId( temp );
		}
		else
		{
			// set the weapon used
			iWeapon = GetWeaponId( killer_weapon_name );
		}
	}

	AssertMsg( iWeapon >= 0 && iWeapon < TF_WEAPON_COUNT, "Referencing a weapon not in tf_shareddefs.h.  Check to make it's defined and it's mapped correctly in g_szProjectileNames and g_iProjectileWeapons." );
	return iWeapon;
}

#endif

// ------------------------------------------------------------------------------------------------ //
// CObjectInfo tables.
// ------------------------------------------------------------------------------------------------ //

CObjectInfo::CObjectInfo( const char *pObjectName )
{
	m_pObjectName = pObjectName;
	m_pClassName = NULL;
	m_flBuildTime = -9999;
	m_nMaxObjects = -9999;
	m_Cost = -9999;
	m_CostMultiplierPerInstance = -999;
	m_flUpgradeDuration = -999;
	m_UpgradeCost = -9999;
	m_MaxUpgradeLevel = -9999;
	m_pBuilderWeaponName = NULL;
	m_pBuilderPlacementString = NULL;
	m_SelectionSlot = -9999;
	m_SelectionPosition = -9999;
	m_bSolidToPlayerMovement = false;
	m_pIconActive = NULL;
	m_pIconInactive = NULL;
	m_pIconMenu = NULL;
	m_pViewModel = NULL;
	m_pPlayerModel = NULL;
	m_iDisplayPriority = 0;
	m_bVisibleInWeaponSelection = true;
	m_pExplodeSound = NULL;
	m_pUpgradeSound = NULL;
	m_pExplosionParticleEffect = NULL;
	m_bAutoSwitchTo = false;
	m_iBuildCount = 0;
	m_iNumAltModes = 0;
	m_bRequiresOwnBuilder = false;
}


CObjectInfo::~CObjectInfo()
{
	delete [] m_pClassName;
	delete [] m_pStatusName;
	delete [] m_pBuilderWeaponName;
	delete [] m_pBuilderPlacementString;
	delete [] m_pIconActive;
	delete [] m_pIconInactive;
	delete [] m_pIconMenu;
	delete [] m_pViewModel;
	delete [] m_pPlayerModel;
	delete [] m_pExplodeSound;
	delete [] m_pUpgradeSound;
	delete [] m_pExplosionParticleEffect;
}

CObjectInfo g_ObjectInfos[OBJ_LAST] =
{
	CObjectInfo( "OBJ_DISPENSER" ),
	CObjectInfo( "OBJ_TELEPORTER" ),
	CObjectInfo( "OBJ_SENTRYGUN" ),
	CObjectInfo( "OBJ_ATTACHMENT_SAPPER" ),
#ifdef STAGING_ONLY
	CObjectInfo( "OBJ_CATAPULT" ),
	CObjectInfo( "OBJ_SPY_TRAP" ),
#endif
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_ObjectInfos ) == OBJ_LAST );

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int GetBuildableId( const char *pszBuildableName )
{
	for ( int iBuildable = 0; iBuildable < OBJ_LAST; ++iBuildable )
	{
		if ( !Q_stricmp( pszBuildableName, g_ObjectInfos[iBuildable].m_pObjectName ) )
			return iBuildable;
	}

	return OBJ_LAST;
}

bool AreObjectInfosLoaded()
{
	return g_ObjectInfos[0].m_pClassName != NULL;
}

static void SpewFileInfo( IBaseFileSystem *pFileSystem, const char *resourceName, const char *pathID, KeyValues *pValues )
{
	bool bFileExists = pFileSystem->FileExists( resourceName, pathID );
	bool bFileWritable = pFileSystem->IsFileWritable( resourceName, pathID );
	unsigned int nSize = pFileSystem->Size( resourceName, pathID );

	Msg( "resourceName:%s pathID:%s bFileExists:%d size:%u writeable:%d\n", resourceName, pathID, bFileExists, nSize, bFileWritable );

	unsigned int filesize = ( unsigned int )-1;
	FileHandle_t f = filesystem->Open( resourceName, "rb", pathID );
	if ( f )
	{
		filesize = filesystem->Size( f );
		filesystem->Close( f );
	}

	Msg( " FileHandle_t:%p size:%u\n", f, filesize );

	IFileSystem *pFS = 	(IFileSystem *)filesystem;

	char cwd[ MAX_PATH ];
	cwd[ 0 ] = 0;
	pFS->GetCurrentDirectory( cwd, ARRAYSIZE( cwd ) );

	bool bAvailable = pFS->IsFileImmediatelyAvailable( resourceName );

	Msg( " IsFileImmediatelyAvailable:%d cwd:%s\n", bAvailable, cwd );

	pFS->PrintSearchPaths();

	if ( pValues )
	{
		Msg( "Keys:" );
		KeyValuesDumpAsDevMsg( pValues, 2, 0 );
	}
}

void LoadObjectInfos( IBaseFileSystem *pFileSystem )
{
	const char *pFilename = "scripts/objects.txt";

	// Make sure this stuff hasn't already been loaded.
	Assert( !AreObjectInfosLoaded() );

	KeyValues *pValues = new KeyValues( "Object descriptions" );
	if ( !pValues->LoadFromFile( pFileSystem, pFilename, "GAME" ) )
	{
		// Getting "Can't open scripts/objects.txt for object info." errors. Spew file information
		//  before the Error() call which should show up in the minidumps.
		SpewFileInfo( pFileSystem, pFilename, "GAME", NULL );

		Error( "Can't open %s for object info.", pFilename );
		pValues->deleteThis();
		return;
	}

	// Now read each class's information in.
	for ( int iObj=0; iObj < ARRAYSIZE( g_ObjectInfos ); iObj++ )
	{
		CObjectInfo *pInfo = &g_ObjectInfos[iObj];
		KeyValues *pSub = pValues->FindKey( pInfo->m_pObjectName );
		if ( !pSub )
		{
			// Getting "Missing section 'OBJ_DISPENSER' from scripts/objects.txt" errors.
			SpewFileInfo( pFileSystem, pFilename, "GAME", pValues );

			// It seems that folks have corrupt files when these errors are seen in http://minidump.
			// Does it make sense to call the below Steam API so it'll force a validation next startup time?
			// Need to verify it's real corruption and not someone dorking around with their objects.txt file...
			//
			// From Martin Otten: If you have a file on disc and you�re 100% sure it�s
			//  corrupt, call ISteamApps::MarkContentCorrupt( false ), before you shutdown
			//  the game. This will cause a content validation in Steam.

			Error( "Missing section '%s' from %s.", pInfo->m_pObjectName, pFilename );
			pValues->deleteThis();
			return;
		}

		// Read all the info in.
		if ( (pInfo->m_flBuildTime = pSub->GetFloat( "BuildTime", -999 )) == -999 ||
			(pInfo->m_nMaxObjects = pSub->GetInt( "MaxObjects", -999 )) == -999 ||
			(pInfo->m_Cost = pSub->GetInt( "Cost", -999 )) == -999 ||
			(pInfo->m_CostMultiplierPerInstance = pSub->GetFloat( "CostMultiplier", -999 )) == -999 ||
			(pInfo->m_flUpgradeDuration = pSub->GetFloat( "UpgradeDuration", -999 )) == -999 ||
			(pInfo->m_UpgradeCost = pSub->GetInt( "UpgradeCost", -999 )) == -999 ||
			(pInfo->m_MaxUpgradeLevel = pSub->GetInt( "MaxUpgradeLevel", -999 )) == -999 ||
			(pInfo->m_SelectionSlot = pSub->GetInt( "SelectionSlot", -999 )) == -999 ||
			(pInfo->m_iBuildCount = pSub->GetInt( "BuildCount", -999 )) == -999 ||
			(pInfo->m_SelectionPosition = pSub->GetInt( "SelectionPosition", -999 )) == -999 )
		{
			SpewFileInfo( pFileSystem, pFilename, "GAME", pValues );

			Error( "Missing data for object '%s' in %s.", pInfo->m_pObjectName, pFilename );
			pValues->deleteThis();
			return;
		}

		pInfo->m_pClassName = ReadAndAllocStringValue( pSub, "ClassName", pFilename );
		pInfo->m_pStatusName = ReadAndAllocStringValue( pSub, "StatusName", pFilename );
		pInfo->m_pBuilderWeaponName = ReadAndAllocStringValue( pSub, "BuilderWeaponName", pFilename );
		pInfo->m_pBuilderPlacementString = ReadAndAllocStringValue( pSub, "BuilderPlacementString", pFilename );
		pInfo->m_bSolidToPlayerMovement = pSub->GetInt( "SolidToPlayerMovement", 0 ) ? true : false;
		pInfo->m_pIconActive = ReadAndAllocStringValue( pSub, "IconActive", pFilename );
		pInfo->m_pIconInactive = ReadAndAllocStringValue( pSub, "IconInactive", pFilename );
		pInfo->m_pIconMenu = ReadAndAllocStringValue( pSub, "IconMenu", pFilename );
		pInfo->m_bUseItemInfo = ( pSub->GetInt( "UseItemInfo", 0 ) > 0 );
		pInfo->m_pViewModel = ReadAndAllocStringValue( pSub, "Viewmodel", pFilename );
		pInfo->m_pPlayerModel = ReadAndAllocStringValue( pSub, "Playermodel", pFilename );
		pInfo->m_iDisplayPriority = pSub->GetInt( "DisplayPriority", 0 );
		pInfo->m_pHudStatusIcon = ReadAndAllocStringValue( pSub, "HudStatusIcon", pFilename );
		pInfo->m_bVisibleInWeaponSelection = ( pSub->GetInt( "VisibleInWeaponSelection", 1 ) > 0 );
		pInfo->m_pExplodeSound = ReadAndAllocStringValue( pSub, "ExplodeSound", pFilename );
		pInfo->m_pUpgradeSound = ReadAndAllocStringValue( pSub, "UpgradeSound", pFilename );
		pInfo->m_pExplosionParticleEffect = ReadAndAllocStringValue( pSub, "ExplodeEffect", pFilename );
		pInfo->m_bAutoSwitchTo = ( pSub->GetInt( "autoswitchto", 0 ) > 0 );
		pInfo->m_iMetalToDropInGibs = pSub->GetInt( "MetalToDropInGibs", 0 );
		pInfo->m_bRequiresOwnBuilder = pSub->GetBool( "RequiresOwnBuilder", 0 );

		// Read the other alternate object modes.
		KeyValues *pAltModesKey = pSub->FindKey( "AltModes" );
		if ( pAltModesKey )
		{
			int iIndex = 0;
			while ( iIndex<OBJECT_MAX_MODES )
			{
				char buf[256];
				Q_snprintf( buf, sizeof(buf), "AltMode%d", iIndex );
				KeyValues *pCurrentModeKey = pAltModesKey->FindKey( buf );
				if ( !pCurrentModeKey )
					break;

				pInfo->m_AltModes[iIndex].pszStatusName = ReadAndAllocStringValue( pCurrentModeKey, "StatusName", pFilename );
				pInfo->m_AltModes[iIndex].pszModeName   = ReadAndAllocStringValue( pCurrentModeKey, "ModeName",   pFilename );
				pInfo->m_AltModes[iIndex].pszIconMenu   = ReadAndAllocStringValue( pCurrentModeKey, "IconMenu",   pFilename );

				iIndex++;
			}
			pInfo->m_iNumAltModes = iIndex-1;
		}

		// Alternate mode 0 always matches the defaults.
		pInfo->m_AltModes[0].pszStatusName = pInfo->m_pStatusName;
		pInfo->m_AltModes[0].pszIconMenu   = pInfo->m_pIconMenu;
	}

	pValues->deleteThis();
}


const CObjectInfo* GetObjectInfo( int iObject )
{
	Assert( iObject >= 0 && iObject < OBJ_LAST );
	Assert( AreObjectInfosLoaded() );
	return &g_ObjectInfos[iObject];
}

ConVar tf_cheapobjects( "tf_cheapobjects","0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Set to 1 and all objects will cost 0" );

//-----------------------------------------------------------------------------
// Purpose: Return the cost of another object of the specified type
//			If bLast is set, return the cost of the last built object of the specified type
// 
// Note: Used to contain logic from tf2 that multiple instances of the same object
//       cost different amounts. See tf2/game_shared/tf_shareddefs.cpp for details
//-----------------------------------------------------------------------------
int InternalCalculateObjectCost( int iObjectType )
{
	if ( tf_cheapobjects.GetInt() )
	{
		return 0;
	}

	int iCost = GetObjectInfo( iObjectType )->m_Cost;

	return iCost;
}

//-----------------------------------------------------------------------------
// Purpose: Calculate the cost to upgrade an object of a specific type
//-----------------------------------------------------------------------------
int	CalculateObjectUpgrade( int iObjectType, int iObjectLevel )
{
	// Max level?
	if ( iObjectLevel >= GetObjectInfo( iObjectType )->m_MaxUpgradeLevel )
		return 0;

	int iCost = GetObjectInfo( iObjectType )->m_UpgradeCost;
	for ( int i = 0; i < (iObjectLevel - 1); i++ )
	{
		iCost *= OBJECT_UPGRADE_COST_MULTIPLIER_PER_LEVEL;
	}

	return iCost;
}

//-----------------------------------------------------------------------------
// Purpose: Return true if the specified class is allowed to build the specified object type
//-----------------------------------------------------------------------------
bool ClassCanBuild( int iClass, int iObjectType )
{
	/*
	for ( int i = 0; i < OBJ_LAST; i++ )
	{
		// Hit the end?
		if ( g_TFClassInfos[iClass].m_pClassObjects[i] == OBJ_LAST )
			return false;

		// Found it?
		if ( g_TFClassInfos[iClass].m_pClassObjects[i] == iObjectType )
			return true;
	}

	return false;
	*/

	return ( iClass == TF_CLASS_ENGINEER );
}

const unsigned char *GetTFEncryptionKey( void )
{ 
	return (unsigned char *)"E2NcUkG2"; 
}

//-----------------------------------------------------------------------------
// Per-class weapon entity translations
//-----------------------------------------------------------------------------
struct wpntranslation_class_weapons_t
{
	const char *pszWpnString;
	const char *pszClassWpn[TF_LAST_NORMAL_CLASS];
};

wpntranslation_class_weapons_t pszWpnEntTranslationList[] = 
{
	{
		"tf_weapon_shotgun",
		{
			"",							// TF_CLASS_UNDEFINED = 0,
			"",							// TF_CLASS_SCOUT,
			"",							// TF_CLASS_SNIPER,
			"tf_weapon_shotgun_soldier",// TF_CLASS_SOLDIER,
			"",							// TF_CLASS_DEMOMAN,
			"",							// TF_CLASS_MEDIC,
			"tf_weapon_shotgun_hwg",	// TF_CLASS_HEAVYWEAPONS,
			"tf_weapon_shotgun_pyro",	// TF_CLASS_PYRO,
			"",							// TF_CLASS_SPY,
			"tf_weapon_shotgun_primary",// TF_CLASS_ENGINEER,		
		}
	},

	{
		"tf_weapon_pistol",
		{
			"",							// TF_CLASS_UNDEFINED = 0,
			"tf_weapon_pistol_scout",	// TF_CLASS_SCOUT,
			"",							// TF_CLASS_SNIPER,
			"",							// TF_CLASS_SOLDIER,
			"",							// TF_CLASS_DEMOMAN,
			"",							// TF_CLASS_MEDIC,
			"",							// TF_CLASS_HEAVYWEAPONS,
			"",							// TF_CLASS_PYRO,
			"",							// TF_CLASS_SPY,
			"tf_weapon_pistol",			// TF_CLASS_ENGINEER,		
		}
	},

	{
		"tf_weapon_shovel",
		{
			"",							// TF_CLASS_UNDEFINED = 0,
			"",							// TF_CLASS_SCOUT,
			"",							// TF_CLASS_SNIPER,
			"tf_weapon_shovel",			// TF_CLASS_SOLDIER,
			"tf_weapon_bottle",			// TF_CLASS_DEMOMAN,
			"",							// TF_CLASS_MEDIC,
			"",							// TF_CLASS_HEAVYWEAPONS,
			"",							// TF_CLASS_PYRO,
			"",							// TF_CLASS_SPY,
			"",							// TF_CLASS_ENGINEER,		
		}
	},
	{
		"tf_weapon_bottle",
		{
			"",							// TF_CLASS_UNDEFINED = 0,
			"",							// TF_CLASS_SCOUT,
			"",							// TF_CLASS_SNIPER,
			"tf_weapon_shovel",			// TF_CLASS_SOLDIER,
			"tf_weapon_bottle",			// TF_CLASS_DEMOMAN,
			"",							// TF_CLASS_MEDIC,
			"",							// TF_CLASS_HEAVYWEAPONS,
			"",							// TF_CLASS_PYRO,
			"",							// TF_CLASS_SPY,
			"",							// TF_CLASS_ENGINEER,		
		}
	},
	{
		"saxxy",
		{
			"",							// TF_CLASS_UNDEFINED = 0,
			"tf_weapon_bat",			// TF_CLASS_SCOUT,
			"tf_weapon_club",			// TF_CLASS_SNIPER,
			"tf_weapon_shovel",			// TF_CLASS_SOLDIER,
			"tf_weapon_bottle",			// TF_CLASS_DEMOMAN,
			"tf_weapon_bonesaw",		// TF_CLASS_MEDIC,
			"tf_weapon_fireaxe",		// TF_CLASS_HEAVYWEAPONS,		HWG uses a fireaxe because he doesn't have a default melee weapon of his own; also I am a terrible person
			"tf_weapon_fireaxe",		// TF_CLASS_PYRO,
			"tf_weapon_knife",			// TF_CLASS_SPY,
			"tf_weapon_wrench",			// TF_CLASS_ENGINEER,		
		}
	},
	{
		"tf_weapon_throwable",
		{
			"",											// TF_CLASS_UNDEFINED = 0,
			"tf_weapon_throwable_secondary",			// TF_CLASS_SCOUT,
			"tf_weapon_throwable_secondary",			// TF_CLASS_SNIPER,
			"tf_weapon_throwable_secondary",			// TF_CLASS_SOLDIER,
			"tf_weapon_throwable_secondary",			// TF_CLASS_DEMOMAN,
			"tf_weapon_throwable_primary",				// TF_CLASS_MEDIC,
			"tf_weapon_throwable_secondary",			// TF_CLASS_HEAVYWEAPONS
			"tf_weapon_throwable_secondary",			// TF_CLASS_PYRO,
			"tf_weapon_throwable_secondary",			// TF_CLASS_SPY,
			"tf_weapon_throwable_secondary",			// TF_CLASS_ENGINEER,		
		}
	},
	{
		"tf_weapon_parachute",
		{
			"",											// TF_CLASS_UNDEFINED = 0,
			"",			// TF_CLASS_SCOUT,
			"",			// TF_CLASS_SNIPER,
			"tf_weapon_parachute_secondary",			// TF_CLASS_SOLDIER,
			"tf_weapon_parachute_primary",				// TF_CLASS_DEMOMAN,
			"",			// TF_CLASS_MEDIC,
			"",			// TF_CLASS_HEAVYWEAPONS
			"",			// TF_CLASS_PYRO,
			""			// TF_CLASS_SPY,
			"",			// TF_CLASS_ENGINEER,		
		}
	},
	{
		"tf_weapon_revolver",
		{
			"",											// TF_CLASS_UNDEFINED = 0,
			"",			// TF_CLASS_SCOUT,
			"",			// TF_CLASS_SNIPER,
			"",			// TF_CLASS_SOLDIER,
			"",				// TF_CLASS_DEMOMAN,
			"",			// TF_CLASS_MEDIC,
			"",			// TF_CLASS_HEAVYWEAPONS
			"",			// TF_CLASS_PYRO,
			"tf_weapon_revolver",				// TF_CLASS_SPY,
			"tf_weapon_revolver_secondary",		// TF_CLASS_ENGINEER,		
		}
	},
};

//-----------------------------------------------------------------------------
// Purpose: We need to support players putting any shotgun into a shotgun slot, pistol into a pistol slot, etc.
//			For legacy reasons, different classes actually spawn different entities for their shotguns/pistols/etc.
//			To deal with this, we translate entities into the right one for the class we're playing.
//-----------------------------------------------------------------------------
const char *TranslateWeaponEntForClass( const char *pszName, int iClass )
{
	if ( pszName )
	{
		for ( int i = 0; i < ARRAYSIZE(pszWpnEntTranslationList); i++ )
		{
			if ( !Q_stricmp( pszName, pszWpnEntTranslationList[i].pszWpnString ) )
			{
				const char *pTransName = pszWpnEntTranslationList[i].pszClassWpn[ iClass ];
				Assert( pTransName && pTransName[0] );
				return pTransName;
			}
		}
	}

	return pszName;
}

//-----------------------------------------------------------------------------
// Helltower Announcer lines for Redmond and Blutarch
//-----------------------------------------------------------------------------
helltower_vo_t g_pszHelltowerAnnouncerLines[] =
{
// EACH MISC PAIR SHOULD HAVE THE SAME NUMBER OF LINES
	{ "Announcer.Helltower_Red_Misc%02u",				16 },
	{ "Announcer.Helltower_Blue_Misc%02u",				16 },

	{ "Announcer.Helltower_Red_Misc_Rare%02u",			21 },
	{ "Announcer.Helltower_Blue_Misc_Rare%02u",			21 },

// THESE PAIRS CAN HAVE DIFFERENT COUNTS
	{ "Announcer.Helltower_Red_Winning%02u",			12 },
	{ "Announcer.Helltower_Blue_Winning%02u",			13 },

	{ "Announcer.Helltower_Red_Winning_Rare%02u",		12 },
	{ "Announcer.Helltower_Blue_Winning_Rare%02u",		8 },

	{ "Announcer.Helltower_Red_Losing%02u",				15 },
	{ "Announcer.Helltower_Blue_Losing%02u",			16 },

	{ "Announcer.Helltower_Red_Losing_Rare%02u",		6 },
	{ "Announcer.Helltower_Blue_Losing_Rare%02u",		5 },

	{ "Announcer.Helltower_Red_Win%02u",				7 },
	{ "Announcer.Helltower_Blue_Win%02u",				7 },

	{ "Announcer.Helltower_Red_Win_Rare%02u",			1 },
	{ "Announcer.Helltower_Blue_Win_Rare%02u",			3 },

	{ "Announcer.Helltower_Red_Lose%02u",				7 },
	{ "Announcer.Helltower_Blue_Lose%02u",				7 },

	{ "Announcer.Helltower_Red_Lose_Rare%02u",			1 },
	{ "Announcer.Helltower_Blue_Lose_Rare%02u",			1 },

	{ "Announcer.Helltower_Red_RoundStart%02u",			4 },
	{ "Announcer.Helltower_Blue_RoundStart%02u",		2 },

	{ "Announcer.Helltower_Red_RoundStart_Rare%02u",	4 },
	{ "Announcer.Helltower_Blue_RoundStart_Rare%02u",	2 },

	{ "Announcer.Helltower_Red_Skeleton_King%02u",		4 },
	{ "Announcer.Helltower_Blue_Skeleton_King%02u",		4 },

	{ "Announcer.Helltower_Red_Almost_Win%02u",		1 },
	{ "Announcer.Helltower_Blue_Almost_Win%02u",		1 },

	{ "Announcer.Helltower_Red_Almost_Lose%02u",		1 },
	{ "Announcer.Helltower_Blue_Almost_Lose%02u",		1 },

};

#ifdef TF_CLIENT_DLL
//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
const char *g_pszInvasionMaps[] =
{
	"maps/ctf_2fort_invasion.bsp",
	"maps/koth_probed.bsp",
	"maps/arena_byre.bsp",
	"maps/pd_watergate.bsp"
};

bool IsPlayingInvasionMap( void )
{
	const char *pszCurrentMap = engine->GetLevelName();

	for ( int i = 0; i < ARRAYSIZE( g_pszInvasionMaps ); i++ )
	{
		if ( FStrEq( g_pszInvasionMaps[i], pszCurrentMap ) )
			return true;
	}

	return false;
}

const char *g_pszClassIcons[SCOREBOARD_CLASS_ICONS] =
{
	"",
	"../hud/leaderboard_class_scout",
	"../hud/leaderboard_class_sniper",
	"../hud/leaderboard_class_soldier",
	"../hud/leaderboard_class_demo",
	"../hud/leaderboard_class_medic",
	"../hud/leaderboard_class_heavy",
	"../hud/leaderboard_class_pyro",
	"../hud/leaderboard_class_spy",
	"../hud/leaderboard_class_engineer",
	"../hud/leaderboard_class_scout_d",
	"../hud/leaderboard_class_sniper_d",
	"../hud/leaderboard_class_soldier_d",
	"../hud/leaderboard_class_demo_d",
	"../hud/leaderboard_class_medic_d",
	"../hud/leaderboard_class_heavy_d",
	"../hud/leaderboard_class_pyro_d",
	"../hud/leaderboard_class_spy_d",
	"../hud/leaderboard_class_engineer_d",
};

const char *g_pszClassIconsAlt[SCOREBOARD_CLASS_ICONS] =
{
	"",
	"class_icons/class_icon_orange_scout",
	"class_icons/class_icon_orange_sniper",
	"class_icons/class_icon_orange_soldier",
	"class_icons/class_icon_orange_demo",
	"class_icons/class_icon_orange_medic",
	"class_icons/class_icon_orange_heavy",
	"class_icons/class_icon_orange_pyro",
	"class_icons/class_icon_orange_spy",
	"class_icons/class_icon_orange_engineer",
	"class_icons/class_icon_orange_scout_d",
	"class_icons/class_icon_orange_sniper_d",
	"class_icons/class_icon_orange_soldier_d",
	"class_icons/class_icon_orange_demo_d",
	"class_icons/class_icon_orange_medic_d",
	"class_icons/class_icon_orange_heavy_d",
	"class_icons/class_icon_orange_pyro_d",
	"class_icons/class_icon_orange_spy_d",
	"class_icons/class_icon_orange_engineer_d",
};

const char *g_pszItemClassImagesRed[] =
{
	"class_portraits/all_class",	// TF_CLASS_UNDEFINED = 0,
	"class_portraits/scout",		// TF_CLASS_SCOUT,			
	"class_portraits/sniper",		// TF_CLASS_SNIPER,
	"class_portraits/soldier",		// TF_CLASS_SOLDIER,
	"class_portraits/demoman",		// TF_CLASS_DEMOMAN,
	"class_portraits/medic",		// TF_CLASS_MEDIC,
	"class_portraits/heavy",		// TF_CLASS_HEAVYWEAPONS,
	"class_portraits/pyro",			// TF_CLASS_PYRO,
	"class_portraits/spy",			// TF_CLASS_SPY,
	"class_portraits/engineer",		// TF_CLASS_ENGINEER,
	"class_portraits/scout_grey",		// TF_CLASS_SCOUT,			
	"class_portraits/sniper_grey",		// TF_CLASS_SNIPER,
	"class_portraits/soldier_grey",		// TF_CLASS_SOLDIER,
	"class_portraits/demoman_grey",		// TF_CLASS_DEMOMAN,
	"class_portraits/medic_grey",		// TF_CLASS_MEDIC,
	"class_portraits/heavy_grey",		// TF_CLASS_HEAVYWEAPONS,
	"class_portraits/pyro_grey",		// TF_CLASS_PYRO,
	"class_portraits/spy_grey",			// TF_CLASS_SPY,
	"class_portraits/engineer_grey",	// TF_CLASS_ENGINEER,
};

const char *g_pszItemClassImagesBlue[] =
{
	"class_portraits/all_class",		// TF_CLASS_UNDEFINED = 0,
	"class_portraits/scout_blue",		// TF_CLASS_SCOUT,			
	"class_portraits/sniper_blue",		// TF_CLASS_SNIPER,
	"class_portraits/soldier_blue",		// TF_CLASS_SOLDIER,
	"class_portraits/demoman_blue",		// TF_CLASS_DEMOMAN,
	"class_portraits/medic_blue",		// TF_CLASS_MEDIC,
	"class_portraits/heavy_blue",		// TF_CLASS_HEAVYWEAPONS,
	"class_portraits/pyro_blue",		// TF_CLASS_PYRO,
	"class_portraits/spy_blue",			// TF_CLASS_SPY,
	"class_portraits/engineer_blue",	// TF_CLASS_ENGINEER,
	"class_portraits/scout_blue_grey",		// TF_CLASS_SCOUT,			
	"class_portraits/sniper_blue_grey",		// TF_CLASS_SNIPER,
	"class_portraits/soldier_blue_grey",	// TF_CLASS_SOLDIER,
	"class_portraits/demoman_blue_grey",	// TF_CLASS_DEMOMAN,
	"class_portraits/medic_blue_grey",		// TF_CLASS_MEDIC,
	"class_portraits/heavy_blue_grey",		// TF_CLASS_HEAVYWEAPONS,
	"class_portraits/pyro_blue_grey",		// TF_CLASS_PYRO,
	"class_portraits/spy_blue_grey",		// TF_CLASS_SPY,
	"class_portraits/engineer_blue_grey",	// TF_CLASS_ENGINEER,
};

const char *g_pszCompetitiveMedalImages[] =
{
	"",
	"competitive/competitive_coin_bronze",
	"competitive/competitive_coin_silver",
	"competitive/competitive_coin_gold",
};
COMPILE_TIME_ASSERT( ARRAYSIZE( g_pszCompetitiveMedalImages ) == StatMedal_Max );

#endif // TF_CLIENT_DLL


// rune icons for each team
static const char *s_pszRuneIcons[2][RUNE_TYPES_MAX] =
{
	// RED TEAM
	{
		"powerup_icon_strength_red",
		"powerup_icon_haste_red",
		"powerup_icon_regen_red",
		"powerup_icon_resist_red",
		"powerup_icon_vampire_red",
		"powerup_icon_reflect_red",
		"powerup_icon_precision_red",
		"powerup_icon_agility_red",
		"powerup_icon_knockout_red",
		"powerup_icon_king_red",
		"powerup_icon_plague_red",
		"powerup_icon_supernova_red",
	},
	// BLUE TEAM
	{
		"powerup_icon_strength_blue",
		"powerup_icon_haste_blue",
		"powerup_icon_regen_blue",
		"powerup_icon_resist_blue",
		"powerup_icon_vampire_blue",
		"powerup_icon_reflect_blue",
		"powerup_icon_precision_blue",
		"powerup_icon_agility_blue",
		"powerup_icon_knockout_blue",
		"powerup_icon_king_blue",
		"powerup_icon_plague_blue",
		"powerup_icon_supernova_blue",
	}
};
COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszRuneIcons[0] ) == RUNE_TYPES_MAX );
COMPILE_TIME_ASSERT( ARRAYSIZE( s_pszRuneIcons[1] ) == RUNE_TYPES_MAX );

const char *GetPowerupIconName( RuneTypes_t type, int iTeam )
{
	int iTeamIndex = iTeam == TF_TEAM_RED ? 0 : 1;
	if ( type != RUNE_NONE && type < RUNE_TYPES_MAX )
	{
		return s_pszRuneIcons[ iTeamIndex ][ type ];
	}

	return NULL;
}