/***
*
*	Copyright (c) 2001, 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.
*
****/
/*

===== threewave_gamerules.cpp ========================================================

  This contains all the gamerules for the ThreeWave CTF Gamemode.
  It also contains the Flag entity information.

*/

#ifdef THREEWAVE

#define NUM_TEAMS 2

char *sTeamNames[] =
{
	"SPECTATOR",
	"RED",
	"BLUE",
};

#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"player.h"
#include	"weapons.h"
#include	"gamerules.h"
#include	"skill.h"
#include	"game.h"
#include	"items.h"
#include	"threewave_gamerules.h"

extern int gmsgCTFMsgs;
extern int gmsgShowMenu;
extern int gmsgFlagStatus;
extern int gmsgRuneStatus;
extern int gmsgFlagCarrier;
extern int gmsgScoreInfo;

extern unsigned short g_usHook;	
extern unsigned short g_usCable;
extern unsigned short g_usCarried;
extern unsigned short g_usFlagSpawn;


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

bool g_bSpawnedRunes;
void SpawnRunes( void );

extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer, bool bCheckDM );
extern edict_t *RuneSelectSpawnPoint( void );

// Standard Scoring
#define TEAM_CAPTURE_CAPTURE_BONUS 5 // what you get for capture
#define TEAM_CAPTURE_TEAM_BONUS  10 // what your team gets for capture
#define TEAM_CAPTURE_RECOVERY_BONUS  1 // what you get for recovery
#define TEAM_CAPTURE_FLAG_BONUS  0 // what you get for picking up enemy flag
#define TEAM_CAPTURE_FRAG_CARRIER_BONUS  2 // what you get for fragging a enemy flag carrier
#define TEAM_CAPTURE_FLAG_RETURN_TIME 40 // seconds until auto return

// bonuses
#define TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS  2 // bonus for fraggin someone
// who has recently hurt your flag carrier
#define TEAM_CAPTURE_CARRIER_PROTECT_BONUS 1 // bonus for fraggin someone while
// either you or your target are near your flag carrier
#define TEAM_CAPTURE_FLAG_DEFENSE_BONUS 1 // bonus for fraggin someone while
// either you or your target are near your flag
#define TEAM_CAPTURE_RETURN_FLAG_ASSIST_BONUS 1 // awarded for returning a flag that causes a
// capture to happen almost immediately
#define TEAM_CAPTURE_FRAG_CARRIER_ASSIST_BONUS 2 // award for fragging a flag carrier if a
// capture happens almost immediately

// Radius
#define TEAM_CAPTURE_TARGET_PROTECT_RADIUS 550 // the radius around an object being
// defended where a target will be worth extra frags
#define TEAM_CAPTURE_ATTACKER_PROTECT_RADIUS 550 // the radius around an object being
// defended where an attacker will get extra frags when making kills

// timeouts
#define TEAM_CAPTURE_CARRIER_DANGER_PROTECT_TIMEOUT 4
#define TEAM_CAPTURE_CARRIER_FLAG_SINCE_TIMEOUT 2
#define TEAM_CAPTURE_FRAG_CARRIER_ASSIST_TIMEOUT 6
#define TEAM_CAPTURE_RETURN_FLAG_ASSIST_TIMEOUT 4



class CThreeWaveGameMgrHelper : public IVoiceGameMgrHelper
{
public:
	virtual bool		CanPlayerHearPlayer(CBasePlayer *pPlayer1, CBasePlayer *pPlayer2)
	{
		return stricmp(pPlayer1->TeamID(), pPlayer2->TeamID()) == 0;
	}
};
static CThreeWaveGameMgrHelper g_GameMgrHelper;



extern DLL_GLOBAL BOOL		g_fGameOver;

char* GetTeamName( int team )
{
	if ( team < 0 || team > NUM_TEAMS )
		team = 0;

	return sTeamNames[ team ];
}

CThreeWave :: CThreeWave()
{
	// CHalfLifeMultiplay already initialized it - just override its helper callback.
	m_VoiceGameMgr.SetHelper(&g_GameMgrHelper);

	m_DisableDeathMessages = FALSE;
	m_DisableDeathPenalty = FALSE;

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

	iBlueTeamScore = iRedTeamScore = 0;
	g_bSpawnedRunes = FALSE;

	// 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();
}


BOOL CThreeWave::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] )
{
	return CHalfLifeMultiplay::ClientConnected(pEntity, pszName, pszAddress, szRejectReason);
}


extern cvar_t timeleft, fragsleft;

void CThreeWave :: Think ( void )
{
	m_VoiceGameMgr.Update(gpGlobals->frametime);

	///// Check game rules /////
	static int last_frags;
	static int last_time;

	int frags_remaining = 0;
	int time_remaining = 0;

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

	float flTimeLimit = CVAR_GET_FLOAT("mp_timelimit") * 60;
	
	time_remaining = (int)(flTimeLimit ? ( flTimeLimit - gpGlobals->time ) : 0);

	if ( flTimeLimit != 0 && gpGlobals->time >= flTimeLimit )
	{
		GoToIntermission();
		return;
	}

	float flFragLimit = fraglimit.value;
	if ( flFragLimit )
	{
		int bestfrags = 9999;
		int remain;

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

			remain = flFragLimit - team_scores[i];
			if ( remain < bestfrags )
			{
				bestfrags = remain;
			}
		}
		frags_remaining = bestfrags;
	}

	if ( !g_bSpawnedRunes )
		SpawnRunes();

	if ( m_flFlagStatusTime && m_flFlagStatusTime <= gpGlobals->time )
         GetFlagStatus( NULL );
	
	// Updates when frags change
	if ( frags_remaining != last_frags )
	{
		g_engfuncs.pfnCvar_DirectSet( &fragsleft, UTIL_VarArgs( "%i", frags_remaining ) );
	}

	// Updates once per second
	if ( timeleft.value != last_time )
	{
		g_engfuncs.pfnCvar_DirectSet( &timeleft, UTIL_VarArgs( "%i", time_remaining ) );
	}

	last_frags = frags_remaining;
	last_time  = time_remaining;
}

void CThreeWave :: JoinTeam ( CBasePlayer *pPlayer, int iTeam )
{
	if ( pPlayer->pev->team == iTeam )
		return;

	if ( pPlayer->m_flNextTeamChange > gpGlobals->time )
		return;

	pPlayer->m_flNextTeamChange = gpGlobals->time + 5;

	if ( pPlayer->pev->team == 0 )
	{
		ChangePlayerTeam( pPlayer, iTeam );
		RecountTeams();

		pPlayer->Spawn();
	}
	else 
	{
		ChangePlayerTeam( pPlayer, iTeam );
        RecountTeams();
	}
}

int CThreeWave::TeamWithFewestPlayers( void )
{

	CBaseEntity *pPlayer = NULL;
	CBasePlayer *player = NULL;

	int iNumRed, iNumBlue;
	
	int iTeam;

	// Initialize the player counts..
	iNumRed = iNumBlue = 0;
	
	pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );

	while (	(pPlayer != NULL) && (!FNullEnt(pPlayer->edict()))	)
	{
		if (pPlayer->pev->flags != FL_DORMANT)
		{
			player = GetClassPtr((CBasePlayer *)pPlayer->pev);

			
				if ( player->pev->team == RED )
					iNumRed += 1;
	
				else if ( player->pev->team == BLUE )
					iNumBlue += 1;
		
		}
		pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
	}

	if ( iNumRed == iNumBlue )
	{
		switch ( RANDOM_LONG( 0, 1 ) )
		{
			case 0: iTeam = RED; break;
			case 1: iTeam = BLUE; break;
		}
	}
	else if ( iNumRed == 0 && iNumBlue == 0)
	{
		switch ( RANDOM_LONG( 0, 1 ) )
		{
			case 0: iTeam = RED; break;
			case 1: iTeam = BLUE; break;
		}
	}

	else if ( iNumRed > iNumBlue )
		iTeam = BLUE;

	else if ( iNumRed < iNumBlue )
		iTeam = RED;

	return iTeam;

}

void DropRune ( CBasePlayer *pPlayer );
//=========================================================
// 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 CThreeWave :: ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
{
	if( m_VoiceGameMgr.ClientCommand( pPlayer, pcmd ) )
		return TRUE;

	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_iMenu )
		{
			case Team_Menu:
				
				switch ( slot )
				{
				case 1: 
					JoinTeam( pPlayer, RED );
					break;
				case 2:
					JoinTeam( pPlayer, BLUE );
					break;
				case 5:
					JoinTeam( pPlayer, TeamWithFewestPlayers() );
					break;
				}

				break;

			case Team_Menu_IG:

				switch ( slot )
				{
				case 1: 
					JoinTeam( pPlayer, RED );
					break;
				case 2:
					JoinTeam( pPlayer, BLUE );
					break;
				case 5:
					JoinTeam( pPlayer, TeamWithFewestPlayers() );
					break;
				default:
					return TRUE;
				}

				break;
		}
 
		return TRUE;
	}
	else if ( FStrEq( pcmd, "droprune" ) )
	{
		DropRune( pPlayer );

		return TRUE;
	}
	else if ( FStrEq( pcmd, "changeteam" ) )
	{
		if ( pPlayer->pev->team != 0 )
		{
			 pPlayer->ShowMenu( 1 + 2 + 16 + 512, -1, FALSE, "#Team_Menu_Join_IG" );
			 pPlayer->m_iMenu = Team_Menu_IG;
		}

		return TRUE;
	}

	return FALSE;
}

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

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

edict_t *CThreeWave::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
{
	edict_t *pentSpawnSpot;

	if ( FBitSet( pPlayer->m_afPhysicsFlags, PFLAG_OBSERVER ) || pPlayer->pev->team == 0 )
	     pentSpawnSpot = EntSelectSpawnPoint( pPlayer, FALSE );
	else
	{
		if ( RANDOM_LONG ( 1, 7 ) < 3 )
			pentSpawnSpot= EntSelectSpawnPoint( pPlayer, TRUE );	
		else
			pentSpawnSpot= EntSelectSpawnPoint( pPlayer, FALSE );	
	}

	if ( IsMultiplayer() && pentSpawnSpot->v.target )
	{
		FireTargets( STRING(pentSpawnSpot->v.target), pPlayer, pPlayer, USE_TOGGLE, 0 );
	}

	pPlayer->pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1);
	pPlayer->pev->v_angle  = g_vecZero;
	pPlayer->pev->velocity = g_vecZero;
	pPlayer->pev->angles = VARS(pentSpawnSpot)->angles;
	pPlayer->pev->punchangle = g_vecZero;
	pPlayer->pev->fixangle = TRUE;

	return pentSpawnSpot;
}

void CThreeWave :: PlayerTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
{
	if ( !pAttacker->IsPlayer() )
		return;

	if ( pPlayer->pev->team == pAttacker->pev->team )
		return;

	if ( pPlayer->m_bHasFlag )
	{
		pPlayer->pCarrierHurter = (CBasePlayer *)pAttacker;
		pPlayer->m_flCarrierHurtTime = gpGlobals->time + TEAM_CAPTURE_CARRIER_DANGER_PROTECT_TIMEOUT;
	}

}

void CThreeWave :: PlayerSpawn( CBasePlayer *pPlayer )
{
	BOOL		addDefault;
	CBaseEntity	*pWeaponEntity = NULL;

	if ( pPlayer->pev->team == 0 )
	{
		pPlayer->pev->takedamage		= DAMAGE_NO;
		pPlayer->pev->solid			= SOLID_NOT;
		pPlayer->pev->movetype		= MOVETYPE_NOCLIP;
		pPlayer->pev->effects		|= EF_NODRAW;
		pPlayer->pev->flags |= FL_NOTARGET;
        pPlayer->m_afPhysicsFlags |= PFLAG_OBSERVER;
        pPlayer->m_iHideHUD |= HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH; 

		pPlayer->m_flFlagStatusTime = gpGlobals->time + 0.1;
	}
	else
	{
		pPlayer->pev->weapons |= (1<<WEAPON_SUIT);
		
		addDefault = TRUE;

		while ( pWeaponEntity = UTIL_FindEntityByClassname( pWeaponEntity, "game_player_equip" ))
		{
			pWeaponEntity->Touch( pPlayer );
			addDefault = FALSE;
		}

		if ( addDefault )
		{
			pPlayer->m_bHasFlag = FALSE;

			pPlayer->m_iHideHUD &= ~HIDEHUD_WEAPONS;
			pPlayer->m_iHideHUD &= ~HIDEHUD_FLASHLIGHT;
			pPlayer->m_iHideHUD &= ~HIDEHUD_HEALTH;
			pPlayer->m_afPhysicsFlags &= ~PFLAG_OBSERVER;

			// Start with init ammoload
			pPlayer->m_iAmmoShells = 25;

			// Start with shotgun and axe
			pPlayer->GiveNamedItem( "weapon_quakegun" );
			pPlayer->m_iQuakeItems |= ( IT_SHOTGUN | IT_AXE | IT_EXTRA_WEAPON );
			pPlayer->m_iQuakeWeapon = pPlayer->W_BestWeapon();
			pPlayer->W_SetCurrentAmmo();

			pPlayer->m_flFlagStatusTime = gpGlobals->time + 0.1;
		}
	}

/*	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pPlayer->pev);
		WRITE_BYTE( pPlayer->m_iRuneStatus );
	MESSAGE_END();*/
}

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

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

	int clientIndex = pPlayer->entindex();
	// 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 )
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgTeamInfo, NULL, pPlayer->edict() );
				WRITE_BYTE( plr->entindex() );
				WRITE_STRING( plr->TeamID() );
			MESSAGE_END();

			if ( ((CBasePlayer *)plr)->m_bHasFlag )
			{
				MESSAGE_BEGIN( MSG_ONE, gmsgFlagCarrier, NULL, pPlayer->edict() );
					WRITE_BYTE( plr->entindex() );
					WRITE_BYTE( 1 );
				MESSAGE_END();
			}
		}
	}

	//Remove Rune icon if we have one.
	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pPlayer->pev);
		WRITE_BYTE( 0 );
	MESSAGE_END();

	if ( pPlayer->pev->team == 0)
	{
		 pPlayer->ShowMenu( 1 + 2 + 16, -1, FALSE, "#Team_Menu_Join" );
		 pPlayer->m_iMenu = Team_Menu;
	}
}


void CThreeWave::ChangePlayerTeam( CBasePlayer *pPlayer, int iTeam )
{
	int damageFlags = DMG_GENERIC;
	int clientIndex = pPlayer->entindex();

	if ( pPlayer->pev->team != 0 )
	{
		damageFlags |= DMG_ALWAYSGIB;

		// 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;
	}

	int oldTeam = pPlayer->pev->team;
	pPlayer->pev->team = iTeam;

	if ( pPlayer->pev->team == RED )
	{
		strncpy( pPlayer->m_szTeamName, "RED", TEAM_NAME_LENGTH );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", "red" );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "topcolor", UTIL_VarArgs( "%d", 255 ) );
	}
	else if ( pPlayer->pev->team == BLUE )
	{
		strncpy( pPlayer->m_szTeamName, "BLUE", TEAM_NAME_LENGTH );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", "blue" );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "topcolor", UTIL_VarArgs( "%d", 153 ) );
	}

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

	MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
		WRITE_BYTE( ENTINDEX(pPlayer->edict()) );
		WRITE_SHORT( pPlayer->pev->frags );
		WRITE_SHORT( pPlayer->m_iDeaths );
		WRITE_SHORT( pPlayer->pev->team );
	MESSAGE_END();

	// log the change
	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" joined team \"%s\"\n", 
		STRING(pPlayer->pev->netname),
		GETPLAYERUSERID( pPlayer->edict() ),
		GETPLAYERAUTHID( pPlayer->edict() ),
		GetTeamName( oldTeam ),
		pPlayer->m_szTeamName );
}


//=========================================================
// ClientUserInfoChanged
//=========================================================
void CThreeWave::ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer )
{

	int clientIndex = pPlayer->entindex();

	if ( pPlayer->pev->team == RED )
	{
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "topcolor", UTIL_VarArgs( "%d", 255 ) );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", "red" );
	}
	else if ( pPlayer->pev->team == BLUE )
	{
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "topcolor", UTIL_VarArgs( "%d", 153 ) );
		g_engfuncs.pfnSetClientKeyValue( clientIndex, g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict() ), "model", "blue" );
	}

}

extern int gmsgDeathMsg;

//=========================================================
// Deathnotice. 
//=========================================================
void CThreeWave::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 CThreeWave :: ClientDisconnected( edict_t *pClient )
{
	if ( pClient )
	{
		CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );

		if ( pPlayer )
		{
			//We have the flag, spawn it
			if ( pPlayer->m_bHasFlag )
			{
				CBaseEntity *pEnt; 

				//We have the BLUE flag, Spawn it
				if ( pPlayer->pev->team == RED )
				{
					pEnt = CBaseEntity::Create( "item_flag_team2", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );

					UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_Blue_Flag\"\n",  
						STRING( pPlayer->pev->netname ), 
						GETPLAYERUSERID( pPlayer->edict() ),
						GETPLAYERAUTHID( pPlayer->edict() ),
						GetTeamName( pPlayer->pev->team ) );
				}
				//We have the RED flag, Spawn it
				else if ( pPlayer->pev->team == BLUE )
				{
					pEnt = CBaseEntity::Create( "item_flag_team1", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );

					UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_Red_Flag\"\n",  
							STRING( pPlayer->pev->netname ), 
							GETPLAYERUSERID( pPlayer->edict() ),
							GETPLAYERAUTHID( pPlayer->edict() ),
							GetTeamName( pPlayer->pev->team ) );
				}
   
				pEnt->pev->velocity = pPlayer->pev->velocity * 1.2; 
				pEnt->pev->angles.x = 0;

				CItemFlag *pFlag = (CItemFlag *)pEnt; 
				pFlag->Dropped = TRUE; 
				pFlag->m_flDroppedTime = gpGlobals->time + TEAM_CAPTURE_FLAG_RETURN_TIME;

				PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
				pPlayer->edict(), g_usCarried, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
				0.0, 0.0, pPlayer->entindex(), pPlayer->pev->team, 1, 0 );

				MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
					if ( pPlayer->pev->team == RED )
						WRITE_BYTE( BLUE_FLAG_LOST );
					else if ( pPlayer->pev->team == BLUE )
						WRITE_BYTE( RED_FLAG_LOST );
			
					WRITE_STRING( STRING(pPlayer->pev->netname) );
				MESSAGE_END();

				m_flFlagStatusTime = gpGlobals->time + 0.1;

				pPlayer->m_bHasFlag = FALSE;
			}

			// drop any runes the player has
			CBaseEntity *pRune;
			char * runeName;

			switch ( pPlayer->m_iRuneStatus )
			{
				case ITEM_RUNE1_FLAG:

					pRune = CBaseEntity::Create( "item_rune1", pPlayer->pev->origin, pPlayer->pev->angles, NULL );
					
					pRune->pev->velocity = pPlayer->pev->velocity * 1.5; 
					pRune->pev->angles.x = 0;
					((CResistRune*)pRune)->dropped = true;

					runeName = "ResistRune";
						
					break;

				case ITEM_RUNE2_FLAG:

					pRune = CBaseEntity::Create( "item_rune2", pPlayer->pev->origin, pPlayer->pev->angles, NULL );

					pRune->pev->velocity = pPlayer->pev->velocity * 1.5; 
					pRune->pev->angles.x = 0;
					((CStrengthRune*)pRune)->dropped = true;

					runeName = "StrengthRune";
			
					break;

				case ITEM_RUNE3_FLAG:
			
					pRune = CBaseEntity::Create( "item_rune3", pPlayer->pev->origin, pPlayer->pev->angles, NULL );

					pRune->pev->velocity = pPlayer->pev->velocity * 1.5; 
					pRune->pev->angles.x = 0;
					((CHasteRune*)pRune)->dropped = true;

					runeName = "HasteRune";
			
					break;

				case ITEM_RUNE4_FLAG:
			
					pRune = CBaseEntity::Create( "item_rune4", pPlayer->pev->origin, pPlayer->pev->angles, NULL );

					pRune->pev->velocity = pPlayer->pev->velocity * 1.5; 
					pRune->pev->angles.x = 0;
					((CRegenRune*)pRune)->dropped = true;

					runeName = "RegenRune";
				
					break;

				default:

					runeName = "Unknown";

					break;
			}

			if ( pPlayer->m_iRuneStatus )
			{
				pPlayer->m_iRuneStatus = 0;

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_%s\"\n", 
					STRING(pPlayer->pev->netname),
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					pPlayer->m_szTeamName,
					runeName );
			}

			FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 );

			UTIL_LogPrintf( "\"%s<%i><%s><%s>\" disconnected\n",  
				STRING( pPlayer->pev->netname ), 
				GETPLAYERUSERID( pPlayer->edict() ),
				GETPLAYERAUTHID( pPlayer->edict() ),
				GetTeamName( pPlayer->pev->team ) );

			pPlayer->RemoveAllItems( TRUE );// destroy all of the players weapons and items
		}
	}
}

void CThreeWave :: PlayerThink( CBasePlayer *pPlayer )
{
	if ( g_fGameOver )
	{
		// check for button presses
		if ( pPlayer->m_afButtonPressed & ( IN_DUCK | IN_ATTACK | IN_ATTACK2 | IN_USE | IN_JUMP ) )
			m_iEndIntermissionButtonHit = TRUE;

		// clear attack/use commands from player
		pPlayer->m_afButtonPressed = 0;
		pPlayer->pev->button = 0;
		pPlayer->m_afButtonReleased = 0;
	}

	if ( pPlayer->pFlagCarrierKiller )
	{
		if ( pPlayer->m_flFlagCarrierKillTime <= gpGlobals->time )
			pPlayer->pFlagCarrierKiller = NULL;
	}

	if ( pPlayer->pFlagReturner )
	{
		if ( pPlayer->m_flFlagReturnTime <= gpGlobals->time )
			pPlayer->pFlagReturner = NULL;
	}

	if ( pPlayer->pCarrierHurter )
	{
		if ( pPlayer->m_flCarrierHurtTime <= gpGlobals->time )
			 pPlayer->pCarrierHurter = NULL;
	}

	if ( pPlayer->m_iRuneStatus == ITEM_RUNE4_FLAG) 
	{
		if ( pPlayer->m_flRegenTime <= gpGlobals->time) 
		{
			

			if ( pPlayer->pev->health < 150 ) 
			{
				pPlayer->pev->health += 5;

				if ( pPlayer->pev->health > 150)
					pPlayer->pev->health = 150;

				pPlayer->m_flRegenTime = gpGlobals->time + 1;
				
				EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "rune/rune4.wav", 1, ATTN_NORM);
			}
			if ( pPlayer->pev->armorvalue < 150 && pPlayer->pev->armorvalue )
			{
				pPlayer->pev->armorvalue += 5;

				if ( pPlayer->pev->armorvalue > 150)
					pPlayer->pev->armorvalue = 150;

				pPlayer->m_flRegenTime = gpGlobals->time + 1;

				EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "rune/rune4.wav", 1, ATTN_NORM);
			}
		}
	}

	if ( pPlayer->m_bOn_Hook ) 
		pPlayer->Service_Grapple();

	if ( pPlayer->m_flFlagStatusTime && pPlayer->m_flFlagStatusTime <= gpGlobals->time )
		GetFlagStatus( pPlayer );
}

//=========================================================
//=========================================================
void CThreeWave :: PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
	CBasePlayer *pk = NULL; 
	
	if ( pKiller )
	{
		CBaseEntity *pTemp =  CBaseEntity::Instance( pKiller );

		if ( pTemp->IsPlayer() )
			pk = (CBasePlayer*)pTemp;
	}
	
	//Only award a bonus if the Flag carrier had the flag for more than 2 secs
	//Prevents from people waiting for the flag carrier to grab the flag and then killing him
	//Instead of actually defending the flag.
	if ( pVictim->m_bHasFlag  )
    {
		if ( pk )
		{
			if ( pVictim->pev->team != pk->pev->team )
			{
				if ( pVictim->m_flCarrierPickupTime <= gpGlobals->time )
					pk->AddPoints( TEAM_CAPTURE_FRAG_CARRIER_BONUS, TRUE );

				if ( pk->pev->team == RED )
				{
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pk->pev->netname ) );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " fragged " );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "BLUE" );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier!\n" );

					UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Killed_Enemy_Flag_Carrier\"\n",  
						STRING( pk->pev->netname ), 
						GETPLAYERUSERID( pk->edict() ),
						GETPLAYERAUTHID( pk->edict() ),
						GetTeamName( pk->pev->team ) );

					if ( iBlueFlagStatus == BLUE_FLAG_STOLEN )
					{
						for ( int i = 1; i <= gpGlobals->maxClients; i++ )
						{
							CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
														
							if ( pTeamMate )
								{
									if ( pTeamMate->m_bHasFlag )
									{
										pTeamMate->pFlagCarrierKiller = pk;
										pTeamMate->m_flFlagCarrierKillTime = gpGlobals->time + TEAM_CAPTURE_FRAG_CARRIER_ASSIST_TIMEOUT;
									}
								}
						}
					}
				}

				if ( pk->pev->team == BLUE )
				{
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pk->pev->netname ) );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " fragged " );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED" );
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier!\n" );

					UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Killed_Enemy_Flag_Carrier\"\n",  
						STRING( pk->pev->netname ), 
						GETPLAYERUSERID( pk->edict() ),
						GETPLAYERAUTHID( pk->edict() ),
						GetTeamName( pk->pev->team ) );

					if ( iRedFlagStatus == RED_FLAG_STOLEN )
					{
						for ( int i = 1; i <= gpGlobals->maxClients; i++ )
						{
							CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
														
							if ( pTeamMate )
								{
									if ( pTeamMate->m_bHasFlag )
									{
										pTeamMate->pFlagCarrierKiller = pk;
										pTeamMate->m_flFlagCarrierKillTime = gpGlobals->time + TEAM_CAPTURE_FRAG_CARRIER_ASSIST_TIMEOUT;
									}
								}
						}
					}
				}
			}
		}


        CBaseEntity *pEnt; 

		//We have the BLUE flag, Spawn it
        if ( pVictim->pev->team == RED )
		{
            pEnt = CBaseEntity::Create( "item_flag_team2", pVictim->pev->origin, pVictim->pev->angles, pVictim->edict() );

			UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_Blue_Flag\"\n",  
				STRING( pVictim->pev->netname ), 
				GETPLAYERUSERID( pVictim->edict() ),
				GETPLAYERAUTHID( pVictim->edict() ),
				GetTeamName( pVictim->pev->team ) );
		}
		else if ( pVictim->pev->team == BLUE )
		{
            pEnt = CBaseEntity::Create( "item_flag_team1", pVictim->pev->origin, pVictim->pev->angles, pVictim->edict() );

			UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_Red_Flag\"\n",  
				STRING( pVictim->pev->netname ), 
				GETPLAYERUSERID( pVictim->edict() ),
				GETPLAYERAUTHID( pVictim->edict() ),
				GetTeamName( pVictim->pev->team ) );
		}
       
        pEnt->pev->velocity = pVictim->pev->velocity * 1.2; 
		pEnt->pev->angles.x = 0;

        CItemFlag *pFlag = (CItemFlag *)pEnt; 
        pFlag->Dropped = TRUE; 

		PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
		pVictim->edict(), g_usCarried, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
		0.0, 0.0, pVictim->entindex(), pVictim->pev->team, 1, 0 );

		pFlag->m_flDroppedTime = gpGlobals->time + TEAM_CAPTURE_FLAG_RETURN_TIME;

        MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
			if ( pVictim->pev->team == RED )
				WRITE_BYTE( BLUE_FLAG_LOST );
			else if ( pVictim->pev->team == BLUE )
				WRITE_BYTE( RED_FLAG_LOST );
	
			WRITE_STRING( STRING(pVictim->pev->netname) );
		MESSAGE_END();

		pVictim->m_bHasFlag = FALSE;

		m_flFlagStatusTime = gpGlobals->time + 0.1;
	}
	else
	{
		if ( pk )
		{
				if ( pk->pev->team == RED )
				{
					if ( iBlueFlagStatus == BLUE_FLAG_STOLEN )
					{
						for ( int i = 1; i <= gpGlobals->maxClients; i++ )
						{
							CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
														
							if ( pTeamMate && pTeamMate != pk )
								{
									if ( pTeamMate->pev->team == pk->pev->team )
									{
										if ( pTeamMate->m_bHasFlag )
										{
											if ( pTeamMate->pCarrierHurter )
											{
												if ( pTeamMate->pCarrierHurter == pVictim )
												{
													if ( pTeamMate->m_flCarrierHurtTime > gpGlobals->time )
													{
														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pk->pev->netname ) );
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends ");
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED" );
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier against an agressive enemy\n");
												
													    pk->AddPoints( TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS, TRUE );
													}
												}
											}
										}
									}
								}
						}
					}
				}
		
				if ( pk->pev->team == BLUE )
				{
					if ( iRedFlagStatus == RED_FLAG_STOLEN )
					{
						for ( int i = 1; i <= gpGlobals->maxClients; i++ )
						{
							CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
														
							if ( pTeamMate && pTeamMate != pk )
								{
									if ( pTeamMate->pev->team == pk->pev->team )
									{
										if ( pTeamMate->m_bHasFlag )
										{
											if ( pTeamMate->pCarrierHurter )
											{
												if ( pTeamMate->pCarrierHurter == pVictim )
												{
													if ( pTeamMate->m_flCarrierHurtTime > gpGlobals->time )
													{
														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pk->pev->netname ) );
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends ");
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "BLUE" );
 														UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier against an agressive enemy\n");

													    pk->AddPoints( TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS, TRUE );
													}
												}
											}
										}
									}
								}
						}
					}
				}
		}
		
	}

	// Find if this guy is near our flag or our flag carrier
	CBaseEntity *ent = NULL;
	float Dist;

	if ( pk )
	{
		if ( pk->pev->team == RED )
		{
			while((ent = UTIL_FindEntityByClassname( ent, "item_flag_team1")) != NULL)
			{
				//Do not defend a invisible flag
				if ( ent->pev->effects & EF_NODRAW )
					break;

				Dist = (pk->pev->origin - ent->pev->origin).Length();

				if ( Dist <= TEAM_CAPTURE_TARGET_PROTECT_RADIUS )
				{
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING ( pk->pev->netname ));
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends the ");
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED");
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " flag\n");

					pk->AddPoints( TEAM_CAPTURE_FLAG_DEFENSE_BONUS, TRUE );
					break;
				}
			}

			if ( iBlueFlagStatus == BLUE_FLAG_STOLEN )
				{
					for ( int i = 1; i <= gpGlobals->maxClients; i++ )
					{
						CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
														
						if ( pTeamMate && pTeamMate != pk )
							{
								if ( pTeamMate->pev->team == pk->pev->team )
								{
									if ( pTeamMate->m_bHasFlag )
									{
										Dist = (pk->pev->origin - pTeamMate->pev->origin).Length();

										if ( Dist <= TEAM_CAPTURE_TARGET_PROTECT_RADIUS )
										{
											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING ( pk->pev->netname ));
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends ");
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED");
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier\n");

											pk->AddPoints( TEAM_CAPTURE_CARRIER_PROTECT_BONUS, TRUE );
										}
									}
								}
							}
					}
				}

		}
		else if ( pk->pev->team == BLUE )
		{
			while((ent = UTIL_FindEntityByClassname( ent, "item_flag_team2")) != NULL)
			{
				//Do not defend a invisible flag
				if ( ent->pev->effects & EF_NODRAW )
					break;

				Dist = (pk->pev->origin - ent->pev->origin).Length();

				if ( Dist <= TEAM_CAPTURE_TARGET_PROTECT_RADIUS )
				{
					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING ( pk->pev->netname ));
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends the ");
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED");
 					UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " flag\n");

					pk->AddPoints( TEAM_CAPTURE_FLAG_DEFENSE_BONUS, TRUE );
					break;
				}
			}

			if ( iRedFlagStatus == RED_FLAG_STOLEN )
				{
					for ( int i = 1; i <= gpGlobals->maxClients; i++ )
					{
						CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
													
						if ( pTeamMate && pTeamMate != pk )
							{
								if ( pTeamMate->pev->team == pk->pev->team )
								{
									if ( pTeamMate->m_bHasFlag )
									{
										Dist = (pk->pev->origin - pTeamMate->pev->origin).Length();

										if ( Dist <= TEAM_CAPTURE_TARGET_PROTECT_RADIUS )
										{
											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING ( pk->pev->netname ));
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " defends ");
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "RED");
 											UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "'s flag carrier\n");

											pk->AddPoints( TEAM_CAPTURE_CARRIER_PROTECT_BONUS, TRUE );
										}
									}
								}
							}
					}
				}

		}
	}

	CBaseEntity *pRune;
	char * runeName;

	switch ( pVictim->m_iRuneStatus )
	{
		case ITEM_RUNE1_FLAG:

			pRune = CBaseEntity::Create( "item_rune1", pVictim->pev->origin, pVictim->pev->angles, NULL );
			
			pRune->pev->velocity = pVictim->pev->velocity * 1.5; 
			pRune->pev->angles.x = 0;
			((CResistRune*)pRune)->dropped = true;

			runeName = "ResistRune";
				
			break;

		case ITEM_RUNE2_FLAG:

			pRune = CBaseEntity::Create( "item_rune2", pVictim->pev->origin, pVictim->pev->angles, NULL );

			pRune->pev->velocity = pVictim->pev->velocity * 1.5; 
			pRune->pev->angles.x = 0;
			((CStrengthRune*)pRune)->dropped = true;

			runeName = "StrengthRune";
	
			break;

		case ITEM_RUNE3_FLAG:
	
			pRune = CBaseEntity::Create( "item_rune3", pVictim->pev->origin, pVictim->pev->angles, NULL );

			pRune->pev->velocity = pVictim->pev->velocity * 1.5; 
			pRune->pev->angles.x = 0;
			((CHasteRune*)pRune)->dropped = true;

			runeName = "HasteRune";
	
			break;

		case ITEM_RUNE4_FLAG:
	
			pRune = CBaseEntity::Create( "item_rune4", pVictim->pev->origin, pVictim->pev->angles, NULL );

			pRune->pev->velocity = pVictim->pev->velocity * 1.5; 
			pRune->pev->angles.x = 0;
			((CRegenRune*)pRune)->dropped = true;

			runeName = "RegenRune";
		
			break;

		default:

			runeName = "Unknown";

			break;
	}

	if ( pVictim->m_iRuneStatus )
	{
		UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_%s\"\n", 
			STRING(pVictim->pev->netname),
			GETPLAYERUSERID( pVictim->edict() ),
			GETPLAYERAUTHID( pVictim->edict() ),
			pVictim->m_szTeamName,
			runeName );
	}

	if ( pVictim->m_ppHook )
	   (( CGrapple *)pVictim->m_ppHook)->Reset_Grapple();

	pVictim->m_iRuneStatus = 0;

	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pVictim->pev);
		WRITE_BYTE( pVictim->m_iRuneStatus );
	MESSAGE_END();

	if ( !m_DisableDeathPenalty )
	{
		CHalfLifeMultiplay::PlayerKilled( pVictim, pKiller, pInflictor );
		RecountTeams();
	}
}


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

BOOL CThreeWave::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 CThreeWave::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;

	//As simple as this
	if ( pPlayer->pev->team == pTarget->pev->team )
	{
		return GR_TEAMMATE;
	}

	return GR_NOTTEAMMATE;
}

//=========================================================
//=========================================================
BOOL CThreeWave::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 CThreeWave::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 *CThreeWave::GetTeamID( CBaseEntity *pEntity )
{
	if ( pEntity == NULL || pEntity->pev == NULL )
		return "";

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


int CThreeWave::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 *CThreeWave::GetIndexedTeamName( int teamIndex )
{
	if ( teamIndex < 0 || teamIndex >= num_teams )
		return "";

	return team_names[ teamIndex ];
}


BOOL CThreeWave::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;
}


void CThreeWave::GetFlagStatus( CBasePlayer *pPlayer )
{
	
	CBaseEntity *pFlag = NULL;
    int iFoundCount = 0;
	int iDropped = 0;

	while((pFlag = UTIL_FindEntityByClassname( pFlag, "carried_flag_team1")) != NULL)
	{
		if ( pFlag && !FBitSet( pFlag->pev->flags, FL_KILLME) )
			iFoundCount++;
	}

	if ( iFoundCount >= 1 )
		iRedFlagStatus = RED_FLAG_STOLEN;

	if ( !iFoundCount )
	{
		while((pFlag = UTIL_FindEntityByClassname( pFlag, "item_flag_team1")) != NULL)
		{
			if ( pFlag )
			{
				if ( ((CItemFlag *)pFlag)->Dropped )
					iDropped++;

				iFoundCount++;
			}
		}
			
		if ( iFoundCount > 1 && iDropped == 1 )
			iRedFlagStatus = RED_FLAG_DROPPED;
		else if ( iFoundCount >= 1 && iDropped == 0 )
			iRedFlagStatus = RED_FLAG_ATBASE;
	}

	iDropped = iFoundCount = 0;

	while((pFlag = UTIL_FindEntityByClassname( pFlag, "carried_flag_team2")) != NULL)
	{
		if ( pFlag && !FBitSet( pFlag->pev->flags, FL_KILLME) )
			iFoundCount++;
	}

	if ( iFoundCount >= 1 )
		iBlueFlagStatus = BLUE_FLAG_STOLEN;

	if ( !iFoundCount )
	{

		while((pFlag = UTIL_FindEntityByClassname( pFlag, "item_flag_team2")) != NULL)
		{
			if ( pFlag )
			{
				if ( ((CItemFlag *)pFlag)->Dropped )
					iDropped++;

				iFoundCount++;
			}
		}
			
		if ( iFoundCount > 1 && iDropped == 1 )
			iBlueFlagStatus = BLUE_FLAG_DROPPED;
		else if ( iFoundCount >= 1 && iDropped == 0 )
			iBlueFlagStatus = BLUE_FLAG_ATBASE;
	}

	if ( pPlayer )
	{
		if ( pPlayer->pev->team == 0 )
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgFlagStatus, NULL, pPlayer->edict() );
				WRITE_BYTE( 0 );  
				WRITE_BYTE( iRedFlagStatus );
				WRITE_BYTE( iBlueFlagStatus );
				WRITE_BYTE( iRedTeamScore );
				WRITE_BYTE( iBlueTeamScore );
			MESSAGE_END();
		}
		else
		{
			MESSAGE_BEGIN( MSG_ONE, gmsgFlagStatus, NULL, pPlayer->edict() );
				WRITE_BYTE( 1 );  
				WRITE_BYTE( iRedFlagStatus );
				WRITE_BYTE( iBlueFlagStatus );
				WRITE_BYTE( iRedTeamScore );
				WRITE_BYTE( iBlueTeamScore );
			MESSAGE_END();
		}

		pPlayer->m_flFlagStatusTime = 0.0;
	}
	else
	{
		MESSAGE_BEGIN( MSG_ALL, gmsgFlagStatus, NULL );
				WRITE_BYTE( 1 );  
				WRITE_BYTE( iRedFlagStatus );
				WRITE_BYTE( iBlueFlagStatus );
				WRITE_BYTE( iRedTeamScore );
				WRITE_BYTE( iBlueTeamScore );
		MESSAGE_END();

		m_flFlagStatusTime = 0.0;
	}

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *plr = UTIL_PlayerByIndex( i );
		if ( plr )
		{
			if ( ((CBasePlayer *)plr)->m_bHasFlag )
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgFlagCarrier, NULL );
					WRITE_BYTE( plr->entindex() );
					WRITE_BYTE( 1 );
				MESSAGE_END();
			}
			else
			{
				MESSAGE_BEGIN( MSG_ALL, gmsgFlagCarrier, NULL );
					WRITE_BYTE( plr->entindex() );
					WRITE_BYTE( 0 );
				MESSAGE_END();
			}
		}
	}
}


//=========================================================
//=========================================================
void CThreeWave::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;
			}
		}
	}
}

/*****************************************************
******************************************************
                THREEWAVE CTF FLAG CODE
******************************************************
*****************************************************/

enum Flag_Anims
{
	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( FlagThink );
    SetTouch( FlagTouch ); 

    pev->nextthink = gpGlobals->time + 0.3; 

	//Set the Skin based on the team.
    pev->skin = pev->team;
   
    Dropped = FALSE; 
	m_flDroppedTime = 0.0;

    pev->sequence = NOT_CARRIED;
    pev->framerate = 1.0; 
   
   // if ( !DROP_TO_FLOOR(ENT(pev)) )
   //       ResetFlag( pev->team );
} 

void CItemFlag::FlagTouch ( CBaseEntity *pToucher  )
{
	if ( !pToucher )
		return;

	if ( !pToucher->IsPlayer() )
		return;

	if ( FBitSet( pev->effects, EF_NODRAW ) )
		return;
	
	if ( pToucher->pev->health <= 0 )
		 return;

	if ( pToucher->pev->team == 0 )
		return;

	CBasePlayer *pPlayer = (CBasePlayer *)pToucher;

	//Same team as the flag
	if ( pev->team == pToucher->pev->team )
	{
		//Flag is dropped, let's return it
		if ( Dropped )
		{
			Dropped = FALSE;
	
			pPlayer->AddPoints( TEAM_CAPTURE_RECOVERY_BONUS, TRUE );

			if ( pPlayer->pev->team == RED )
			{

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Returned_Red_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );

				if ( ((CThreeWave *) g_pGameRules)->iBlueFlagStatus == BLUE_FLAG_STOLEN )
				{
					for ( int i = 1; i <= gpGlobals->maxClients; i++ )
					{
						CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
													
						if ( pTeamMate )
						{
							if ( pTeamMate->m_bHasFlag )
							{
								pTeamMate->pFlagReturner = pPlayer;
								pTeamMate->m_flFlagReturnTime = gpGlobals->time + TEAM_CAPTURE_RETURN_FLAG_ASSIST_TIMEOUT;
							}
						}
					}
				}
			}

			if ( pPlayer->pev->team == BLUE )
			{
				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Returned_Blue_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );

				if ( ((CThreeWave *) g_pGameRules)->iRedFlagStatus == RED_FLAG_STOLEN )
				{
					for ( int i = 1; i <= gpGlobals->maxClients; i++ )
					{
						CBasePlayer *pTeamMate = (CBasePlayer *)UTIL_PlayerByIndex( i );
													
						if ( pTeamMate )
						{
							if ( pTeamMate->m_bHasFlag )
							{
								pTeamMate->pFlagReturner = pPlayer;
								pTeamMate->m_flFlagReturnTime = gpGlobals->time + TEAM_CAPTURE_RETURN_FLAG_ASSIST_TIMEOUT;
							}
						}
					}
				}
			}

			//Back at home!
			ResetFlag( pev->team );
            
			MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
		
				if ( pev->team == RED )
					WRITE_BYTE( RED_FLAG_RETURNED_PLAYER );
				else if ( pev->team == BLUE )
					WRITE_BYTE( BLUE_FLAG_RETURNED_PLAYER );
	
				WRITE_STRING( STRING(pToucher->pev->netname) );
			MESSAGE_END();

			//Remove this one
            UTIL_Remove( this );
		
			return;
		}
		//Not Dropped, means it's the one in our base
		else if ( !Dropped )
		{
			//We have the enemy flag!
			//Capture it!
			if ( pPlayer->m_bHasFlag )
			{
				if ( pev->team == RED )
					Capture( pPlayer, BLUE );
				else if ( pev->team == BLUE )
					Capture( pPlayer, RED );

				PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
				pPlayer->edict(), g_usCarried, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
				0.0, 0.0, pPlayer->entindex(), pPlayer->pev->team, 1, 0 );


				return;
			}
		}
	}
	else 
	{
		if ( Dropped )
		{
			MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
			
				if ( pev->team == RED )
					WRITE_BYTE( RED_FLAG_STOLEN );
				else if ( pev->team == BLUE )
					WRITE_BYTE( BLUE_FLAG_STOLEN );

				WRITE_STRING( STRING(pToucher->pev->netname) );

			MESSAGE_END();

			pPlayer->m_bHasFlag = TRUE;

			CBaseEntity *pEnt = NULL;

			if ( pev->team == RED )
			{
				pEnt = CBaseEntity::Create( "carried_flag_team1", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Picked_Up_Red_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
			}
			else if ( pev->team == BLUE )
			{
				pEnt = CBaseEntity::Create( "carried_flag_team2", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Picked_Up_Blue_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
			}

			CCarriedFlag *pCarriedFlag = (CCarriedFlag *)pEnt;
			pCarriedFlag->Owner = pPlayer;
			
			PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
			pPlayer->edict(), g_usCarried, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
			0.0, 0.0, pPlayer->entindex(), pPlayer->pev->team, 0, 0 );



			UTIL_Remove( this );
		}
		else
		{
			pev->effects |= EF_NODRAW;
			
			MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
			
				if ( pev->team == RED )
					WRITE_BYTE( RED_FLAG_STOLEN );
				else if ( pev->team == BLUE )
					WRITE_BYTE( BLUE_FLAG_STOLEN );

				WRITE_STRING( STRING(pToucher->pev->netname) );

			MESSAGE_END();

			pPlayer->m_bHasFlag = TRUE;
			pPlayer->m_flCarrierPickupTime = gpGlobals->time + TEAM_CAPTURE_CARRIER_FLAG_SINCE_TIMEOUT;

			CBaseEntity *pEnt = NULL;

			if ( pev->team == RED )
			{
				pEnt = CBaseEntity::Create( "carried_flag_team1", pev->origin, pev->angles, pPlayer->edict() );

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Stole_Red_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
			}
			else if ( pev->team == BLUE )
			{
				pEnt = CBaseEntity::Create( "carried_flag_team2", pev->origin, pev->angles, pPlayer->edict() );

				UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Stole_Blue_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
			}

			CCarriedFlag *pCarriedFlag = (CCarriedFlag *)pEnt;
			pCarriedFlag->Owner = pPlayer;

			PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
			pPlayer->edict(), g_usCarried, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
			0.0, 0.0, pPlayer->entindex(), pPlayer->pev->team, 0, 0 );
		}
		
		((CThreeWave *) g_pGameRules)->m_flFlagStatusTime = gpGlobals->time + 0.1;
	}
}

void CItemFlag::Capture(CBasePlayer *pPlayer, int iTeam )
{
	CBaseEntity *pFlag1 = NULL; 

	MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
	
		if ( iTeam == RED )
			WRITE_BYTE( RED_FLAG_CAPTURED );
		else if ( iTeam == BLUE )
			WRITE_BYTE( BLUE_FLAG_CAPTURED );

		WRITE_STRING( STRING( pPlayer->pev->netname) );

	MESSAGE_END();

	if ( pPlayer->pFlagCarrierKiller )
	{
		if ( pPlayer->m_flFlagCarrierKillTime > gpGlobals->time )
		{
			UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pPlayer->pFlagCarrierKiller->pev->netname ) );
			UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " gets an assist for fragging the flag carrier!\n");

			pPlayer->pFlagCarrierKiller->AddPoints( TEAM_CAPTURE_FRAG_CARRIER_ASSIST_BONUS, TRUE );
			pPlayer->pFlagCarrierKiller = NULL;
			pPlayer->m_flFlagCarrierKillTime = 0.0;
		}
	}

	if ( pPlayer->pFlagReturner )
	{
		if ( pPlayer->m_flFlagReturnTime > gpGlobals->time )
		{
			UTIL_ClientPrintAll( HUD_PRINTNOTIFY, STRING( pPlayer->pFlagReturner->pev->netname ) );
			UTIL_ClientPrintAll( HUD_PRINTNOTIFY, " gets an assist for returning his flag!\n");

			pPlayer->pFlagReturner->AddPoints( TEAM_CAPTURE_RETURN_FLAG_ASSIST_BONUS, TRUE );
			pPlayer->pFlagReturner = NULL;
			pPlayer->m_flFlagReturnTime = 0.0;
		}
	}

	if ( iTeam != pPlayer->pev->team )
	{
		if ( iTeam == RED )
		{
			UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Captured_Red_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
		}
		else
		{
			UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Captured_Blue_Flag\"\n",  
					STRING( pPlayer->pev->netname ), 
					GETPLAYERUSERID( pPlayer->edict() ),
					GETPLAYERAUTHID( pPlayer->edict() ),
					GetTeamName( pPlayer->pev->team ) );
		}
	}

	if ( iTeam == RED )
	{
		((CThreeWave *) g_pGameRules)->iBlueTeamScore++;

		while((pFlag1 = UTIL_FindEntityByClassname( pFlag1, "carried_flag_team1")) != NULL)
		{
			if ( pFlag1 )
				UTIL_Remove( pFlag1 );
		}
	}
	else if ( iTeam == BLUE )
	{
		((CThreeWave *) g_pGameRules)->iRedTeamScore++;

		while((pFlag1 = UTIL_FindEntityByClassname( pFlag1, "carried_flag_team2")) != NULL)
		{
			if ( pFlag1 )
				UTIL_Remove( pFlag1 );
		}
	}

	pPlayer->m_bHasFlag = FALSE;

	pPlayer->AddPoints( TEAM_CAPTURE_CAPTURE_BONUS, TRUE );

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
		{
			CBaseEntity *pTeamMate = UTIL_PlayerByIndex( i );
										
			if ( pTeamMate )
				{
					if ( pTeamMate->pev->team == pPlayer->pev->team )
					     pTeamMate->AddPoints( TEAM_CAPTURE_TEAM_BONUS, TRUE );
				}
		}

    ResetFlag( iTeam );
} 

void CItemFlag::Materialize( void )
{
    if ( pev->effects & EF_NODRAW )
    {        
        pev->effects &= ~EF_NODRAW;
        pev->effects |= EF_MUZZLEFLASH;
    } 

	PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
	edict(), g_usFlagSpawn, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
	0.0, 0.0, pev->team, 0, 0, 0 );

	Dropped = FALSE;

    SetTouch( FlagTouch );
    SetThink( FlagThink );
} 


void CItemFlag::ResetFlag( int iTeam )
{
	CBaseEntity *pFlag1 = NULL; 

	if ( iTeam == BLUE )
	{
		while((pFlag1 = UTIL_FindEntityByClassname( pFlag1, "item_flag_team2")) != NULL)
		{
			CItemFlag *pFlag2 = (CItemFlag *)pFlag1;

			if ( pFlag2->Dropped )
				continue;

			if ( pFlag2->pev->effects & EF_NODRAW)
				 pFlag2->Materialize(); 
		}

	}
	else if ( iTeam == RED )
	{
		while((pFlag1 = UTIL_FindEntityByClassname( pFlag1, "item_flag_team1")) != NULL)
		{
			CItemFlag *pFlag2 = (CItemFlag *)pFlag1;
			
			if ( pFlag2->Dropped )
				continue;

			if ( pFlag2->pev->effects & EF_NODRAW)
				 pFlag2->Materialize(); 
		}
	}

	((CThreeWave *) g_pGameRules)->m_flFlagStatusTime = gpGlobals->time + 0.1;

} 

void CItemFlag::FlagThink( void )
{
   	if ( Dropped )
	{
		if ( m_flDroppedTime <= gpGlobals->time )
		{
			
			ResetFlag( pev->team );
			
			MESSAGE_BEGIN ( MSG_ALL, gmsgCTFMsgs, NULL );
		
				if ( pev->team == RED )
					WRITE_BYTE( RED_FLAG_RETURNED );
				else if ( pev->team == BLUE )
					WRITE_BYTE( BLUE_FLAG_RETURNED );
	
				WRITE_STRING( "" );
			MESSAGE_END();

			UTIL_Remove( this );
			return;
		}
	}

	//Using 0.2 just in case we might lag the server.
	pev->nextthink = gpGlobals->time + 0.2;
} 

void CItemFlag::Precache( void )
{
    PRECACHE_MODEL ("models/flag.mdl");
    PRECACHE_SOUND ("ctf/flagcap.wav");
    PRECACHE_SOUND ("ctf/flagtk.wav");
	PRECACHE_SOUND ("ctf/flagret.wav");
} 

class CItemFlagTeam1 : public CItemFlag
{
    void Spawn( void )
    {
        pev->team = RED;
        CItemFlag::Spawn( );
    }
}; 

class CItemFlagTeam2 : public CItemFlag
{
    void Spawn( void )
    {
        pev->team = BLUE;
        CItemFlag::Spawn( );
    }
}; 

LINK_ENTITY_TO_CLASS( item_flag_team1, CItemFlagTeam1 ); 
LINK_ENTITY_TO_CLASS( item_flag_team2, CItemFlagTeam2 ); 


void CCarriedFlag ::Spawn( )
{
    Precache( ); 

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

    pev->movetype = MOVETYPE_NONE;
    pev->solid = SOLID_NOT; 
  
    pev->effects |= EF_NODRAW; 

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

    if ( pev->team == RED )
        pev->skin = 1;
    else if ( pev->team == BLUE )
        pev->skin = 2; 

	m_iOwnerOldVel = 0;

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

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

void CCarriedFlag::FlagThink( )
{
    //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_bHasFlag )
         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 )
    {
        pev->team = RED; 

        CCarriedFlag::Spawn( );
    }
}; 

class CCarriedFlagTeam2 : public CCarriedFlag
{
    void Spawn( void )
    {
        pev->team = BLUE;
 
        CCarriedFlag::Spawn( );
    }
}; 

LINK_ENTITY_TO_CLASS( carried_flag_team1, CCarriedFlagTeam1 ); 
LINK_ENTITY_TO_CLASS( carried_flag_team2, CCarriedFlagTeam2 ); 


/***************************************
****************************************
				RUNES
****************************************
***************************************/

/*----------------------------------------------------------------------
  The Rune Game modes

  Rune 1 - Earth Magic
	  resistance
  Rune 2 - Black Magic
	  strength
  Rune 3 - Hell Magic
	  haste
  Rune 4 - Elder Magic
	  regeneration

 ----------------------------------------------------------------------*/

BOOL IsRuneSpawnPointValid( CBaseEntity *pSpot )
{
	CBaseEntity *ent = NULL;
	
	while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL )
	{
		//Try not to spawn it near other runes.
		if ( !strcmp( STRING( ent->pev->classname ), "item_rune1")  || 
			 !strcmp( STRING( ent->pev->classname ), "item_rune2")  ||
			 !strcmp( STRING( ent->pev->classname ), "item_rune3")  ||
			 !strcmp( STRING( ent->pev->classname ), "item_rune4")  )
			return FALSE;
	}

	return TRUE;
}

edict_t *RuneSelectSpawnPoint( void )
{
	CBaseEntity *pSpot;
	
	pSpot = NULL;

	// Randomize the start spot
	for ( int i = RANDOM_LONG(1,5); i > 0; i-- )
		pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
	if ( !pSpot )  // skip over the null point
		pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );

	CBaseEntity *pFirstSpot = pSpot;

	do 
	{
		if ( pSpot )
		{
			if ( IsRuneSpawnPointValid( pSpot ) )
			{
				if ( pSpot->pev->origin == Vector( 0, 0, 0 ) )
				{
					pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
					continue;
				}
				// if so, go to pSpot
				goto ReturnSpot;
			}
		
		}
		// increment pSpot
		pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
	} while ( pSpot != pFirstSpot ); // loop if we're not back to the start

	// we haven't found a place to spawn yet,  so kill any guy at the first spawn point and spawn there
	if ( pSpot )
		goto ReturnSpot;
	
	// If startspot is set, (re)spawn there.
	if ( FStringNull( gpGlobals->startspot ) || !strlen(STRING(gpGlobals->startspot)))
	{
		pSpot = UTIL_FindEntityByClassname(NULL, "info_player_start");
		if ( pSpot )
			goto ReturnSpot;
	}
	else
	{
		pSpot = UTIL_FindEntityByTargetname( NULL, STRING(gpGlobals->startspot) );
		if ( pSpot )
			goto ReturnSpot;
	}

ReturnSpot:
	if ( !pSpot )
	{
		ALERT(at_error, "PutClientInServer: no info_player_start on level");
		return INDEXENT(0);
	}
	return pSpot->edict();
}

void VectorScale (const float *in, float scale, float *out)
{
	out[0] = in[0]*scale;
	out[1] = in[1]*scale;
	out[2] = in[2]*scale;
}

void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
{
	result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
	result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
	result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
}

#define VectorSet(v, x, y, z)	(v[0]=(x), v[1]=(y), v[2]=(z))

void DropRune ( CBasePlayer *pPlayer )
{
	TraceResult tr;

	// do they even have a rune?
	if ( pPlayer->m_iRuneStatus == 0 )
		return;

	// Make Sure there's enough room to drop the rune here
	// This is so hacky ( the reason why we are doing this), and I hate it to death.
	UTIL_MakeVectors ( pPlayer->pev->v_angle );
	Vector vecSrc	= pPlayer->GetGunPosition( );
	Vector vecEnd	= vecSrc + gpGlobals->v_forward * 32;
	UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, human_hull, ENT( pPlayer->pev ), &tr );

	if (tr.flFraction != 1)
	{
		ClientPrint( pPlayer->pev, HUD_PRINTCENTER, "Not enough room to drop the rune here." );
		return;
	}

	CBaseEntity *pRune = NULL;
	char * runeName;

	if ( pPlayer->m_iRuneStatus == ITEM_RUNE1_FLAG )
	{
		pRune = CBaseEntity::Create( "item_rune1", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );
		runeName = "ResistRune";

		if ( pRune )
			((CResistRune*)pRune)->dropped = true;
	}
	else if ( pPlayer->m_iRuneStatus == ITEM_RUNE2_FLAG )
	{
		pRune = CBaseEntity::Create( "item_rune2", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );
		runeName = "StrengthRune";

		if ( pRune )
			((CStrengthRune*)pRune)->dropped = true;
	}
	else if ( pPlayer->m_iRuneStatus == ITEM_RUNE3_FLAG )
	{
		pRune = CBaseEntity::Create( "item_rune3", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );
		runeName = "HasteRune";

		if ( pRune )
			((CHasteRune*)pRune)->dropped = true;
	}
	else if ( pPlayer->m_iRuneStatus == ITEM_RUNE4_FLAG )
	{
		pRune = CBaseEntity::Create( "item_rune4", pPlayer->pev->origin, pPlayer->pev->angles, pPlayer->edict() );
		runeName = "RegenRune";

		if ( pRune )
			((CRegenRune*)pRune)->dropped = true;
	}
	else
	{
		runeName = "Unknown";
	}

	if ( pPlayer->m_iRuneStatus == ITEM_RUNE3_FLAG )
		g_engfuncs.pfnSetClientMaxspeed( ENT( pPlayer->pev ), PLAYER_MAX_SPEED ); //Reset Haste player speed to normal

	pPlayer->m_iRuneStatus = 0;

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Dropped_%s\"\n", 
		STRING(pPlayer->pev->netname),
		GETPLAYERUSERID( pPlayer->edict() ),
		GETPLAYERAUTHID( pPlayer->edict() ),
		pPlayer->m_szTeamName,
		runeName );

	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pPlayer->pev);
		WRITE_BYTE( pPlayer->m_iRuneStatus );
	MESSAGE_END();
}


void CResistRune::RuneTouch ( CBaseEntity *pOther )
{
	//No toucher?
	if ( !pOther )
		return;

	//Not a player?
	if ( !pOther->IsPlayer() )
		return;

	//DEAD?!
	if ( pOther->pev->health <= 0 )
		 return;

	//Spectating?
	if ( pOther->pev->movetype == MOVETYPE_NOCLIP )
		 return;
	
	//Only one per customer
	if ( ((CBasePlayer *)pOther)->m_iRuneStatus )
	{
		ClientPrint( pOther->pev, HUD_PRINTCENTER, "You already have a rune!\n" );
		return;
	}

	if ( !m_bTouchable )
		return;

	((CBasePlayer *)pOther)->m_iRuneStatus = m_iRuneFlag; //Add me the rune flag

	ClientPrint( pOther->pev, HUD_PRINTCENTER, "You got the rune of Resistance!\n" );

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Found_ResistRune\"\n", 
		STRING(pOther->pev->netname),
		GETPLAYERUSERID( pOther->edict() ),
		GETPLAYERAUTHID( pOther->edict() ),
		((CBasePlayer *)pOther)->m_szTeamName );

	EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM );

	//Update my client side rune hud thingy.
	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pOther->pev);
		WRITE_BYTE( ((CBasePlayer *)pOther)->m_iRuneStatus );
	MESSAGE_END();

	//And Remove this entity
	UTIL_Remove( this );
}


void CResistRune::RuneRespawn ( void )
{
	edict_t *pentSpawnSpot;
	vec3_t vOrigin;

	pentSpawnSpot = RuneSelectSpawnPoint();
	vOrigin = VARS(pentSpawnSpot)->origin;
	
	UTIL_SetOrigin( pev, vOrigin );

	if ( dropped )
		UTIL_LogPrintf( "\"<-1><><>\" triggered triggered \"Respawn_ResistRune\"\n" );
	    
	Spawn();
}

void CResistRune::MakeTouchable ( void )
{
	m_bTouchable = TRUE;
	pev->nextthink = gpGlobals->time + 120; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( RuneRespawn );
}

void CResistRune::Spawn ( void )
{
	SET_MODEL( ENT(pev), "models/rune_resist.mdl");
   
	m_bTouchable = FALSE;

	m_iRuneFlag = ITEM_RUNE1_FLAG;

	dropped = false;

	pev->movetype = MOVETYPE_TOSS;
    pev->solid = SOLID_TRIGGER;

	vec3_t forward, right, up;

	UTIL_SetSize( pev, Vector(-15, -15, -15), Vector(15, 15, 15) ); 

	pev->angles.z = pev->angles.x = 0;
	pev->angles.y = RANDOM_LONG ( 0, 360 );

	//If we got an owner, it means we are either dropping the flag or diying and letting it go.
	if ( pev->owner )
	    g_engfuncs.pfnAngleVectors ( pev->owner->v.angles, forward, right, up );
	else
		g_engfuncs.pfnAngleVectors ( pev->angles, forward, right, up);

	UTIL_SetOrigin( pev, pev->origin );
	
	pev->velocity = ( forward * 400 ) + ( up * 200 );
	
	if ( pev->owner == NULL )
	{
		pev->origin.z += 16;
		pev->velocity.z = 300;
	}
	
	pev->owner = NULL;
	
	SetTouch( RuneTouch );
	
	pev->nextthink = gpGlobals->time + 1; 
	SetThink ( MakeTouchable );
}


LINK_ENTITY_TO_CLASS( item_rune1, CResistRune ); 


void CStrengthRune::MakeTouchable ( void )
{
	m_bTouchable = TRUE;
	pev->nextthink = gpGlobals->time + 120; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( RuneRespawn );
}

void CStrengthRune::RuneTouch ( CBaseEntity *pOther )
{
	//No toucher?
	if ( !pOther )
		return;

	//Not a player?
	if ( !pOther->IsPlayer() )
		return;

	//DEAD?!
	if ( pOther->pev->health <= 0 )
		 return;

	//Spectating?
	if ( pOther->pev->movetype == MOVETYPE_NOCLIP )
		 return;
	
	//Only one per customer
	if ( ((CBasePlayer *)pOther)->m_iRuneStatus )
	{
		ClientPrint( pOther->pev, HUD_PRINTCENTER, "You already have a rune!\n" );
		return;
	}

	if ( !m_bTouchable )
		return;

	((CBasePlayer *)pOther)->m_iRuneStatus = m_iRuneFlag; //Add me the rune flag

	ClientPrint( pOther->pev, HUD_PRINTCENTER, "You got the rune of Strength!\n" );

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Found_StrengthRune\"\n", 
		STRING(pOther->pev->netname),
		GETPLAYERUSERID( pOther->edict() ),
		GETPLAYERAUTHID( pOther->edict() ),
		((CBasePlayer *)pOther)->m_szTeamName );

	EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM );

	//Update my client side rune hud thingy.
	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pOther->pev);
		WRITE_BYTE( ((CBasePlayer *)pOther)->m_iRuneStatus );
	MESSAGE_END();

	//And Remove this entity
	UTIL_Remove( this );
}

void CStrengthRune::RuneRespawn ( void )
{
	edict_t *pentSpawnSpot;
	vec3_t vOrigin;

	pentSpawnSpot = RuneSelectSpawnPoint();
	vOrigin = VARS(pentSpawnSpot)->origin;
	
	UTIL_SetOrigin( pev, vOrigin );

	if ( dropped )
		UTIL_LogPrintf( "\"<-1><><>\" triggered triggered \"Respawn_StrengthRune\"\n" );

	Spawn();
}


void CStrengthRune::Spawn ( void )
{
	SET_MODEL( ENT(pev), "models/rune_strength.mdl");
    
	m_bTouchable = FALSE;

	m_iRuneFlag = ITEM_RUNE2_FLAG;

	dropped = false;

	pev->movetype = MOVETYPE_TOSS;
    pev->solid = SOLID_TRIGGER;

	vec3_t forward, right, up;

	UTIL_SetSize( pev, Vector(-15, -15, -15), Vector(15, 15, 15) ); 

	pev->angles.z = pev->angles.x = 0;
	pev->angles.y = RANDOM_LONG ( 0, 360 );

	//If we got an owner, it means we are either dropping the flag or diying and letting it go.
	if ( pev->owner )
	    g_engfuncs.pfnAngleVectors ( pev->owner->v.angles, forward, right, up);
	else
		g_engfuncs.pfnAngleVectors ( pev->angles, forward, right, up);

	UTIL_SetOrigin( pev, pev->origin );
	
	pev->velocity = ( forward * 400 ) + ( up * 200 );
	
	if ( pev->owner == NULL )
	{
		pev->origin.z += 16;
		pev->velocity.z = 300;
	}
	
	pev->owner = NULL;
	
	SetTouch( RuneTouch );

	pev->nextthink = gpGlobals->time + 1; 
	SetThink ( MakeTouchable );
}


LINK_ENTITY_TO_CLASS( item_rune2, CStrengthRune ); 

void CHasteRune::MakeTouchable ( void )
{
	m_bTouchable = TRUE;
	pev->nextthink = gpGlobals->time + 120; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( RuneRespawn );
}


void CHasteRune::RuneTouch ( CBaseEntity *pOther )
{
	//No toucher?
	if ( !pOther )
		return;

	//Not a player?
	if ( !pOther->IsPlayer() )
		return;

	//DEAD?!
	if ( pOther->pev->health <= 0 )
		 return;

	//Spectating?
	if ( pOther->pev->movetype == MOVETYPE_NOCLIP )
		 return;
	
	//Only one per customer
	if ( ((CBasePlayer *)pOther)->m_iRuneStatus )
	{
		ClientPrint( pOther->pev, HUD_PRINTCENTER, "You already have a rune!\n" );
		return;
	}

	if ( !m_bTouchable )
		return;

	((CBasePlayer *)pOther)->m_iRuneStatus = m_iRuneFlag; //Add me the rune flag

	ClientPrint( pOther->pev, HUD_PRINTCENTER, "You got the rune of Haste!\n" );

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Found_HasteRune\"\n", 
		STRING(pOther->pev->netname),
		GETPLAYERUSERID( pOther->edict() ),
		GETPLAYERAUTHID( pOther->edict() ),
		((CBasePlayer *)pOther)->m_szTeamName );

	g_engfuncs.pfnSetClientMaxspeed( ENT( pOther->pev ), ( PLAYER_MAX_SPEED * 1.25 ) ); //25% more speed

	EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM );

	//Update my client side rune hud thingy.
	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pOther->pev);
		WRITE_BYTE( ((CBasePlayer *)pOther)->m_iRuneStatus );
	MESSAGE_END();

	//And Remove this entity
	UTIL_Remove( this );
}

void CHasteRune::RuneRespawn ( void )
{
	edict_t *pentSpawnSpot;
	vec3_t vOrigin;

	pentSpawnSpot = RuneSelectSpawnPoint();
	vOrigin = VARS(pentSpawnSpot)->origin;
	
	UTIL_SetOrigin( pev, vOrigin );

	if ( dropped )
		UTIL_LogPrintf( "\"<-1><><>\" triggered triggered \"Respawn_HasteRune\"\n" );
    
	Spawn();
}


void CHasteRune::Spawn ( void )
{
	SET_MODEL( ENT(pev), "models/rune_haste.mdl");
 
	m_bTouchable = FALSE;

	m_iRuneFlag = ITEM_RUNE3_FLAG;

	dropped = false;

	pev->movetype = MOVETYPE_TOSS;
    pev->solid = SOLID_TRIGGER;

	vec3_t forward, right, up;

	UTIL_SetSize( pev, Vector(-15, -15, -15), Vector(15, 15, 15) ); 

	pev->angles.z = pev->angles.x = 0;
	pev->angles.y = RANDOM_LONG ( 0, 360 );

	//If we got an owner, it means we are either dropping the flag or diying and letting it go.
	if ( pev->owner )
	    g_engfuncs.pfnAngleVectors ( pev->owner->v.angles, forward, right, up);
	else
		g_engfuncs.pfnAngleVectors ( pev->angles, forward, right, up);

	UTIL_SetOrigin( pev, pev->origin );
	
	pev->velocity = ( forward * 400 ) + ( up * 200 );
	
	if ( pev->owner == NULL )
	{
		pev->origin.z += 16;
		pev->velocity.z = 300;
	}
	
	pev->owner = NULL;
	
	SetTouch( RuneTouch );

	pev->nextthink = gpGlobals->time + 1; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( MakeTouchable );
}


LINK_ENTITY_TO_CLASS( item_rune3, CHasteRune ); 


void CRegenRune::MakeTouchable ( void )
{
	m_bTouchable = TRUE;
	pev->nextthink = gpGlobals->time + 120; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( RuneRespawn );
}

void CRegenRune::RuneTouch ( CBaseEntity *pOther )
{
	//No toucher?
	if ( !pOther )
		return;

	//Not a player?
	if ( !pOther->IsPlayer() )
		return;

	//DEAD?!
	if ( pOther->pev->health <= 0 )
		 return;

	//Spectating?
	if ( pOther->pev->movetype == MOVETYPE_NOCLIP )
		 return;
	
	//Only one per customer
	if ( ((CBasePlayer *)pOther)->m_iRuneStatus )
	{
		ClientPrint( pOther->pev, HUD_PRINTCENTER, "You already have a rune!\n" );
		return;
	}

	if ( !m_bTouchable )
		return;

	((CBasePlayer *)pOther)->m_iRuneStatus = m_iRuneFlag; //Add me the rune flag

	ClientPrint( pOther->pev, HUD_PRINTCENTER, "You got the rune of Regeneration!\n" );

	UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"Found_RegenRune\"\n", 
		STRING(pOther->pev->netname),
		GETPLAYERUSERID( pOther->edict() ),
		GETPLAYERAUTHID( pOther->edict() ),
		((CBasePlayer *)pOther)->m_szTeamName );
	
	EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM );

	//Update my client side rune hud thingy.
	MESSAGE_BEGIN( MSG_ONE, gmsgRuneStatus, NULL, pOther->pev);
		WRITE_BYTE( ((CBasePlayer *)pOther)->m_iRuneStatus );
	MESSAGE_END();

	//And Remove this entity
	UTIL_Remove( this );
}

void CRegenRune::RuneRespawn ( void )
{
	edict_t *pentSpawnSpot;
	vec3_t vOrigin;

	pentSpawnSpot = RuneSelectSpawnPoint();
	vOrigin = VARS(pentSpawnSpot)->origin;
	
	UTIL_SetOrigin( pev, vOrigin );

	if ( dropped )
		UTIL_LogPrintf( "\"<-1><><>\" triggered triggered \"Respawn_RegenRune\"\n" );
    
	Spawn();
}


void CRegenRune::Spawn ( void )
{

	SET_MODEL( ENT(pev), "models/rune_regen.mdl" );
    
	m_bTouchable = FALSE;

	m_iRuneFlag = ITEM_RUNE4_FLAG;

	dropped = false;

	pev->movetype = MOVETYPE_TOSS;
    pev->solid = SOLID_TRIGGER;

	vec3_t forward, right, up;

	UTIL_SetSize( pev, Vector(-15, -15, -15), Vector(15, 15, 15) ); 

	pev->angles.z = pev->angles.x = 0;
	pev->angles.y = RANDOM_LONG ( 0, 360 );

	//If we got an owner, it means we are either dropping the flag or diying and letting it go.
	if ( pev->owner )
	    g_engfuncs.pfnAngleVectors ( pev->owner->v.angles, forward, right, up);
	else
		g_engfuncs.pfnAngleVectors ( pev->angles, forward, right, up);

	UTIL_SetOrigin( pev, pev->origin );
	
	pev->velocity = ( forward * 400 ) + ( up * 200 );
	
	if ( pev->owner == NULL )
	{
		pev->origin.z += 16;
		pev->velocity.z = 300;
	}
	
	pev->owner = NULL;

	SetTouch( RuneTouch );

	pev->nextthink = gpGlobals->time + 1; // if no one touches it in two minutes,
											// respawn it somewhere else, so inaccessible 
											// ones will come 'back'
	SetThink ( MakeTouchable );
}


LINK_ENTITY_TO_CLASS( item_rune4, CRegenRune ); 

/*
================
SpawnRunes
spawn all the runes
self is the entity that was created for us, we remove it
================
*/
void SpawnRunes( void )
{
	if ( g_bSpawnedRunes )
		return;

	edict_t *pentSpawnSpot;

	pentSpawnSpot = RuneSelectSpawnPoint();
	CBaseEntity::Create( "item_rune1", VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles, NULL );
	
	pentSpawnSpot = RuneSelectSpawnPoint();
	CBaseEntity::Create( "item_rune2", VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles, NULL );

	pentSpawnSpot = RuneSelectSpawnPoint();
	CBaseEntity::Create( "item_rune3", VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles, NULL );
	
	pentSpawnSpot = RuneSelectSpawnPoint();
	CBaseEntity::Create( "item_rune4", VARS(pentSpawnSpot)->origin, VARS(pentSpawnSpot)->angles, NULL );

	g_bSpawnedRunes = TRUE;
}


/***********************************************
************************************************
				    GRAPPLE
************************************************
***********************************************/

void CGrapple::Reset_Grapple ( void )
{
		CBaseEntity *pOwner =  CBaseEntity::Instance( pev->owner );
		
	    ((CBasePlayer *)pOwner)->m_bOn_Hook = FALSE;
        ((CBasePlayer *)pOwner)->m_bHook_Out = FALSE;

		PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
		((CBasePlayer *)pOwner)->edict(), g_usCable, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
		0.0, 0.0, entindex(), pev->team, 1, 0 );

		STOP_SOUND( edict(), CHAN_WEAPON, "weapons/grhang.wav" );
		STOP_SOUND( ((CBasePlayer *)pOwner)->edict(), CHAN_WEAPON, "weapons/grfire.wav" );
		STOP_SOUND( ((CBasePlayer *)pOwner)->edict(), CHAN_WEAPON, "weapons/grpull.wav" );

		((CBasePlayer *)pOwner)->m_ppHook = NULL;
		pev->enemy = NULL;

        UTIL_Remove ( this );
}

void CGrapple::GrappleTouch ( CBaseEntity *pOther )
{
	CBaseEntity *pOwner =  CBaseEntity::Instance( pev->owner );

	if ( pOther == pOwner )
             return;
		

        // DO NOT allow the grapple to hook to any projectiles, no matter WHAT!
        // if you create new types of projectiles, make sure you use one of the
        // classnames below or write code to exclude your new classname so
        // grapples will not stick to them.
        if ( FClassnameIs( pOther->pev, "grenade" )||   
			 FClassnameIs( pOther->pev, "spike" ) || 
			 FClassnameIs( pOther->pev, "hook" ) )
			return;
      
        if ( FClassnameIs( pOther->pev, "player" ) )
        {
                // glance off of teammates
                if ( pOther->pev->team == pOwner->pev->team )
                        return;

               // sound (self, CHAN_WEAPON, "player/axhit1.wav", 1, ATTN_NORM);
                //TakeDamage( pOther->pev, pOwner->pev, 10, DMG_GENERIC );

                // make hook invisible since we will be pulling directly
                // towards the player the hook hit. Quakeworld makes it
                // too quirky to try to match hook's velocity with that of
                // the client that it hit. 
               // setmodel (self, "");

				pev->velocity = Vector(0,0,0);
				UTIL_SetOrigin( pev, pOther->pev->origin);
        }
        else if ( !FClassnameIs( pOther->pev, "player" ) )
        {
               // sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);

                // One point of damage inflicted upon impact. Subsequent
                // damage will only be done to PLAYERS... this way secret
                // doors and triggers will only be damaged once.
                if ( pOther->pev->takedamage )
                        TakeDamage( pOther->pev, pOwner->pev, 1, DMG_GENERIC );

                pev->velocity = Vector(0,0,0);

				EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "weapons/grhit.wav", 1, ATTN_NORM);

				//No sparks underwater
				if ( pev->waterlevel == 0 )
					UTIL_Sparks( pev->origin );
        }

        // conveniently clears the sound channel of the CHAIN1 sound,
        // which is a looping sample and would continue to play. Tink1 is
        // the least offensive choice, ass NULL.WAV loops and clogs the
        // channel with silence
      //  sound (self.owner, CHAN_NO_PHS_ADD+CHAN_WEAPON, "weapons/tink1.wav", 1, ATTN_NORM);

        if ( !(pOwner->pev->button & IN_ATTACK) )
        {
			if ( ((CBasePlayer*)pOwner)->m_bOn_Hook ) 
			{
                Reset_Grapple();
				return;
			}
        }


        if ( pOwner->pev->flags & FL_ONGROUND)
        {
                pOwner->pev->flags &= ~FL_ONGROUND;
//                setorigin(self.owner,self.owner.origin + '0 0 1');
        }

        ((CBasePlayer*)pOwner)->m_bOn_Hook = TRUE;

       // sound (self.owner, CHAN_WEAPON, "weapons/chain2.wav", 1, ATTN_NORM);

        // CHAIN2 is a looping sample. Use LEFTY as a flag so that client.qc
        // will know to only play the tink sound ONCE to clear the weapons
        // sound channel. (Lefty is a leftover from AI.QC, so I reused it to
        // avoid adding a field)
        //self.owner.lefty = TRUE;
	
		STOP_SOUND( ((CBasePlayer *)pOwner)->edict(), CHAN_WEAPON, "weapons/grfire.wav" );

        pev->enemy = pOther->edict();// remember this guy!
        SetThink ( Grapple_Track );
        pev->nextthink = gpGlobals->time;
		m_flNextIdleTime = gpGlobals->time + 0.1;
		pev->solid = SOLID_NOT;
        SetTouch ( NULL );
};

bool CanSee ( CBaseEntity *pEnemy, CBaseEntity *pOwner )
{
	TraceResult tr;

	UTIL_TraceLine ( pOwner->pev->origin, pEnemy->pev->origin,  ignore_monsters, ENT( pOwner->pev ), &tr);
	if ( tr.flFraction == 1 )
		return TRUE;

	UTIL_TraceLine ( pOwner->pev->origin, pEnemy->pev->origin + Vector( 15, 15, 0 ),  ignore_monsters, ENT( pOwner->pev ), &tr);
	if ( tr.flFraction == 1 )
		return TRUE;

	UTIL_TraceLine ( pOwner->pev->origin, pEnemy->pev->origin + Vector( -15, -15, 0 ),  ignore_monsters, ENT( pOwner->pev ), &tr);
	if ( tr.flFraction == 1 )
		return TRUE;

	UTIL_TraceLine ( pOwner->pev->origin, pEnemy->pev->origin + Vector( -15, 15, 0 ),  ignore_monsters, ENT( pOwner->pev ), &tr);
	if ( tr.flFraction == 1 )
		return TRUE;

	UTIL_TraceLine ( pOwner->pev->origin, pEnemy->pev->origin + Vector( 15, -15, 0 ),  ignore_monsters, ENT( pOwner->pev ), &tr);
	if ( tr.flFraction == 1 )
		return TRUE;

	return FALSE;
}

void CGrapple::Grapple_Track ( void )
{
	CBaseEntity *pOwner =  CBaseEntity::Instance( pev->owner );
	CBaseEntity *pEnemy =  CBaseEntity::Instance( pev->enemy );

        // Release dead targets
        if ( FClassnameIs( pEnemy->pev, "player" ) && pEnemy->pev->health <= 0)
                Reset_Grapple();
                
        
		// drop the hook if owner is dead or has released the button
        if ( !((CBasePlayer*)pOwner)->m_bOn_Hook|| ((CBasePlayer*)pOwner)->pev->health <= 0)
        {
                Reset_Grapple();
                return;
        }

		if ( !(pOwner->pev->button & IN_ATTACK) )
        {
			if ( ((CBasePlayer*)pOwner)->m_iQuakeWeapon == IT_EXTRA_WEAPON ) 
			{
                Reset_Grapple();
				return;
			}
        }

        // bring the pAiN!
        if ( FClassnameIs( pEnemy->pev, "player" ) )
        {
			if ( !CanSee( pEnemy, pOwner ) ) 
			{
				Reset_Grapple();
				return;
			}


			// move the hook along with the player.  It's invisible, but
			// we need this to make the sound come from the right spot
			UTIL_SetOrigin( pev, pEnemy->pev->origin);
			
			//sound (self, CHAN_WEAPON, "blob/land1.wav", 1, ATTN_NORM);

			SpawnBlood( pEnemy->pev->origin, BLOOD_COLOR_RED, 1 );
			((CBasePlayer *)pEnemy)->TakeDamage( pev, pOwner->pev, 1, DMG_GENERIC );
        }

        // If the hook is not attached to the player, constantly copy
        // copy the target's velocity. Velocity copying DOES NOT work properly
        // for a hooked client. 
        if ( !FClassnameIs( pEnemy->pev, "player" ) )
              pev->velocity = pEnemy->pev->velocity;

        pev->nextthink = gpGlobals->time + 0.1;
};

void CBasePlayer::Service_Grapple ( void )
{
        Vector  hook_dir;
		CBaseEntity *pEnemy =  CBaseEntity::Instance( pev->enemy );

        // drop the hook if player lets go of button
        if ( !(pev->button & IN_ATTACK) )
        {
			if ( m_iQuakeWeapon == IT_EXTRA_WEAPON ) 
			{
                ((CGrapple *)m_ppHook)->Reset_Grapple();
				return;
			}
        }

		if ( m_ppHook->pev->enemy != NULL )
		{
			// If hooked to a player, track them directly!
			if ( FClassnameIs( pEnemy->pev, "player" ) )
			{
				pEnemy =  CBaseEntity::Instance( pev->enemy );
				hook_dir = ( pEnemy->pev->origin - pev->origin );
			}
			// else, track to hook
			else if ( !FClassnameIs( pEnemy->pev, "player" ) )
				hook_dir = ( m_ppHook->pev->origin - pev->origin );
			
			pev->velocity =  ( (hook_dir).Normalize() * 750 );
			pev->speed = 750;

			if ( ((CGrapple *)m_ppHook)->m_flNextIdleTime <= gpGlobals->time && (hook_dir).Length() <= 50 )
			{
				//No sparks underwater
				if ( m_ppHook->pev->waterlevel == 0 )
					UTIL_Sparks( m_ppHook->pev->origin );
			
				STOP_SOUND( edict(), CHAN_WEAPON, "weapons/grpull.wav" );
				EMIT_SOUND( ENT( m_ppHook->pev ), CHAN_WEAPON, "weapons/grhang.wav", 1, ATTN_NORM);

				((CGrapple *)m_ppHook)->m_flNextIdleTime = gpGlobals->time + RANDOM_LONG( 1, 3 );

				PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
				edict(), g_usCable, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
				0.0, 0.0, m_ppHook->entindex(), pev->team, 1, 0 );

			}
			else if ( ((CGrapple *)m_ppHook)->m_flNextIdleTime <= gpGlobals->time )
			{
				//No sparks underwater
				if ( m_ppHook->pev->waterlevel == 0 )
					UTIL_Sparks( m_ppHook->pev->origin );

				STOP_SOUND( edict(), CHAN_WEAPON, "weapons/grfire.wav" );
				EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "weapons/grpull.wav", 1, ATTN_NORM);
				((CGrapple *)m_ppHook)->m_flNextIdleTime = gpGlobals->time + RANDOM_LONG( 1, 3 );
			}

		}
};

void CGrapple::OnAirThink ( void )
{
	TraceResult tr;

	CBaseEntity *pOwner =  CBaseEntity::Instance( pev->owner );

	if ( !(pOwner->pev->button & IN_ATTACK) )
	{
         Reset_Grapple();
		 return;
	}

	UTIL_TraceLine ( pev->origin, pOwner->pev->origin,  ignore_monsters, ENT(pev), &tr);

	if ( tr.flFraction < 1.0 )
	{
		Reset_Grapple();
		return;
	}

	pev->nextthink = gpGlobals->time + 0.5;
}

			
     
void CGrapple::Spawn ( void )
{
	pev->movetype = MOVETYPE_FLYMISSILE;
	pev->solid = SOLID_BBOX;

	SET_MODEL ( ENT(pev),"models/hook.mdl");

	SetTouch ( GrappleTouch );
	SetThink ( OnAirThink );

	pev->nextthink = gpGlobals->time + 0.1;
}

LINK_ENTITY_TO_CLASS( hook, CGrapple ); 

void CBasePlayer::Throw_Grapple ( void )
{
        if ( m_bHook_Out )
             return;

		CBaseEntity *pHookCBEnt = NULL;
	
		pHookCBEnt = CBaseEntity::Create( "hook", pev->origin, pev->angles, NULL );

		if ( pHookCBEnt )
		{
			m_ppHook = pHookCBEnt;
		
			m_ppHook->pev->owner = edict();
		
			UTIL_MakeVectors ( pev->v_angle);

			UTIL_SetOrigin ( m_ppHook->pev , pev->origin + gpGlobals->v_forward * 16 + Vector( 0, 0, 16 ) );
			UTIL_SetSize( m_ppHook->pev, Vector(0,0,0) , Vector(0,0,0) );

			EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "weapons/grfire.wav", 1, ATTN_NORM);

			//Make if fly forward
			m_ppHook->pev->velocity = gpGlobals->v_forward * 1000;
			//And make the hook face forward too!
			m_ppHook->pev->angles = UTIL_VecToAngles ( gpGlobals->v_forward );	
			m_ppHook->pev->fixangle = TRUE;

			PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, 
			edict(), g_usCable, 0, (float *)&g_vecZero, (float *)&g_vecZero, 
			0.0, 0.0, m_ppHook->entindex(), pev->team, 0, 0 );

		
			m_bHook_Out = TRUE;
		}
};



#endif
