/***
*
*	Copyright (c) 1999, Valve LLC. All rights reserved.
*	
*	This product contains software technology licensed from Id 
*	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
*	All Rights Reserved.
*
*   Use, distribution, and modification of this source code and/or resulting
*   object code is restricted to non-commercial enhancements to products from
*   Valve LLC.  All other use, distribution, or modification is prohibited
*   without written permission from Valve LLC.
*
****/
//
// hmctf_gamerules.cpp
//
#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"player.h"
#include	"weapons.h"
#include	"gamerules.h"
#include	"hmctf_gamerules.h"
#include	"game.h"
#include	"items.h"

static char team_names[MAX_TEAMS][MAX_TEAMNAME_LENGTH];
static int team_scores[MAX_TEAMS];
static int num_teams = 0;

int HMCTF_TEAM1_CAPTURES;
int HMCTF_TEAM2_CAPTURES;

int HMCTF_TEAM1FLAGSTOLEN;
int HMCTF_TEAM2FLAGSTOLEN;

extern DLL_GLOBAL BOOL		g_fGameOver;

CHalfLifeHMCTF :: CHalfLifeHMCTF()
{
	m_DisableDeathMessages = FALSE;
	m_DisableDeathPenalty = FALSE;

	memset( team_names, 0, sizeof(team_names) );
	memset( team_scores, 0, sizeof(team_scores) );
	num_teams = 0;

	// Copy over the team from the server config
	m_szTeamList[0] = 0;

	// Cache this because the team code doesn't want to deal with changing this in the middle of a game
	strncpy( m_szTeamList, teamlist.string, TEAMPLAY_TEAMLISTLENGTH );

	edict_t *pWorld = INDEXENT(0);
	if ( pWorld && pWorld->v.team )
	{
		if ( teamoverride.value )
		{
			const char *pTeamList = STRING(pWorld->v.team);
			if ( pTeamList && strlen(pTeamList) )
			{
				strncpy( m_szTeamList, pTeamList, TEAMPLAY_TEAMLISTLENGTH );
			}
		}
	}
	// Has the server set teams
	if ( strlen( m_szTeamList ) )
		m_teamLimit = TRUE;
	else
		m_teamLimit = FALSE;

	RecountTeams();

	HMCTF_TEAM1_CAPTURES = HMCTF_TEAM2_CAPTURES = 0;
	HMCTF_TEAM1FLAGSTOLEN = HMCTF_TEAM2FLAGSTOLEN = 0;
}

extern int gmsgHMCTFScores;

void CHalfLifeHMCTF :: Think ( void )
{
	///// Check game rules /////

	if ( g_fGameOver )   // someone else quit the game already
	{
		CHalfLifeMultiplay::Think();
		return;
	}

	//Updating the client HUD
	MESSAGE_BEGIN( MSG_ALL, gmsgHMCTFScores );
		WRITE_BYTE( 1 );
		WRITE_BYTE( HMCTF_TEAM1FLAGSTOLEN );
		WRITE_BYTE( HMCTF_TEAM2FLAGSTOLEN );
		WRITE_LONG( HMCTF_TEAM1_CAPTURES );
		WRITE_LONG( HMCTF_TEAM2_CAPTURES );
	MESSAGE_END();

	//Capture limit before everything
	float flCaptureLimit = capturelimit.value;
	if ( flCaptureLimit != 0)
	{
		if (HMCTF_TEAM1_CAPTURES >= flCaptureLimit || HMCTF_TEAM2_CAPTURES >= flCaptureLimit)
		{
			UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "Capture limit hit!\n");
			GoToIntermission();
			return;
		}
	}

	float flTimeLimit = CVAR_GET_FLOAT("mp_timelimit") * 60;
	
	if ( flTimeLimit != 0 && gpGlobals->time >= flTimeLimit )
	{
		GoToIntermission();
		return;
	}

	float flFragLimit = fraglimit.value;
	if ( flFragLimit )
	{
		// check if any team is over the frag limit
		for ( int i = 0; i < num_teams; i++ )
		{
			if ( team_scores[i] >= flFragLimit )
			{
				GoToIntermission();
				return;
			}
		}
	}
}

//=========================================================
// ClientCommand
// the user has typed a command which is unrecognized by everything else;
// this check to see if the gamerules knows anything about the command
//=========================================================
BOOL CHalfLifeHMCTF :: ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
	char szMenu [512];

	if ( FStrEq( pcmd, "menuselect" ) )
	{
		if ( CMD_ARGC() < 2 )
			return TRUE;

		int slot = atoi( CMD_ARGV(1) );

		// select the item from the current menu
		switch(pPlayer->m_nMenu)
		case Menu_ChangeTeam:
		{
			if (slot == 1)
			{
				JoinTeam(pPlayer, HMCTF_TEAM1_NAME);
			}
			else if (slot == 2)
			{
				JoinTeam(pPlayer, HMCTF_TEAM2_NAME);
			}
			else if (slot == 3)
			{
				JoinTeam(pPlayer, TeamWithFewestPlayers());
			}
		}

		return TRUE;
	}
	else if ( FStrEq( pcmd, "changeteam" ) )
	{
		//Make sure he's not dead
		if (!pPlayer->IsAlive()) return TRUE;

		pPlayer->m_nMenu = Menu_ChangeTeam;
		strcpy (szMenu, "Select a team\n\n");
		strcat (szMenu, "1. Blue Team\n");
		strcat (szMenu, "2. Red Team\n\n");
		strcat (szMenu, "3. Autoteam");
		ShowMenu (pPlayer, 0x7, 0, 0, szMenu);

		return TRUE;
	}

	return FALSE;
}

extern int gmsgGameMode;
extern int gmsgSayText;
extern int gmsgTeamInfo;


void CHalfLifeHMCTF :: UpdateGameMode( CBasePlayer *pPlayer )
{
	MESSAGE_BEGIN( MSG_ONE, gmsgGameMode, NULL, pPlayer->edict() );
		WRITE_BYTE( 1 );  // game mode teamplay
	MESSAGE_END();
}


const char *CHalfLifeHMCTF::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
{
	return NULL;
}


//=========================================================
// InitHUD
//=========================================================
void CHalfLifeHMCTF::InitHUD( CBasePlayer *pPlayer )
{
	SetDefaultPlayerTeam( pPlayer );
	CHalfLifeMultiplay::InitHUD( pPlayer );

	RecountTeams();

	char *mdls = g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model" );

	// update this player with all the other players team info
	// loop through all active players and send their team info to the new client
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );
		if ( plr && IsValidTeam( plr->TeamID() ) )
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgTeamInfo, NULL, pPlayer->edict() );
				WRITE_BYTE( plr->entindex() );
				WRITE_STRING( plr->TeamID() );
			MESSAGE_END();
		}
	}

	if ( pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER)
	{
		char szMenu [512];
		pPlayer->m_nMenu = Menu_ChangeTeam;
		strcpy (szMenu, "Select a team\n\n");
		strcat (szMenu, "1. Blue Team\n");
		strcat (szMenu, "2. Red Team\n\n");
		strcat (szMenu, "3. Autoteam");
		ShowMenu (pPlayer, 0x7, 0, 0, szMenu);
	}
}


void CHalfLifeHMCTF::ChangePlayerTeam( CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib )
{
	int damageFlags = DMG_GENERIC;
	int clientIndex = pPlayer->entindex();

	if ( !bGib )
	{
		damageFlags |= DMG_NEVERGIB;
	}
	else
	{
		damageFlags |= DMG_ALWAYSGIB;
	}

	if ( bKill )
	{
		// kill the player,  remove a death,  and let them start on the new team
		m_DisableDeathMessages = TRUE;
		m_DisableDeathPenalty = TRUE;

		entvars_t *pevWorld = VARS( INDEXENT(0) );
		pPlayer->TakeDamage( pevWorld, pevWorld, 900, damageFlags );

		m_DisableDeathMessages = FALSE;
		m_DisableDeathPenalty = FALSE;
	}

	// copy out the team name from the model
	strncpy( pPlayer->m_szTeamName, pTeamName, TEAM_NAME_LENGTH );

	g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", pPlayer->m_szTeamName );
	g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "team", pPlayer->m_szTeamName );

	// notify everyone's HUD of the team change
	MESSAGE_BEGIN( MSG_ALL, gmsgTeamInfo );
		WRITE_BYTE( clientIndex );
		WRITE_STRING( pPlayer->m_szTeamName );
	MESSAGE_END();
}


//=========================================================
// ClientUserInfoChanged
//=========================================================
void CHalfLifeHMCTF::ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer )
{
	// an observer can change a lot, but isn't visible so hasn't got to do much here
	if ( pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER)
		return;

	// prevent skin/color/model changes
	char *mdls = g_engfuncs.pfnInfoKeyValue( infobuffer, "model" );

	//if the model name and the teamname are the same then return
	if ( !stricmp( mdls, pPlayer->m_szTeamName ) )
		return;

	//else make sure the player model is the same as the teamname
	g_engfuncs.pfnSetClientKeyValue( pPlayer->entindex(), g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", pPlayer->m_szTeamName );
}

extern int gmsgDeathMsg;

//=========================================================
// Deathnotice. 
//=========================================================
void CHalfLifeHMCTF::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor )
{
	if ( m_DisableDeathMessages )
		return;
	
	if ( pVictim && pKiller && pKiller->flags & FL_CLIENT )
	{
		CBasePlayer *pk = (CBasePlayer*) CBaseEntity::Instance( pKiller );

		if ( pk )
		{
			if ( (pk != pVictim) && (PlayerRelationship( pVictim, pk ) == GR_TEAMMATE) )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgDeathMsg );
					WRITE_BYTE( ENTINDEX(ENT(pKiller)) );		// the killer
					WRITE_BYTE( ENTINDEX(pVictim->edict()) );	// the victim
					WRITE_STRING( "teammate" );		// flag this as a teammate kill
				MESSAGE_END();
				return;
			}
		}
	}

	CHalfLifeMultiplay::DeathNotice( pVictim, pKiller, pevInflictor );
}

//=========================================================
//=========================================================
void CHalfLifeHMCTF :: PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
	if ( !m_DisableDeathPenalty )
	{
		CHalfLifeMultiplay::PlayerKilled( pVictim, pKiller, pInflictor );
		RecountTeams();
	}
}


//=========================================================
// IsTeamplay
//=========================================================
BOOL CHalfLifeHMCTF::IsTeamplay( void )
{
	return TRUE;
}

BOOL CHalfLifeHMCTF::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
{
	if ( pAttacker && PlayerRelationship( pPlayer, pAttacker ) == GR_TEAMMATE )
	{
		// my teammate hit me.
		if ( (CVAR_GET_FLOAT("mp_friendlyfire") == 0) && (pAttacker != pPlayer) )
		{
			// friendly fire is off, and this hit came from someone other than myself,  then don't get hurt
			return FALSE;
		}
	}

	return CHalfLifeMultiplay::FPlayerCanTakeDamage( pPlayer, pAttacker );
}

//=========================================================
//=========================================================
int CHalfLifeHMCTF::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
{
	// half life multiplay has a simple concept of Player Relationships.
	// you are either on another player's team, or you are not.
	if ( !pPlayer || !pTarget || !pTarget->IsPlayer() )
		return GR_NOTTEAMMATE;

	if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
	{
		return GR_TEAMMATE;
	}

	return GR_NOTTEAMMATE;
}

//=========================================================
//=========================================================
BOOL CHalfLifeHMCTF::ShouldAutoAim( CBasePlayer *pPlayer, edict_t *target )
{
	// always autoaim, unless target is a teammate
	CBaseEntity *pTgt = CBaseEntity::Instance( target );
	if ( pTgt && pTgt->IsPlayer() )
	{
		if ( PlayerRelationship( pPlayer, pTgt ) == GR_TEAMMATE )
			return FALSE; // don't autoaim at teammates
	}

	return CHalfLifeMultiplay::ShouldAutoAim( pPlayer, target );
}

//=========================================================
//=========================================================
int CHalfLifeHMCTF::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
{
	if ( !pKilled )
		return 0;

	if ( !pAttacker )
		return 1;

	if ( pAttacker != pKilled && PlayerRelationship( pAttacker, pKilled ) == GR_TEAMMATE )
		return -1;

	return 1;
}

//=========================================================
//=========================================================
const char *CHalfLifeHMCTF::GetTeamID( CBaseEntity *pEntity )
{
	if ( pEntity == NULL || pEntity->pev == NULL )
		return "";

	// return their team name
	return pEntity->TeamID();
}


int CHalfLifeHMCTF::GetTeamIndex( const char *pTeamName )
{
	if ( pTeamName && *pTeamName != 0 )
	{
		// try to find existing team
		for ( int tm = 0; tm < num_teams; tm++ )
		{
			if ( !stricmp( team_names[tm], pTeamName ) )
				return tm;
		}
	}
	
	return -1;	// No match
}


const char *CHalfLifeHMCTF::GetIndexedTeamName( int teamIndex )
{
	if ( teamIndex < 0 || teamIndex >= num_teams )
		return "";

	return team_names[ teamIndex ];
}


BOOL CHalfLifeHMCTF::IsValidTeam( const char *pTeamName ) 
{
	if ( !m_teamLimit )	// Any team is valid if the teamlist isn't set
		return TRUE;

	return ( GetTeamIndex( pTeamName ) != -1 ) ? TRUE : FALSE;
}

const char *CHalfLifeHMCTF::TeamWithFewestPlayers( void )
{
	int i;
	int minPlayers = MAX_TEAMS;
	int teamCount[ MAX_TEAMS ];
	char *pTeamName = NULL;

	//check if the hmctf teams exist. If a team doesn't exist it is the team with the fewest players
	if (GetTeamIndex(HMCTF_TEAM1_NAME) == -1)
		return HMCTF_TEAM1_NAME;
	else if (GetTeamIndex(HMCTF_TEAM2_NAME) == -1)
		return HMCTF_TEAM2_NAME;

	memset( teamCount, 0, MAX_TEAMS * sizeof(int) );
	
	// loop through all clients, count number of players on each team
	for ( i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );

		if ( plr )
		{
			int team = GetTeamIndex( plr->TeamID() );
			if ( team >= 0 )
				teamCount[team] ++;
		}
	}

	// Find team with least players
	for ( i = 0; i < num_teams; i++ )
	{
		if ( teamCount[i] < minPlayers )
		{
			minPlayers = teamCount[i];
			pTeamName = team_names[i];
		}
	}

	return pTeamName;
}


//=========================================================
//=========================================================
void CHalfLifeHMCTF::RecountTeams( void )
{
	char	*pName;
	char	teamlist[TEAMPLAY_TEAMLISTLENGTH];

	// loop through all teams, recounting everything
	num_teams = 0;

	// Copy all of the teams from the teamlist
	// make a copy because strtok is destructive
	strcpy( teamlist, m_szTeamList );
	pName = teamlist;
	pName = strtok( pName, ";" );
	while ( pName != NULL && *pName )
	{
		if ( GetTeamIndex( pName ) < 0 )
		{
			strcpy( team_names[num_teams], pName );
			num_teams++;
		}
		pName = strtok( NULL, ";" );
	}

	if ( num_teams < 2 )
	{
		num_teams = 0;
		m_teamLimit = FALSE;
	}

	// Sanity check
	memset( team_scores, 0, sizeof(team_scores) );

	// loop through all clients
	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );

		if ( plr )
		{
			const char *pTeamName = plr->TeamID();
			// try add to existing team
			int tm = GetTeamIndex( pTeamName );
			
			if ( tm < 0 ) // no team match found
			{ 
				if ( !m_teamLimit )
				{
					// add to new team
					tm = num_teams;
					num_teams++;
					team_scores[tm] = 0;
					strncpy( team_names[tm], pTeamName, MAX_TEAMNAME_LENGTH );
				}
			}

			if ( tm >= 0 )
			{
				team_scores[tm] += plr->pev->frags;
			}
		}
	}
}

void CHalfLifeHMCTF::JoinTeam(CBasePlayer *pPlayer, const char *pTeamName)
{
	char text [1024];

	//If already on this team, do nothing
	if (FStrEq(pPlayer->m_szTeamName, pTeamName))
	{
		return;
	}
	//Join the team

	//When observer, make it respawnable, let it join a team and then respawn
	if ( pPlayer->m_afPhysicsFlags & PFLAG_OBSERVER)
	{
		// notify everyone of the team change
		sprintf( text, "* %s has joined the %s team\n", STRING(pPlayer->pev->netname), pTeamName );
		UTIL_SayText( text, pPlayer );
		UTIL_LogPrintf( "\"%s<%i>\" joined the %s team\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ), pTeamName );
		pPlayer->pev->deadflag = DEAD_RESPAWNABLE;
		ChangePlayerTeam( pPlayer, pTeamName, FALSE, FALSE );
		pPlayer->hmctf_state = HMCTF_STATE_START;
		// recound stuff
		RecountTeams();
		pPlayer->Spawn();
	}
	//Else just switch teams
	else
	{
		// notify everyone of the team change
		sprintf( text, "* %s has changed to the %s team\n", STRING(pPlayer->pev->netname), pTeamName );
		UTIL_SayText( text, pPlayer );
		UTIL_LogPrintf( "\"%s<%i>\" changed to the %s team\n", STRING( pPlayer->pev->netname ), GETPLAYERUSERID( pPlayer->edict() ), pTeamName );
		ChangePlayerTeam( pPlayer, pTeamName, TRUE, TRUE );
		pPlayer->hmctf_state = HMCTF_STATE_START;
		// recound stuff
		RecountTeams();
	}
}


//=========================================================
// Menustuff
//=========================================================

extern int gmsgShowMenu; // the message # for ShowMenu

void CHalfLifeHMCTF::ShowMenu (CBasePlayer *pPlayer, int bitsValidSlots, int nDisplayTime, BOOL fNeedMore, char *pszText)
{
	MESSAGE_BEGIN( MSG_ONE, gmsgShowMenu, NULL, pPlayer ->pev);
		WRITE_SHORT( bitsValidSlots);
		WRITE_CHAR( nDisplayTime );
		WRITE_BYTE( fNeedMore );
		WRITE_STRING (pszText);
	MESSAGE_END();
}

//=========================================================
// Flags
//=========================================================

extern int gmsgItemPickup;

enum Flag_Animations 
{ 
ON_GROUND = 0, 
NOT_CARRIED,
CARRIED,
WAVE_IDLE,
FLAG_POSITION
};

void CItemFlag::Spawn ( void )
{
	Precache( );
	SET_MODEL(ENT(pev), "models/flag.mdl");
		
	pev->movetype = MOVETYPE_TOSS;
	pev->solid = SOLID_TRIGGER;
	UTIL_SetOrigin( pev, pev->origin );
	UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));

	SetThink( Think );
	SetTouch( FlagTouch );

	pev->nextthink = gpGlobals->time + 0.1;
		
	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
		pev->skin = 1;
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
		pev->skin = 2;

	Dropped = FALSE;

	pev->sequence = NOT_CARRIED;
	pev->framerate = 1.0;

	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
	{
		pev->rendercolor.x = 0;
		pev->rendercolor.y = 0;
		pev->rendercolor.z = 128;			
	}
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
	{
		pev->rendercolor.x = 128;
		pev->rendercolor.y = 0;
		pev->rendercolor.z = 0;
	}
	pev->renderamt = 50;
	pev->renderfx = kRenderFxGlowShell;
}

void CItemFlag::Precache( void )
{
	PRECACHE_MODEL ("models/flag.mdl");
	PRECACHE_SOUND ("hmctf/stolen.wav");
	PRECACHE_SOUND ("hmctf/capture.wav");
}

void CItemFlag::Capture(CBasePlayer *pPlayer, const char *pTeamName)
{
	char szText[201];

	sprintf(szText, "%s captured the %s flag!", STRING(pPlayer->pev->netname), pTeamName);

	UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );

	EMIT_SOUND_DYN( ENT(pev), CHAN_STATIC, "hmctf/capture.wav", 1, ATTN_NONE, 0, 100 );

	//Give the player the points
	pPlayer->AddPoints(HMCTF_CAPTUREPOINTS, TRUE);
	pPlayer->AddPointsToTeam(HMCTF_TEAMCAPTUREPOINTS, TRUE);

	//And give the team a capture
	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
		HMCTF_TEAM2_CAPTURES++;
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
		HMCTF_TEAM1_CAPTURES++;

	ResetFlag( pTeamName );
}

void CItemFlag::ResetFlag(const char *pTeamName)
{
	edict_t *pFind;
	
	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
	{
		pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "item_flag_team1" );
		HMCTF_TEAM1FLAGSTOLEN = 0;
	}
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
	{
		pFind = FIND_ENTITY_BY_CLASSNAME( NULL, "item_flag_team2" );
		HMCTF_TEAM2FLAGSTOLEN = 0;
	}
	else return;

	while ( !FNullEnt( pFind ) )
	{
		CBaseEntity *pEnt = CBaseEntity::Instance( pFind );
		CItemFlag *pFlag = (CItemFlag *)pEnt;

		pFlag->Materialize( );

		if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
			pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "item_flag_team1" );
		else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
			pFind = FIND_ENTITY_BY_CLASSNAME( pFind, "item_flag_team2" );
	}
}

void CItemFlag::FlagTouch( CBaseEntity *pOther )
{
	// if it's not a player, ignore
	if ( !pOther->IsPlayer() )
	{
		return;
	}

	CBasePlayer *pPlayer = (CBasePlayer *)pOther;

	if (MyTouch( pPlayer ))
	{
		SUB_UseTargets( pOther, USE_TOGGLE, 0 );
		SetTouch( NULL );
		SetThink( NULL );
		
		//if it's a dropped flag, remove. Else make it invisible
		if (Dropped)
			UTIL_Remove( this );
		else
			pev->effects |= EF_NODRAW;
	}
}

void CItemFlag::Materialize( void )
{
	if ( pev->effects & EF_NODRAW )
	{
		// changing from invisible state to visible.
		EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
		pev->effects &= ~EF_NODRAW;
		pev->effects |= EF_MUZZLEFLASH;
	}

	SetTouch( FlagTouch );
	SetThink( Think );
}

BOOL CItemFlag::MyTouch( CBasePlayer *pPlayer )
{
	char szText[201];

	// Can only carry one flag and can not pickup own flag
	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
	{
		//if client has other teams flag and it isn't a dropped flag, then there is a capture!
		if ( pPlayer->m_fFlagTeam2 && !Dropped)
		{
			pPlayer->m_fFlagTeam2 = FALSE;
			
			Capture(pPlayer, HMCTF_TEAM2_NAME);

			return FALSE;
		}
		else if ( pPlayer->m_fFlagTeam1 )
		{
			return FALSE;
		}
		else if (FStrEq(pPlayer->m_szTeamName, HMCTF_TEAM1_NAME))
		{
			//if dropped, return flag
			if (Dropped)
			{
				ResetFlag(HMCTF_TEAM1_NAME);
				sprintf(szText, "%s returned the %s flag!", STRING(pPlayer->pev->netname), pTeamName);
				UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
				UTIL_Remove( this );
			}
			//but don't pick it up!
			return FALSE;
		}
	}
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
	{
		//if client has other teams flag and it isn't a dropped flag, then there is a capture!
		if ( pPlayer->m_fFlagTeam1 && !Dropped)
		{
			pPlayer->m_fFlagTeam1 = FALSE;

			Capture(pPlayer, HMCTF_TEAM1_NAME);

			return FALSE;
		}
		else if ( pPlayer->m_fFlagTeam2 )
		{
			return FALSE;
		}
		else if (FStrEq(pPlayer->m_szTeamName, HMCTF_TEAM2_NAME))
		{
			//if dropped, return flag
			if (Dropped)
			{
				ResetFlag(HMCTF_TEAM2_NAME);
				sprintf(szText, "%s returned the %s flag!", STRING(pPlayer->pev->netname), pTeamName);
				UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
				UTIL_Remove( this );
			}
			//but don't pick it up!
			return FALSE;
		}
	}

	if ( ( pPlayer->pev->weapons & (1<<WEAPON_SUIT) ) )
	{
		if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
		{
			pPlayer->m_fFlagTeam1 = TRUE;
			// player is now carrying the flag of team1, so give him the flag
			CBaseEntity *pEnt = CBaseEntity::Create( "carried_flag_team1", pev->origin, pev->angles, pPlayer->edict() );
			CCarriedFlag *pCarriedFlag = (CCarriedFlag *)pEnt;
			pCarriedFlag->Owner = pPlayer;
		}
		else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
		{
			pPlayer->m_fFlagTeam2 = TRUE;
			// player is now carrying the flag of team2, so give him the flag
			CBaseEntity *pEnt = CBaseEntity::Create( "carried_flag_team2", pev->origin, pev->angles, pPlayer->edict() );
			CCarriedFlag *pCarriedFlag = (CCarriedFlag *)pEnt;
			pCarriedFlag->Owner = pPlayer;
		}
		MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
			WRITE_STRING( STRING(pev->classname) );
		MESSAGE_END();

		//Let all players hear and read that the flag is gone
		char szText[201];

		if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
			sprintf(szText, "%s got the %s flag!\n", STRING(pPlayer->pev->netname), HMCTF_TEAM1_NAME);
		else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
			sprintf(szText, "%s got the %s flag!\n", STRING(pPlayer->pev->netname), HMCTF_TEAM2_NAME);

		UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );

		//if not dropped, play sound
		if (!Dropped)
		{
			EMIT_SOUND_DYN( ENT(pev), CHAN_STATIC, "hmctf/stolen.wav", 1, ATTN_NORM, 0, 100 );
			if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
				HMCTF_TEAM1FLAGSTOLEN = 1;
			else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
				HMCTF_TEAM2FLAGSTOLEN = 1;
		}

		return TRUE;		
	}
	return FALSE;
}

void CItemFlag::Think( void )
{
	if (Dropped && NextReset <= gpGlobals->time)
	{
		//Let all players know that the flag has been returned
		char szText[201];

		if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
		{
			sprintf(szText, "The %s flag has returned.\n", HMCTF_TEAM1_NAME);
			ResetFlag(HMCTF_TEAM1_NAME);
		}
		else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
		{
			sprintf(szText, "The %s flag has returned.\n", HMCTF_TEAM2_NAME);
			ResetFlag(HMCTF_TEAM2_NAME);
		}

		UTIL_ClientPrintAll( HUD_PRINTCENTER, szText );
		UTIL_Remove( this );
		return;
	}
	pev->frame += pev->framerate;
	if (pev->frame < 0.0 || pev->frame >= 256.0) 
	{
		pev->frame -= (int)(pev->frame / 256.0) * 256.0;
	}
	pev->nextthink = gpGlobals->time + 0.1;
}

class CItemFlagTeam1 : public CItemFlag
{
	void Spawn( void )
	{
		pev->classname = MAKE_STRING("item_flag_team1"); //CCTF map compatibility hack
		strcpy( pTeamName, HMCTF_TEAM1_NAME );

		CItemFlag::Spawn( );
	}
};

LINK_ENTITY_TO_CLASS( item_flag_team1, CItemFlagTeam1 );
LINK_ENTITY_TO_CLASS( ctf_blueflag, CItemFlagTeam1 ); //CCTF map compatibility hack

class CItemFlagTeam2 : public CItemFlag
{
	void Spawn( void )
	{
		pev->classname = MAKE_STRING("item_flag_team2"); //CCTF map compatibility hack
		strcpy( pTeamName, HMCTF_TEAM2_NAME );

		CItemFlag::Spawn( );
	}
};

LINK_ENTITY_TO_CLASS( item_flag_team2, CItemFlagTeam2 );
LINK_ENTITY_TO_CLASS( ctf_redflag, CItemFlagTeam2 ); //CCTF map compatibility hack

//=========================================================
// Carried Flag
//
// This is a complete new entity because it doesn't behave
// as a flag. It just sits on the back of the player and
// removes itself at the right time. (When player is gone
// or death or lost the flag.)
//=========================================================
void CCarriedFlag ::Spawn( )
{
	Precache( );

	SET_MODEL(ENT(pev), "models/flag.mdl");
	UTIL_SetOrigin( pev, pev->origin ); 

	pev->movetype = MOVETYPE_NONE; 
	pev->solid = SOLID_NOT;

	//HACKHACKHACK: Overcome the attachment with no owner yet model "hop" by making it invisible
	pev->effects |= EF_NODRAW;

	pev->sequence = WAVE_IDLE;
	pev->framerate = 1.0;

	if (FStrEq(HMCTF_TEAM1_NAME, pTeamName))
		pev->skin = 1;
	else if (FStrEq(HMCTF_TEAM2_NAME, pTeamName))
		pev->skin = 2;

	SetThink( Think );
	pev->nextthink = gpGlobals->time + 0.1;
}

void CCarriedFlag::Precache( )
{
	PRECACHE_MODEL ("models/flag.mdl");
}

void CCarriedFlag::Think( )
{
	//Make it visible
	pev->effects &= ~EF_NODRAW;

	//And let if follow
	pev->aiment = ENT(Owner->pev);
	pev->movetype = MOVETYPE_FOLLOW;

	//Remove if owner is death
	if (!Owner->IsAlive())
		UTIL_Remove( this );

	//If owner lost flag, remove
	if ( !Owner->m_fFlagTeam1 && !Owner->m_fFlagTeam2)
	{
		UTIL_Remove( this );
	}
	else
	{
		//If owners speed is low, go in idle mode
		if (Owner->pev->velocity.Length() <= 75 && pev->sequence != WAVE_IDLE)
		{
			pev->sequence = WAVE_IDLE;
		}
		//Else let the flag go wild
		else if (Owner->pev->velocity.Length() >= 75 && pev->sequence != CARRIED)
		{
			pev->sequence = CARRIED;
		}
		pev->frame += pev->framerate;
		if (pev->frame < 0.0 || pev->frame >= 256.0) 
		{
			pev->frame -= (int)(pev->frame / 256.0) * 256.0;
		}
		pev->nextthink = gpGlobals->time + 0.1;
	}
}

class CCarriedFlagTeam1 : public CCarriedFlag
{
	void Spawn( void )
	{
		strcpy( pTeamName, HMCTF_TEAM1_NAME );

		CCarriedFlag::Spawn( );
	}
};

LINK_ENTITY_TO_CLASS( carried_flag_team1, CCarriedFlagTeam1 );

class CCarriedFlagTeam2 : public CCarriedFlag
{
	void Spawn( void )
	{
		strcpy( pTeamName, HMCTF_TEAM2_NAME );

		CCarriedFlag::Spawn( );
	}
};

LINK_ENTITY_TO_CLASS( carried_flag_team2, CCarriedFlagTeam2 );

//=========================================================
// Map detection
//=========================================================

class CInfoHMCTFDetect : public CBaseEntity
{
	void Spawn( void )
	{
		UTIL_SetOrigin( pev, pev->origin );
		pev->solid = SOLID_NOT;
		pev->effects = EF_NODRAW;		
	}
};

LINK_ENTITY_TO_CLASS( info_hmctfdetect, CInfoHMCTFDetect );