//======== (C) Copyright 2001 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Workfile: AvHMarineEquipment.cpp $
// $Date: 2002/11/22 21:28:16 $
//
//-------------------------------------------------------------------------------
// $Log: AvHMarineEquipment.cpp,v $
// Revision 1.51  2015/12/03 fmoraw
// - observatory detection method no longer uses vector2d
//
// Revision 1.50  2002/11/22 21:28:16  Flayra
// - mp_consistency changes
//
// Revision 1.49  2002/11/12 02:25:29  Flayra
// - HLTV updates
//
// Revision 1.48  2002/11/06 01:40:17  Flayra
// - Turrets now need an active turret factory to keep firing
//
// Revision 1.47  2002/11/03 04:50:43  Flayra
// - Hard-coded gameplay constants instead of putting in skill.cfg
// - Ammo and health now expire
//
// Revision 1.46  2002/10/28 20:35:46  Flayra
// - Allow HAs and jetpacks to only be picked up by marines
//
// Revision 1.45  2002/10/25 23:19:30  Flayra
// -  Fixed bug where people were being telefragged improperly
//
// Revision 1.44  2002/10/24 21:32:09  Flayra
// - All heavy armor to be given via console
// - Fix for AFKers on inf portals, also for REIN players when recycling portals
//
// Revision 1.43  2002/10/20 21:10:48  Flayra
// - Optimizations
//
// Revision 1.42  2002/10/19 22:33:44  Flayra
// - Various server optimizations
//
// Revision 1.41  2002/10/18 22:20:49  Flayra
// - Reduce llamability of placing phases near drops or hazards
//
// Revision 1.40  2002/10/16 20:54:30  Flayra
// - Added phase gate sound
// - Fixed ghostly command station view model problem after building it
//
// Revision 1.39  2002/10/16 01:00:14  Flayra
// - Allow any jetpack to be picked up (needed for cheat, but this is the way all weapons work too, may need to be changed for marine vs. marine, not sure)
//
// Revision 1.38  2002/10/03 18:57:20  Flayra
// - Picking up heavy armor holsters your weapon for view model switch
// - Added "armory's upgrading, ammo not available" message but removed it for some reason (I think it was acting strange, like playing way too often)
//
// Revision 1.37  2002/09/25 21:12:26  Flayra
// - Undid solidity change (causes Sys_Error)
//
// Revision 1.36  2002/09/25 20:48:47  Flayra
// - Phase gates no longer solid
//
// Revision 1.35  2002/09/23 22:21:21  Flayra
// - Added jetpack and heavy armor
// - Added "cc online" sound
// - Turret factories now upgrade to advanced turret factories for siege
// - Added automatic resupply at armory, but removed it
// - Observatories scan in 2D now, to match commander range overlay
//
// Revision 1.34  2002/09/09 19:59:31  Flayra
// - Fixed up phase gates (no longer teleport you unless you have two, and they work properly now)
// - Refactored reinforcements
// - Fixed bug where secondary command station couldn't be built
//
// Revision 1.33  2002/08/31 18:01:02  Flayra
// - Work at VALVe
//
// Revision 1.32  2002/08/16 02:39:14  Flayra
// - Fixed command station problems after game ends
//
// Revision 1.31  2002/08/09 01:06:02  Flayra
// - Removed ability for command station to be resurrected
// - Fixed up phase gate effects
//
// Revision 1.30  2002/08/02 21:55:55  Flayra
// - Allow phase teleport to fail nicely.  For some reason, players don't hear their own phase sound anymore, it seems to play at the point where they left instead of where they arrive
//
// Revision 1.29  2002/07/26 23:05:54  Flayra
// - Numerical event feedback
// - Started to add sparks when buildings were hit but didn't know the 3D point to play it at
//
// Revision 1.28  2002/07/24 18:45:42  Flayra
// - Linux and scripting changes
//
// Revision 1.27  2002/07/23 17:11:47  Flayra
// - Phase gates must be built and can be destroyed, observatories decloak aliens, hooks for fast reinforcements upgrade, nuke damage increased, commander banning
//
// Revision 1.26  2002/07/08 17:02:57  Flayra
// - Refactored reinforcements, updated entities for new artwork
//
// Revision 1.25  2002/07/01 21:29:59  Flayra
// - Removed phase particles, added implosion instead, don't select command station on log-in (messy for drawing building radii)
//
// Revision 1.24  2002/06/25 18:04:43  Flayra
// - Renamed some buildings, armory is now upgraded to advanced armory
//
// Revision 1.23  2002/06/10 19:58:17  Flayra
// - Scan update happens more often, in case aliens go cloaked during scan
//
// Revision 1.22  2002/06/03 16:50:35  Flayra
// - Renamed weapons factory and armory, added ammo resupplying
//
// Revision 1.21  2002/05/28 17:51:34  Flayra
// - Tried to make nuke sound play, extended shake duration to sound length, reinforcement refactoring, mark command stations as mapper placed, so they aren't deleted on level cleanup, support for point-entity buildable command stations
//
// Revision 1.20  2002/05/23 02:33:42  Flayra
// - Post-crash checkin.  Restored @Backup from around 4/16.  Contains changes for last four weeks of development.
//
//===============================================================================
#include "AvHMarineEquipment.h"
#include "AvHPlayer.h"
#include "AvHMarineEquipmentConstants.h"
#include "AvHPlayerUpgrade.h"
#include "AvHServerUtil.h"
#include "AvHGamerules.h"
#include "../dlls/client.h"
#include "../common/vec_op.h"
#include "../common/vector_util.h"
#include "AvHSoundListManager.h"
#include "../dlls/weapons.h"
#include "AvHServerVariables.h"
#include "AvHConstants.h"
#include "AvHSharedUtil.h"
#include "AvHMovementUtil.h"
#include "../dlls/explode.h"
#include "../dlls/weapons.h"
#include "../util/MathUtil.h"
#include "AvHTitles.h"
#include "AvHServerUtil.h"
#include "AvHParticleConstants.h"
#include "AvHMarineTurret.h"
#include "AvHSiegeTurret.h"
#include "AvHHulls.h"

//LINK_ENTITY_TO_CLASS(kwMine, AvHMine);
//LINK_ENTITY_TO_CLASS(kwDeployedTurret, AvHDeployedTurret);
//LINK_ENTITY_TO_CLASS(kwTurret, AvHTurret);
LINK_ENTITY_TO_CLASS(kwDeployedMine, AvHDeployedMine);
LINK_ENTITY_TO_CLASS(kwHealth, AvHHealth);
LINK_ENTITY_TO_CLASS(kwCatalyst, AvHCatalyst);
LINK_ENTITY_TO_CLASS(kwGenericAmmo, AvHGenericAmmo);
LINK_ENTITY_TO_CLASS(kwJetpack, AvHJetpack);
LINK_ENTITY_TO_CLASS(kwAmmoPack, AvHAmmoPack);
LINK_ENTITY_TO_CLASS(kwHeavyArmor, AvHHeavyArmor);

LINK_ENTITY_TO_CLASS(kwWelder, AvHWelder);
LINK_ENTITY_TO_CLASS(kwScan, AvHScan);
LINK_ENTITY_TO_CLASS(kwPhaseGate, AvHPhaseGate);
//LINK_ENTITY_TO_CLASS(kwSiegeTurret, AvHSiegeTurret);
LINK_ENTITY_TO_CLASS(kwNuke, AvHNuke);

LINK_ENTITY_TO_CLASS(kwInfantryPortal, AvHInfantryPortal);
LINK_ENTITY_TO_CLASS(kwTeamCommand, AvHCommandStation);
LINK_ENTITY_TO_CLASS(kwTurretFactory, AvHTurretFactory);
LINK_ENTITY_TO_CLASS(kwArmory, AvHArmory);
LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHArmory);
//LINK_ENTITY_TO_CLASS(kwAdvancedArmory, AvHAdvancedArmory);
LINK_ENTITY_TO_CLASS(kwArmsLab, AvHArmsLab);
LINK_ENTITY_TO_CLASS(kwPrototypeLab, AvHPrototypeLab);
LINK_ENTITY_TO_CLASS(kwObservatory, AvHObservatory);

extern int						gTeleportEventID;
extern int						gSiegeHitEventID;
extern int						gSiegeViewHitEventID;
extern AvHSoundListManager		gSoundListManager;

void AvHDeployedMine::Precache(void)
{
	PRECACHE_UNMODIFIED_MODEL(kTripmineWModel);
	PRECACHE_UNMODIFIED_MODEL(kTripmineW2Model);
	PRECACHE_UNMODIFIED_SOUND(kTripmineDeploySound);
	PRECACHE_UNMODIFIED_SOUND(kTripmineActivateSound);
	PRECACHE_UNMODIFIED_SOUND(kTripmineChargeSound);
	PRECACHE_UNMODIFIED_SOUND(kTripmineStepOnSound);
}

void AvHDeployedMine::ActiveTouch(CBaseEntity* inOther)
{
	float kTimeBetweenBeeps = 3.0f;
	
	// Hack to circumvent the hack where owners can't collide.  If this isn't done, then mines will blowup when touching the world sometimes.
	//edict_t* theTempOwner = this->pev->owner;
	//this->pev->owner = this->m_pRealOwner;
	bool theEntityCanDoDamage = GetGameRules()->CanEntityDoDamageTo(this, inOther);
	//this->pev->owner = theTempOwner;
	// Check team here and only emit warning beep for friendlies
	if(theEntityCanDoDamage && (this->pev->team != inOther->pev->team))
	{
		GetGameRules()->MarkDramaticEvent(kMineExplodePriority, inOther, this);
		this->Detonate();
	}
	else
	{
		if(gpGlobals->time > this->mLastTimeTouched + kTimeBetweenBeeps)
		{
			// Only players trigger this, not buildings or other mines
			AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
			if(thePlayer)
			{
				// Play warning proximity beep
				EMIT_SOUND( ENT(pev), CHAN_BODY, kTripmineStepOnSound, 0.5, ATTN_NORM ); // shut off chargeup
				this->mLastTimeTouched = gpGlobals->time;
			}
		}
	}
}

// Ripped from old grenade
void AvHDeployedMine::Explode(TraceResult* inTrace, int inBitsDamageType)
{
	float		flRndSound;// sound randomizer
	
	pev->model = iStringNull;//invisible
	pev->solid = SOLID_NOT;// intangible
	
	float theDamageModifier;
	int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier);
	float theDamage = this->pev->dmg*theDamageModifier;

	pev->takedamage = DAMAGE_NO;

	// TODO: Look at upgrade and mark up damage

	// Pull out of the wall a bit
	if ( inTrace->flFraction != 1.0 )
	{
		pev->origin = inTrace->vecEndPos + (inTrace->vecPlaneNormal * (theDamage - 24) * 0.6);
	}

	int iContents = UTIL_PointContents ( pev->origin );
	
	MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin );
		WRITE_BYTE( TE_EXPLOSION );		// This makes a dynamic light and the explosion sprites/sound
		WRITE_COORD( pev->origin.x );	// Send to PAS because of the sound
		WRITE_COORD( pev->origin.y );
		WRITE_COORD( pev->origin.z );
		if (iContents != CONTENTS_WATER)
		{
			WRITE_SHORT( g_sModelIndexFireball );
		}
		else
		{
			WRITE_SHORT( g_sModelIndexWExplosion );
		}
		WRITE_BYTE( (theDamage - 50) * .60  ); // scale * 10
		WRITE_BYTE( 15  ); // framerate
		WRITE_BYTE( TE_EXPLFLAG_NONE );
	MESSAGE_END();

	CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );

	//RadiusDamage(this->pev, this->pevOwner, theDamage, CLASS_NONE, inBitsDamageType);
    int theRadius = BALANCE_VAR(kMineRadius);
	RadiusDamage(this->pev->origin, this->pev, this->mPlacer, theDamage, theRadius, CLASS_NONE, inBitsDamageType);

	// Play view shake here
	float theShakeAmplitude = 80;
	float theShakeFrequency = 100;
	float theShakeDuration = 1.0f;
	float theShakeRadius = 700;
	UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);

	if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 )
	{
		UTIL_DecalTrace( inTrace, DECAL_SCORCH1 );
	}
	else
	{
		UTIL_DecalTrace( inTrace, DECAL_SCORCH2 );
	}

	flRndSound = RANDOM_FLOAT( 0 , 1 );

	switch ( RANDOM_LONG( 0, 2 ) )
	{
		case 0:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM);	break;
		case 1:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM);	break;
		case 2:	EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM);	break;
	}

	pev->effects |= EF_NODRAW;
	SetThink( &CGrenade::Smoke );
	pev->velocity = g_vecZero;
	pev->nextthink = gpGlobals->time + 0.3;

	if (iContents != CONTENTS_WATER)
	{
		int sparkCount = RANDOM_LONG(0,3);
		for ( int i = 0; i < sparkCount; i++ )
			Create( "spark_shower", pev->origin, inTrace->vecPlaneNormal, NULL );
	}
}

void AvHDeployedMine::Smoke(void)
{
	if (UTIL_PointContents(this->pev->origin ) == CONTENTS_WATER)
	{
		UTIL_Bubbles(this->pev->origin - Vector( 64, 64, 64 ), this->pev->origin + Vector( 64, 64, 64 ), 100 );
	}
	else
	{
		float theDamageModifier;
		int theUpgradeLevel = AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser3, this->pev->iuser4, &theDamageModifier);
		int theDamage = this->pev->dmg*theDamageModifier;
		
		MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, this->pev->origin);
			WRITE_BYTE( TE_SMOKE );
			WRITE_COORD( this->pev->origin.x );
			WRITE_COORD( this->pev->origin.y );
			WRITE_COORD( this->pev->origin.z );
			WRITE_SHORT( g_sModelIndexSmoke );
			WRITE_BYTE( (theDamage - 50) * 0.80 ); // scale * 10
			WRITE_BYTE( 12  ); // framerate
		MESSAGE_END();
	}
	UTIL_Remove( this );
}

void AvHDeployedMine::Killed(entvars_t* inAttacker, int inGib)
{
	this->Detonate();

	CBasePlayerItem::Killed(inAttacker, inGib);
}

int	AvHDeployedMine::TakeDamage(entvars_t *inInflictor, entvars_t *inAttacker, float inDamage, int inBitsDamageType)
{
	// Don't include this for potential overflow reasons
	//UTIL_Sparks(this->pev->origin);

	return CBasePlayerItem::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType);
}

void AvHDeployedMine::Detonate()
{
	// Stop charging up
	EMIT_SOUND(ENT(this->pev), CHAN_BODY, "common/null.wav", 0.5, ATTN_NORM ); 

	if(!this->mDetonated && this->mPoweredUp)
	{
		TraceResult tr;
		UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 64, dont_ignore_monsters, ENT(this->pev), &tr);
		
		this->Explode(&tr, NS_DMG_NORMAL);

		this->mDetonated = true;
	}
}

const float kTripmineActiveThinkTime = .8f;
const float kTripminePowerUpThinkTime = .2f;
const float kTripminePowerUpTime = 3.8f;
const float kTripmineFailTime = 20.0f;

void AvHDeployedMine::SetPlacer(entvars_t* inPlacer)
{
	this->mPlacer = inPlacer;
}

void AvHDeployedMine::PowerupThink()
{
	// Find an owner
	if(this->mOwner == NULL)
	{
		edict_t* theOldOwner = this->pev->owner;
		this->pev->owner = NULL;

		TraceResult tr;
		UTIL_TraceLine(this->pev->origin + this->mVecDir * 8, this->pev->origin - this->mVecDir * 32, dont_ignore_monsters, ENT(this->pev), &tr);

		if (tr.fStartSolid || (theOldOwner && tr.pHit == theOldOwner))
		{
			this->pev->owner = theOldOwner;
		}
		else if (tr.flFraction < 1.0)
		{
			this->pev->owner = tr.pHit;
			this->mOwner = CBaseEntity::Instance(this->pev->owner);
			this->mOwnerOrigin = this->mOwner->pev->origin;
			this->mOwnerAngles = this->mOwner->pev->angles;
		}
		else
		{
			STOP_SOUND(ENT(this->pev), CHAN_VOICE, kTripmineDeploySound);
			STOP_SOUND(ENT(this->pev), CHAN_BODY, kTripmineChargeSound);

			SetThink(&CBaseEntity::SUB_Remove);
			this->pev->nextthink = gpGlobals->time + 0.1;

			ALERT(at_console, "WARNING:Tripmine at %.0f, %.0f, %.0f removed\n", this->pev->origin.x, this->pev->origin.y, this->pev->origin.z);
			//KillBeam();
			return;
		}
	}
	else
	{
		this->DetonateIfOwnerInvalid();
	}

	if(!this->mPoweredUp)
	{
		if(gpGlobals->time > (this->mTimePlaced + kTripminePowerUpTime))
		{
			//if(this->pev->solid == SOLID_BBOX)
			//{
				// play enabled sound
				EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, kTripmineActivateSound, 0.5, ATTN_NORM, 1.0, 75 );
				
				SetTouch(&AvHDeployedMine::ActiveTouch);
				
				SetThink(&AvHDeployedMine::ActiveThink);
				this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime;
					
				this->mPoweredUp = true;
			//}
		}
	}

	if(!this->mPoweredUp)
	{
		this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime;
	}
}

void AvHDeployedMine::DetonateIfOwnerInvalid()
{
	if((this->mOwner == NULL) || (this->mOwner->pev->origin != this->mOwnerOrigin) || (this->mOwner->pev->angles != this->mOwnerAngles))
	{
		this->Detonate();
	}
}

// Check to see if our position is no longer valid
void AvHDeployedMine::ActiveThink()
{
	this->DetonateIfOwnerInvalid();
	this->pev->nextthink = gpGlobals->time + kTripmineActiveThinkTime;
}

void AvHDeployedMine::Spawn(void)
{
	this->Precache();

	this->pev->movetype = MOVETYPE_FLY;
	this->pev->solid = SOLID_BBOX;
	this->pev->classname = MAKE_STRING(kwsDeployedMine);
	this->pev->iuser3 = AVH_USER3_MINE;
	
	SET_MODEL(ENT(pev), kTripmineWModel);

	UTIL_SetSize(this->pev, kMineMinSize, kMineMaxSize);
	UTIL_SetOrigin(this->pev, this->pev->origin );

	// Can't emit beep until active
	this->mTimePlaced = gpGlobals->time;
	this->mLastTimeTouched = this->mTimePlaced;
	
	SetThink(&AvHDeployedMine::PowerupThink);
	this->pev->nextthink = gpGlobals->time + kTripminePowerUpThinkTime;

	// give them hit points
	this->pev->takedamage = DAMAGE_YES;
	this->pev->health = BALANCE_VAR(kMineHealth);
	this->pev->dmg = BALANCE_VAR(kMineDamage);
	
	// play deploy sound
	EMIT_SOUND( ENT(this->pev), CHAN_VOICE, kTripmineDeploySound, .8f, ATTN_NORM );
	EMIT_SOUND( ENT(this->pev), CHAN_BODY, kTripmineChargeSound, .5f, ATTN_NORM ); // chargeup

	UTIL_MakeAimVectors(this->pev->angles);
	this->mVecDir = gpGlobals->v_forward;
	this->mVecEnd = this->pev->origin + this->mVecDir * 2048;

	this->mDetonated = false;
	this->mPoweredUp = false;
	this->mOwner = NULL;
	this->mPlacer = NULL;
}

AvHPlayerEquipment::AvHPlayerEquipment()
{
	this->mIsPersistent = false;
	this->mLifetime = -1;
}

void AvHPlayerEquipment::KeyValue(KeyValueData* pkvd)
{
	// Any entity placed by the mapper is persistent
	this->SetPersistent();
	
	if(FStrEq(pkvd->szKeyName, "teamchoice"))
	{
		//this->mTeam = (AvHTeamNumber)(atoi(pkvd->szValue));
		this->pev->team = (AvHTeamNumber)(atoi(pkvd->szValue));
		
		pkvd->fHandled = TRUE;
	}
	else if(FStrEq(pkvd->szKeyName, "angles"))
	{
		// TODO: Insert code here
		//pkvd->fHandled = TRUE;
		int a = 0;
	}
	else if(FStrEq(pkvd->szKeyName, "lifetime"))
	{
		this->mLifetime = atoi(pkvd->szValue);
		pkvd->fHandled = TRUE;
	}
	else
	{
		CBasePlayerItem::KeyValue(pkvd);
	}
}

// 0 means never expire, -1 means no value was set so use default
int AvHPlayerEquipment::GetLifetime() const
{
	int theLifetime = this->mLifetime;

	if(theLifetime < 0)
	{
		theLifetime = AvHSUGetWeaponStayTime();
	}

	return theLifetime;
}

bool AvHPlayerEquipment::GetIsPersistent() const
{
	return this->mIsPersistent;
}

void AvHPlayerEquipment::SetPersistent()
{
	this->mIsPersistent = true;
}

void AvHHealth::Precache(void)
{
	PRECACHE_UNMODIFIED_MODEL(kHealthModel);
	PRECACHE_UNMODIFIED_SOUND(kHealthPickupSound);
}

void AvHHealth::Spawn(void)
{
	this->Precache();
	
	SET_MODEL(ENT(pev), kHealthModel);
	this->pev->movetype = MOVETYPE_TOSS;
	this->pev->solid = SOLID_TRIGGER;
	this->pev->framerate = 1.0;
	
	UTIL_SetSize(pev, kHealthMinSize, kHealthMaxSize); 
	UTIL_SetOrigin( pev, pev->origin );
	
	SetTouch(&AvHHealth::Touch);

	// Expire after a time.
	int theLifetime = this->GetLifetime();
	if(theLifetime > 0)
	{
		SetThink(&AvHHealth::SUB_Remove);
		this->pev->nextthink = gpGlobals->time + theLifetime;
	}
	
	this->pev->iuser3 = AVH_USER3_MARINEITEM;
}

BOOL AvHHealth::GiveHealth(CBaseEntity* inOther, float points)
{
	BOOL theSuccess = FALSE;

// : 1017
// Amount of health to give is now a paramater to allow us to vary the resupply amount for the armoury

//	float thePointsPerHealth = BALANCE_VAR(kPointsPerHealth)
	
	
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
	if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine())
	{
		float thePlayerMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, thePlayer->GetUser3(), thePlayer->GetExperienceLevel());
		if(thePlayer->pev->health < thePlayerMaxHealth)
		{
			float thePointsGiven = min(points, (thePlayerMaxHealth - thePlayer->pev->health));
			
			thePlayer->pev->health += thePointsGiven;
			
			if(ns_cvar_float(&avh_drawdamage))
			{
				thePlayer->PlaybackNumericalEvent(kNumericalInfoHealthEvent, thePointsGiven);
			}
			
			// Remove parasite if player has one
			//int& theUser4 = thePlayer->pev->iuser4;
			//SetUpgradeMask(&theUser4, MASK_PARASITED, false);
			
			EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHealthPickupSound, 1, ATTN_NORM);

			theSuccess = TRUE;
		}
	}

	return theSuccess;
}

void AvHHealth::Touch(CBaseEntity* inOther)
{
	// : 1017 medpack health amount
	if(AvHHealth::GiveHealth(inOther, BALANCE_VAR(kPointsPerHealth)))
	{
		UTIL_Remove(this);
	}
}


void AvHCatalyst::Precache(void)
{
    PRECACHE_UNMODIFIED_MODEL(kCatalystModel);
    PRECACHE_UNMODIFIED_SOUND(kCatalystPickupSound);
}

void AvHCatalyst::Spawn(void)
{
    this->Precache();
    
    SET_MODEL(ENT(pev), kCatalystModel);
    this->pev->movetype = MOVETYPE_TOSS;
    this->pev->solid = SOLID_TRIGGER;
    
    UTIL_SetSize(pev, kCatalystMinSize, kCatalystMaxSize); 
    UTIL_SetOrigin( pev, pev->origin );
    
    SetTouch(&AvHCatalyst::Touch);
    
    // Expire after a time.
    int theLifetime = this->GetLifetime();
    if(theLifetime > 0)
    {
        SetThink(&AvHCatalyst::SUB_Remove);
        this->pev->nextthink = gpGlobals->time + theLifetime;
    }
    
    this->pev->iuser3 = AVH_USER3_MARINEITEM;
}

BOOL AvHCatalyst::GiveCatalyst(CBaseEntity* inOther)
{
    BOOL theSuccess = FALSE;
    
    float theCatalystDuration = BALANCE_VAR(kCatalystDuration);
    
    AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
    if(thePlayer && thePlayer->GetIsRelevant() && thePlayer->GetIsMarine() && !thePlayer->GetIsCatalysted())
    {
        //// The player takes damage too
        //float theDamagePercent = BALANCE_VAR(kCatalystDamagePercent);
        //float theMaxHealth = AvHPlayerUpgrade::GetMaxHealth(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);
        //float theDamage = theDamagePercent*theMaxHealth;
        //
        //// Never kill the player
        //theDamage = min(theDamage, thePlayer->pev->health - 1);
        //thePlayer->TakeDamage(thePlayer->pev, thePlayer->pev, theDamage, DMG_GENERIC | DMG_IGNOREARMOR);

        EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kCatalystPickupSound, 1, ATTN_NORM);

        thePlayer->SetIsCatalysted(true, theCatalystDuration);

        theSuccess = TRUE;
    }
    
    return theSuccess;
}

void AvHCatalyst::Touch(CBaseEntity* inOther)
{
    if(AvHCatalyst::GiveCatalyst(inOther))
    {
        UTIL_Remove(this);
    }
}

void AvHHeavyArmor::Precache(void)
{
	PRECACHE_UNMODIFIED_MODEL(kHeavyModel);
	PRECACHE_UNMODIFIED_SOUND(kHeavyPickupSound);
}

void AvHHeavyArmor::Spawn(void)
{
	this->Precache();
	
	SET_MODEL(ENT(pev), kHeavyModel);
	this->pev->movetype = MOVETYPE_TOSS;
	this->pev->solid = SOLID_TRIGGER;
	UTIL_SetSize(pev, kHeavyMinSize, kHeavyMaxSize); 
	UTIL_SetOrigin( pev, pev->origin );
	
	SetTouch(&AvHHeavyArmor::Touch);
	
	this->pev->iuser3 = AVH_USER3_HEAVY;
}

void AvHHeavyArmor::Touch(CBaseEntity* inOther)
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
	if(thePlayer && thePlayer->GetIsRelevant())
	{
		if(thePlayer->GetIsMarine())
		{
			// Check to make sure they don't have heavy armor or jetpack already
			if((!thePlayer->GetHasJetpack() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasHeavyArmor())//: ignore in combat mode since were trying to touch it.
			{
				// Needed because view model changes
				if(thePlayer->HolsterWeaponToUse())
				{
					// Give player heavy armor
					SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7, false);
					SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13);

					// Mark player with heavy armor
					thePlayer->EffectivePlayerClassChanged();

					// Set new armor value
					thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);

					EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kHeavyPickupSound, 1, ATTN_NORM);
					
					UTIL_Remove(this);
				}
			}
		}
	}
}

void AvHJetpack::Precache(void)
{
	PRECACHE_UNMODIFIED_MODEL(kJetpackModel);
	PRECACHE_UNMODIFIED_SOUND(kJetpackPickupSound);
}

void AvHJetpack::Spawn(void)
{
	this->Precache();
	
	SET_MODEL(ENT(pev), kJetpackModel);
	this->pev->movetype = MOVETYPE_TOSS;
	this->pev->solid = SOLID_TRIGGER;
	UTIL_SetSize(pev, kJetpackMinSize, kJetpackMaxSize); 
	UTIL_SetOrigin( pev, pev->origin );
	
	SetTouch(&AvHJetpack::Touch);
	
	this->pev->iuser3 = AVH_USER3_JETPACK;
}

void AvHJetpack::Touch(CBaseEntity* inOther)
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
	if(thePlayer && thePlayer->GetIsRelevant())
	{
		if(thePlayer->GetIsMarine())
		{
			// Check to make sure they don't have heavy armor or jetpack already
			if((!thePlayer->GetHasHeavyArmor() || GetGameRules()->GetIsCombatMode()) && !thePlayer->GetHasJetpack())//: ignore in combat mode since were trying to touch it.
			{
                if(thePlayer->HolsterWeaponToUse())
                {
				    // Give player jetpack
					SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_13, false);
				    SetUpgradeMask(&thePlayer->pev->iuser4, MASK_UPGRADE_7);
				    
					// Mark player with jetpack
					thePlayer->EffectivePlayerClassChanged();

					EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kJetpackPickupSound, 1, ATTN_NORM);

					// Set new armor value
					thePlayer->pev->armorvalue = AvHPlayerUpgrade::GetMaxArmorLevel(thePlayer->pev->iuser4, (AvHUser3)thePlayer->pev->iuser3);

				    // Set full energy to start
				    thePlayer->pev->fuser3 = 1.0f*kNormalizationNetworkFactor;


				    UTIL_Remove(this);
                }
			}
		}
	}
}


void AvHAmmoPack :: Precache( void )
{
	PRECACHE_UNMODIFIED_MODEL(kAmmoPackModel);
	PRECACHE_UNMODIFIED_SOUND(kAmmoPackPickupSound);
}

void AvHAmmoPack :: Spawn( void )
{
	this->Precache();

	SET_MODEL(ENT(pev), kAmmoPackModel);

	this->pev->movetype = MOVETYPE_TOSS;
	this->pev->solid = SOLID_TRIGGER;
	this->m_flNoTouch = gpGlobals->time + 0.25;

	UTIL_SetSize(pev, kAmmoPackMinSize, kAmmoPackMaxSize); 
	UTIL_SetOrigin( pev, pev->origin );

	SetTouch(&AvHAmmoPack::Touch);
}

void AvHAmmoPack :: Touch( CBaseEntity *inOther)
{
	if(this->m_flNoTouch > gpGlobals->time)
		return;

	//Dont touch non-players
	if(!inOther->IsPlayer())
		return;

	AvHPlayer *thePlayer = dynamic_cast<AvHPlayer*>(inOther);

	//if they dont have a weapon that uses this ammo, dont pick up the ammo pack.
	if ( !(thePlayer->pev->weapons & (1<<m_iWeaponID)) )
		return;

	if( thePlayer->GiveAmmo(this->m_iAmmoAmt, this->m_szAmmoType, this->m_iMaxAmmo) != -1)
	{
		EMIT_SOUND(ENT(thePlayer->pev), CHAN_ITEM, kAmmoPackPickupSound, 1, ATTN_NORM);
		thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0);
		UTIL_Remove(this);
	}
}

BOOL AvHGenericAmmo::GiveAmmo(CBaseEntity* inOther)
{
	// Give ammo to the player.  It will be added as generic ammo, added as one clip to 
	// the player's current weapon.
	if (inOther->GiveAmmo( 0, kwsGenericAmmo, 0 ) != -1)
	{
		EMIT_SOUND(ENT(inOther->pev), CHAN_ITEM, kAmmoPickupSound, 1, ATTN_NORM);
		
		AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inOther);
		if(thePlayer)
		{
			// Just say "ammo received" instead of number of bullets, too confusing
			thePlayer->PlaybackNumericalEvent(kNumericalInfoAmmoEvent, 0);
		}
		
		return TRUE;
	}

	return FALSE;
}

BOOL AvHGenericAmmo::AddAmmo( CBaseEntity *pOther ) 
{ 
	if(this->mDropped)
	{
		return GiveAmmo(pOther);
	}

	return FALSE;
}

void AvHGenericAmmo::Dropped(void)
{
	this->mDropped = true;
	SetThink(NULL);

	// Expire after a time
	SetThink(&AvHGenericAmmo::SUB_Remove);
	this->pev->nextthink = gpGlobals->time + AvHSUGetWeaponStayTime();
}

void AvHGenericAmmo::Precache( void )
{
	PRECACHE_UNMODIFIED_MODEL(kAmmoModel);
	PRECACHE_UNMODIFIED_SOUND(kAmmoPickupSound);
}

void AvHGenericAmmo::Spawn( void )
{ 
	Precache( );
	SET_MODEL(ENT(pev), kAmmoModel);
	UTIL_SetSize(pev, kAmmoMinSize, kAmmoMaxSize); 
	
	CBasePlayerAmmo::Spawn( );
	this->mDropped = false;
	SetThink(&AvHGenericAmmo::Dropped);
	this->pev->nextthink = gpGlobals->time + .15f;

	this->pev->iuser3 = AVH_USER3_MARINEITEM;
}


const float kScanThinkTime = .5f;

AvHScan::AvHScan()
{
}

void AvHScan::Precache(void)
{
	CBaseAnimating::Precache();
	
	PRECACHE_UNMODIFIED_MODEL(kScanModel);
	PRECACHE_UNMODIFIED_SOUND(kScanSound);
}

void AvHScan::Spawn(void)
{
//	this->Precache();
//	CBaseAnimating::Spawn();
//	
//	SET_MODEL(ENT(this->pev), kScanModel);
//	
//	this->pev->movetype = MOVETYPE_NONE;
//	this->pev->solid = SOLID_NOT;
//	
//    this->pev->classname = MAKE_STRING(kwsScan);
//	
//	this->pev->takedamage = DAMAGE_NO;
//	
//	// Start animating
//	this->pev->sequence	= 0;
//	this->pev->frame = 0;
//	ResetSequenceInfo();
	
	this->pev->effects |= (/*EF_BRIGHTFIELD |*/ EF_DIMLIGHT);
	
	this->mTimeCreated = gpGlobals->time;

	SetThink(&AvHScan::ScanThink);
	this->pev->nextthink = gpGlobals->time + GetGameRules()->GetFirstScanThinkTime();
	
	EMIT_SOUND(this->edict(), CHAN_AUTO, kScanSound, 1.0f, ATTN_NORM);

	AvHSUPlayParticleEvent(kpsScanEffect, this->edict(), this->pev->origin);
}

void AvHScan::ScanThink()
{
	// Remove cloaking from nearby enemies
	FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
		if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team))
		{
			// Check that entity is in range of scan
			float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin);
			if(theDistance < BALANCE_VAR(kScanRadius))
			{
				// Remove cloaking, if player has it
				theEntity->TriggerUncloak();
			}
		}
	END_FOR_ALL_ENTITIES(kAvHPlayerClassName)

	// Look in sphere for cloakables
	CBaseEntity* theSphereEntity = NULL;
	while ((theSphereEntity = UTIL_FindEntityInSphere(theSphereEntity, this->pev->origin, BALANCE_VAR(kScanRadius))) != NULL)
	{
		if(!AvHSUGetIsExternalClassName(STRING(theSphereEntity->pev->classname)))
		{
			// TODO: Check team here?
			AvHCloakable* theCloakable = dynamic_cast<AvHCloakable*>(theSphereEntity);
			if(theCloakable)
			{
				theCloakable->Uncloak();
			}
		}
	}

	float theScanTime = GetGameRules()->GetBuildTimeForMessageID(BUILD_SCAN);
	if(gpGlobals->time < (this->mTimeCreated + theScanTime))
	{
		this->pev->nextthink = gpGlobals->time + kScanThinkTime;
	}
	else
	{
		UTIL_Remove(this);
	}
}



AvHPhaseGate::AvHPhaseGate() : AvHMarineBaseBuildable(TECH_PHASE_GATE, BUILD_PHASEGATE, kwsPhaseGate, AVH_USER3_PHASEGATE)
{
	this->mEnabled = false;
	this->mTimeOfLastDeparture=0.0f;
	this->mHasWarmedUp=false;
}

int	AvHPhaseGate::GetSequenceForBoundingBox() const
{
	return 1;
}

void AvHPhaseGate::Killed(entvars_t* inAttacker, int inGib)
{
	this->SetEnabled(false);
	
	AvHMarineBaseBuildable::Killed(inAttacker, inGib);

	GetGameRules()->MarkDramaticEvent(kPGDeathPriority, this->entindex(), inAttacker);
}

void AvHPhaseGate::Precache(void)
{
	CBaseAnimating::Precache();
	
	PRECACHE_UNMODIFIED_MODEL(kPhaseGateModel);
	PRECACHE_UNMODIFIED_SOUND(kPhaseGateSound);
	PRECACHE_UNMODIFIED_SOUND(kPhaseGateTransportSound);
}

void AvHPhaseGate::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	// Include a "warm-up" time so movement chambers don't teleport the player immediately
	this->mTimeOfLastDeparture=gpGlobals->time;
	SetThink(&AvHPhaseGate::IdleThink);

	this->pev->nextthink = gpGlobals->time + kBuildingUseWarmupTime;
}

int	AvHPhaseGate::GetIdleAnimation() const
{
	return 0;
}

void AvHPhaseGate::IdleThink()
{
	bool theIsEnabled = false;
	this->mHasWarmedUp=true;
	// Check if there are any other phase gates on our team and set our enabled state accordingly
	bool theDone = false;
	bool potentialDeadlock=false;
	int theNumPhaseGates=0;
	if(this->GetIsBuilt() && !this->GetIsRecycling())
	{
		edict_t* thePhaseGateEdict = ENT(this->pev);
		
		// Keep looping until we come back to ourself or we find another gate on our team to phase to
		while(!theDone)
		{
			thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate);
			AvHPhaseGate* thePhaseGate = dynamic_cast<AvHPhaseGate*>(CBaseEntity::Instance(thePhaseGateEdict));
			
			// This assert fails in normal operation, seemingly harmlessly
			//ASSERT(thePhaseGate);
			
			if(thePhaseGateEdict == ENT(this->pev))
			{
				theDone = true;
			}
			else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsBuilt() && !thePhaseGate->GetIsRecycling() && thePhaseGate->HasWarmedUp() )
			{
				theIsEnabled = true;
				theDone = true;
			}
			theNumPhaseGates++;
		}
	}

	AvHBaseBuildable::AnimateThink();

	this->SetEnabled(theIsEnabled);

	this->pev->nextthink = gpGlobals->time + kPhaseGateIdleThink;
}

void AvHPhaseGate::ResetEntity()
{
	this->SetEnabled(false);

	AvHMarineBaseBuildable::ResetEntity();
}

void AvHPhaseGate::SetEnabled(bool inEnabledState)
{
	if(inEnabledState != this->mEnabled)
	{
		if(inEnabledState)
		{
			// Start animating
			this->pev->sequence	= 0;
			this->pev->frame = 0;
			ResetSequenceInfo();
			
			//this->pev->effects |= EF_BRIGHTLIGHT;
			this->pev->effects |= EF_DIMLIGHT;
		
			UTIL_EmitAmbientSound( ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, ATTN_NORM, 0, 100);

			SetUse(&AvHPhaseGate::TeleportUse);
		
			this->mEnabled = true;
		}
		else
		{
			// Stop animating
			this->pev->sequence	= 1;
			this->pev->frame = 0;
			ResetSequenceInfo();
		
			this->pev->effects &= ~EF_DIMLIGHT;
		
			UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100);

			SetUse(NULL);
		
			this->mEnabled = false;
		}
	}
}

bool AvHPhaseGate::GetAreTeammatesBlocking(AvHPlayer* inPlayer, const Vector& inOrigin) const
{
	
    // This is based off the logic of AvHSUKillPlayersTouchingPlayer so that
    // the results are consistent.
	
    bool theResult = false;

    FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
	if(theEntity->GetIsRelevant() && theEntity->pev->team == inPlayer->pev->team)
	{
		float theDistanceToPlayer = VectorDistance(inOrigin, theEntity->pev->origin);
		if(theDistanceToPlayer < 30)
		{
            if (gpGlobals->time - theEntity->GetTimeOfLastTeleport() < kPhaseGateAmnestyTime)
            {
                theResult = true;
            }
		}
	}
	END_FOR_ALL_ENTITIES(kAvHPlayerClassName)

    return theResult;

}

void AvHPhaseGate::KillBuildablesTouchingPlayer(AvHPlayer* inPlayer, entvars_t* inInflictor)
{

    FOR_ALL_BASEENTITIES();
	AvHBaseBuildable* theBuildable = dynamic_cast<AvHBaseBuildable*>(theBaseEntity);
	if(theBuildable)
	{
        if (theBuildable->pev->iuser3 != AVH_USER3_HIVE && theBuildable->pev->iuser3 != AVH_USER3_PHASEGATE)
        {
	        float theDistanceToPlayer = VectorDistance(inPlayer->pev->origin, theBuildable->pev->origin);
		    if(theDistanceToPlayer < 50)
		    {
			    theBuildable->TakeDamage(inInflictor, theBuildable->pev, 10000, DMG_GENERIC);
		    }
        }
    }
    END_FOR_ALL_BASEENTITIES();

}
bool AvHPhaseGate::HasWarmedUp() const
{
	return this->mHasWarmedUp;
}
bool AvHPhaseGate::GetIsEnabled() const
{
	return this->GetIsBuilt() && this->mEnabled && !this->GetIsRecycling();
}
void AvHPhaseGate::SetTimeOfLastDeparture(float timeOfLastDeparture) 
{
	mTimeOfLastDeparture=timeOfLastDeparture;
}
bool AvHPhaseGate::IsReadyToUse()
{
	bool theReturn=false;
	if ( (gpGlobals->time - mTimeOfLastDeparture) > BALANCE_VAR(kPhaseGateDepartureInterval) ) 
	{
		theReturn=true;
	}
	return theReturn;
}

void AvHPhaseGate::TeleportUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pActivator);
	CBasePlayerItem* thePlayerItem = dynamic_cast<CBasePlayerItem*>(pActivator);
	
	// Only players on the team of the person that created the phase gate can travel through,
	bool theTeleportAllowed = (thePlayer != NULL) && (thePlayer->pev->team == this->pev->team) && thePlayer->GetIsAbleToAct();
	//bool theTeleportAllowed = thePlayerItem || (thePlayer && ((pActivator->pev->team == this->pev->team) && (this->pev->team != 0)));
	if(theTeleportAllowed && this->GetIsEnabled())
	{
		float theLastTeleportTime = thePlayer->GetTimeOfLastTeleport();
		theTeleportAllowed = (theLastTeleportTime == -1) || ((gpGlobals->time - theLastTeleportTime) >= BALANCE_VAR(kPhaseGateDelay));
		if(theTeleportAllowed)
		{
			if(!GetGameRules()->GetIsTesting())
			{
				// Teleport to "next" gate.  If there isn't more then one gate, respawn the player
				vec3_t theOrigin;
				
				bool theDone = false;
				bool theSuccess = false;
				edict_t* thePhaseGateEdict = ENT(this->pev);
				AvHPhaseGate *theTargetGate=0;
				// Keep looping until we come back to ourself or we find another gate on our team to phase to
				while(!theDone)
				{
					thePhaseGateEdict = FIND_ENTITY_BY_CLASSNAME(thePhaseGateEdict, kwsPhaseGate);
					AvHPhaseGate* thePhaseGate = dynamic_cast<AvHPhaseGate*>(CBaseEntity::Instance(thePhaseGateEdict));

					// This assert fails in normal operation, seemingly harmlessly
					//ASSERT(thePhaseGate);
					
					if(thePhaseGateEdict == ENT(this->pev))
					{
						theDone = true;
					}
					else if((thePhaseGateEdict->v.team == this->pev->team) && thePhaseGate && thePhaseGate->GetIsEnabled() && this->IsReadyToUse() )
					{
						// Players come through on top of phase gate, plus a little above
						theOrigin = thePhaseGateEdict->v.origin;
					
						theOrigin.z +=kRespawnFudgeFactorHeight;

						// Add in proper hull size so players don't get stuck
						Vector thePlayerMinSize, thePlayerMaxSize;						
						thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize);
						theOrigin.z += -thePlayerMinSize.z;

						//Elven Thief - Changed from AvHUGetHull(false to true to allow crouching marines access to phase gates.
						// check out bug 487
                        if(AvHSUGetIsEnoughRoomForHull(theOrigin, AvHMUGetHull(true, thePlayer->pev->iuser3), thePlayer->edict(), true, true))
						{
							theSuccess = true;
							theDone = true;
						}
                        
                        // Now check to see if there are any teammates blocking who just phased in.
                        
                        if (GetAreTeammatesBlocking(thePlayer, theOrigin))
                        {
                            theSuccess = false;
                            theDone = true;
                        }

						if ( theDone == true )
						{
							theTargetGate=thePhaseGate;
						}


					}
				}
				
				if(theSuccess)
				{
					// Mark the player as just having teleported so he doesn't teleport immediately again
					thePlayer->SetPosition(theOrigin);

					thePlayer->pev->velocity = g_vecZero;
					thePlayer->SetTimeOfLastTeleport(gpGlobals->time);
					
					int theFlags = 0;//thePlayer ? FEV_NOTHOST : 0;
					this->SetTimeOfLastDeparture(gpGlobals->time);
					AvHSUPlayPhaseInEffect(theFlags, this, thePlayer);

					// AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev);
					AvHSUPushbackPlayersTouchingPlayer(thePlayer, this->pev);
                    KillBuildablesTouchingPlayer(thePlayer, this->pev);
					
					Vector theFadeColor;
					theFadeColor.x = 235;
					theFadeColor.y = 255;
					theFadeColor.z = 255;
					UTIL_ScreenFade(thePlayer, theFadeColor, .9f, 0.0f, 255, FFADE_IN);

    			}
			}
		}
	}
}

void AvHPhaseGate::UpdateOnRecycle(void)
{
	this->SetEnabled(false);
}

void AvHPhaseGate::UpdateOnRemove(void)
{
	// Make sure sound gets turned off when round ends without it being killed
	if(this->pev)
	{
		UTIL_EmitAmbientSound(ENT(this->pev), this->pev->origin, kPhaseGateSound, 1.0f, .5, SND_STOP, 100);
	}
}


//AvHSiegeTurret::AvHSiegeTurret()
//{
//	float theStartTime = RANDOM_FLOAT(0, avh_siegerof.value);
//	this->mTimeLastFired = gpGlobals->time - theStartTime;
//	this->mTimeToConstruct = GetGameRules()->GetBuildTimeForMessageID(BUILD_SIEGE);
//}
//
//CBaseEntity* AvHSiegeTurret::BestVisibleEnemy(void)
//{
//	CBaseEntity *theCurrentEnemy = NULL;
//	CBaseEntity *theBestEnemy = NULL;
//	float theDistanceToBestEnemy = -1;
//	
//	while((theCurrentEnemy = UTIL_FindEntityInSphere(theCurrentEnemy, this->pev->origin, avh_siegemaxrange.value)) != NULL)
//	{
//		// If entity is a valid target and within our valid range
//		if(this->GetIsValidTarget(theCurrentEnemy))
//		{
//			// Is it closer than our current target?
//			float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, theCurrentEnemy);
//			if((theDistanceToBestEnemy == -1) || (theDistanceToCurrentEnemy < theDistanceToBestEnemy))
//			{
//				theBestEnemy = theCurrentEnemy;
//				theDistanceToBestEnemy = theDistanceToCurrentEnemy;
//			}
//		}
//	}
//	return theBestEnemy;
//}
//
//bool AvHSiegeTurret::GetIsValidTarget(CBaseEntity* inEntity) const
//{
//	bool theTargetIsValid = false;
//	
//	if(AvHDeployedTurret::GetIsValidTarget(inEntity))
//	{
//		float theDistanceToCurrentEnemy = AvHSUEyeToBodyDistance(this->pev, inEntity);
//		if(theDistanceToCurrentEnemy >= this->GetMinimumRange())
//		{
//			// We have to see it as well
//			Vector vecMid = this->pev->origin + this->pev->view_ofs;
//			Vector vecMidEnemy = inEntity->BodyTarget(vecMid);
//			if(FBoxVisible(this->pev, inEntity->pev, vecMidEnemy))
//			{
//				theTargetIsValid = true;
//			}
//		}
//	}
//	return theTargetIsValid;
//}
//
//char* AvHSiegeTurret::GetActiveSound() const
//{
//	return kSiegeActive;
//}
//
//char* AvHSiegeTurret::GetAlertSound() const
//{
//	return kSiegeAlert;
//}
//
//char* AvHSiegeTurret::GetDeploySound() const
//{
//	return kSiegeDeploy;
//}
//
//char* AvHSiegeTurret::GetPingSound() const
//{
//	return kSiegePing;
//}
//
//int AvHSiegeTurret::GetPointValueOfTarget(void) const
//{
//	return 3;
//}
//
//int	AvHSiegeTurret::GetMinimumRange() const
//{
//	return avh_siegeminrange.value;
//}
//
//bool AvHSiegeTurret::NeedsLineOfSight() const
//{
//	return true;
//}
//
//void AvHSiegeTurret::Precache(void)
//{
//	AvHDeployedTurret::Precache();
//	
//	PRECACHE_MODEL(kSiegeTurretModel);
//	PRECACHE_SOUND(kSiegeTurretFire1);
//}
//
//
//void AvHSiegeTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy)
//{
//	// Only fire once every few seconds...this is hacky but there's no way to override think functions so it must be done
//	// I wish it was easier to change the update rate but it's not so...
//	if((gpGlobals->time - this->mTimeLastFired) > avh_siegerof.value)
//	{
//		// Find enemy player in range, ignore walls and everything else
//		if(this->m_hEnemy)
//		{
//			if(this->GetIsValidTarget(this->m_hEnemy))
//			{
//				// Apply damage, taking upgrade into account
//				float theDamageMultiplier;
//				AvHPlayerUpgrade::GetWeaponUpgrade(this->pev->iuser4, &theDamageMultiplier);
//				float theDamage = theDamageMultiplier*avh_siegedamage.value;
//				
//				if(!GetGameRules()->GetIsTesting())
//				{
//					::RadiusDamage(this->m_hEnemy->pev->origin, this->pev, this->pev, theDamage, avh_siegesplashradius.value, CLASS_NONE, DMG_BLAST);
//				}
//				
//				// Play fire sound
//				EMIT_SOUND_DYN(ENT(this->pev), CHAN_AUTO, kSiegeTurretFire1, 1.0, ATTN_NORM, 0, PITCH_NORM);
//				
//				this->pev->effects &= ~EF_MUZZLEFLASH;
//				
//				// Send normal effect to all
//				PLAYBACK_EVENT_FULL(0, this->m_hEnemy->edict(), gSiegeHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
//				
//				if(this->m_hEnemy->IsPlayer())
//				{
//					// Send personal view shake to recipient only (check for splash here, pass param to lessen effect for others?)
//					// TODO: Use upgrade level to parameterize screen shake and fade?
//					PLAYBACK_EVENT_FULL(FEV_HOSTONLY, this->m_hEnemy->edict(), gSiegeViewHitEventID, 0, this->m_hEnemy->pev->origin, this->m_hEnemy->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );
//					
//					Vector theFadeColor;
//					theFadeColor.x = 255;
//					theFadeColor.y = 100;
//					theFadeColor.z = 100;
//					UTIL_ScreenFade(this->m_hEnemy, theFadeColor, .3f, 0.0f, 255, FFADE_OUT);
//				}
//			}
//			else
//			{
//				this->m_hEnemy = NULL;
//			}
//		}
//
//		this->mTimeLastFired = gpGlobals->time;
//	}
//}
//
//void AvHSiegeTurret::Spawn()
//{
//	this->Precache();
//	AvHDeployedTurret::Spawn();
//	
//	SET_MODEL(ENT(this->pev), kSiegeTurretModel);
//	UTIL_SetSize(pev, kSiegeMinSize, kSiegeMaxSize); 
//	
//    this->pev->classname = MAKE_STRING(kwsSiegeTurret);
//	
//	this->pev->takedamage = 1;
//	this->pev->health = (int)(avh_siegehealth.value);
//	this->pev->armorvalue = (int)(avh_siegehealth.value);
//}



AvHMarineBaseBuildable::AvHMarineBaseBuildable(AvHTechID inTechID, AvHMessageID inMessageID, char* inClassName, int inUser4) : AvHBaseBuildable(inTechID, inMessageID, inClassName, inUser4)
{
	this->SetEnergy(0.0f);
}

char* AvHMarineBaseBuildable::GetDeploySound() const
{
	return kMarineBuildingDeploy;
}

bool AvHMarineBaseBuildable::GetIsTechnologyAvailable(AvHMessageID inMessageID) const
{
	bool theIsAvailable = AvHBaseBuildable::GetIsTechnologyAvailable(inMessageID);
	
	// Disable scanning if not enough energy
	if(theIsAvailable && AvHSHUGetDoesTechCostEnergy(inMessageID))
	{
		int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID);
		if(this->mEnergy < theEnergyCost)
		{
			theIsAvailable = false;
		}
	}
	
	return theIsAvailable;
}

char* AvHMarineBaseBuildable::GetKilledSound() const
{
	return kMarineBuildingKilled;
}

int	AvHMarineBaseBuildable::GetPointValue() const
{
	return BALANCE_VAR(kScoringMarineBuildableValue);
}

int AvHMarineBaseBuildable::GetTakeDamageAnimation() const
{
    return -1;
}

void AvHMarineBaseBuildable::ResetEntity()
{
	AvHBaseBuildable::ResetEntity();
}

void AvHMarineBaseBuildable::SetEnergy(float inEnergy)
{
	this->mEnergy = max(min(inEnergy, kMarineStructureMaxEnergy), 0.0f);
	
	float theNormValue = this->mEnergy/kMarineStructureMaxEnergy;
	bool theIsResearching=false;

	const AvHTeam* theTeam = GetGameRules()->GetTeam(AvHTeamNumber(this->GetTeamNumber()));

	if ( theTeam ) {
		theIsResearching=theTeam->GetResearchManager().GetIsResearching(this->entindex());
	}
	if(this->pev && this->GetIsBuilt() && (!theIsResearching))
	{
		AvHSHUSetEnergyState(this->pev->iuser3, this->pev->fuser1, theNormValue);
	}
}

int AvHMarineBaseBuildable::TakeDamage(entvars_t* inInflictor, entvars_t* inAttacker, float inDamage, int inBitsDamageType)
{
	//UTIL_Sparks()

	return AvHBaseBuildable::TakeDamage(inInflictor, inAttacker, inDamage, inBitsDamageType);
}

void AvHMarineBaseBuildable::TechnologyBuilt(AvHMessageID inMessageID)
{
	// Pay energy cost
	if(AvHSHUGetDoesTechCostEnergy(inMessageID))
	{
		int theEnergyCost = GetGameRules()->GetCostForMessageID(inMessageID);
		float theNewEnergy = max(this->mEnergy - theEnergyCost, 0.0f);
		this->SetEnergy(theNewEnergy);

		// Play animation?
		this->PlayAnimationAtIndex(this->GetActiveAnimation(), true);
	}
}

// Marine buildings
const float kNukeThinkInterval = 1.5f;

AvHNuke::AvHNuke() : AvHMarineBaseBuildable(TECH_NULL, BUILD_NUKE, kwsNuke, AVH_USER3_NUKE)
{
}

void AvHNuke::Precache()
{
	AvHMarineBaseBuildable::Precache();
	PRECACHE_UNMODIFIED_SOUND(kNukeActive);
	PRECACHE_UNMODIFIED_SOUND(kNukeExplode);
}

void AvHNuke::ActiveThink()
{
	float theThinkInterval = kNukeThinkInterval;

	// If not active
	if(!this->mActive)
	{
		// Set construction complete
		this->SetConstructionComplete();

		// Play sounds

		// Set active
		this->mActive = true;

		// Mark as a monster
		SetBits(this->pev->flags, FL_MONSTER);

		this->mTimeActivated = gpGlobals->time;
	}
	
	// Emit higher and higher frequency sound
	float theTimeToDetonate = GetGameRules()->GetBuildTimeForMessageID(this->GetMessageID());
	
	float thePercentDone = (gpGlobals->time - this->mTimeActivated)/theTimeToDetonate;
	thePercentDone = min(max(0.0f, thePercentDone), 1.0f);
	
	int thePitch = 100 + thePercentDone*3;
	EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, kNukeActive, 1.0, ATTN_NORM, 0, thePitch);
	
	// If active
	if(this->mActive)
	{
		// If enough time has passed
		ASSERT(this->mTimeActivated != -1);
		
		if(gpGlobals->time > (this->mTimeActivated + theTimeToDetonate))
		{
			// Detonation visuals (any bigger magnitude then this and the explosion doesn't show up)
			int theMagnitude = 300;
			ExplosionCreate(this->pev->origin, this->pev->angles, this->edict(), theMagnitude, FALSE, this->pev->team);
			
			// Do damage (theDamage at epicenter, falls off from there, doesn't hurt people in water, only hurts people that can be seen by blast)
			float theDamage = kNukeDamage;
			float theRadius = kNukeRange;
			::RadiusDamage(this->pev->origin, this->pev, this->pev, theDamage, theRadius, CLASS_NONE, DMG_BLAST);
			
			// Play view shake here
			float theShakeAmplitude = 100;
			float theShakeFrequency = 150;
			float theShakeDuration = 10.0f;
			float theShakeRadius = 2000;
			UTIL_ScreenShake(this->pev->origin, theShakeAmplitude, theShakeFrequency, theShakeDuration, theShakeRadius);
			
			// Add white out effect for players in radius
			FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
				if(theEntity->GetIsRelevant() && !theEntity->GetIsInTopDownMode())
				{
					// If near the epicenter, or if it's visible, we're blinded
					bool theNearEpicenter = false;
					float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin);
					if(theDistance < theRadius/2)
					{
						theNearEpicenter = true;
					}

					bool theExplosionVisible = false;
					if(theEntity->GetIsEntityInSight(this))
					{
						theExplosionVisible = true;
					}

					if(theNearEpicenter || theExplosionVisible)
					{
						Vector theFadeColor;
						theFadeColor.x = 255;
						theFadeColor.y = 255;
						theFadeColor.z = 255;
						UTIL_ScreenFade(theEntity, theFadeColor, 1.5f, .5f, 255, FFADE_IN);
					}
				}
			END_FOR_ALL_ENTITIES(kAvHPlayerClassName)

			//this->pev->flags |= EF_NODRAW;
			SET_MODEL(ENT(pev), kNullModel);
			this->pev->flags |= EF_BRIGHTFIELD;
			this->pev->flags |= EF_BRIGHTLIGHT;
				
			// Remove entity
			SetThink(&AvHNuke::DeathThink);
			theThinkInterval = theShakeDuration;

			// Play special nuke sound (not playing for some reason)
			EMIT_SOUND(this->edict(), CHAN_BODY, kNukeExplode, 1.0f, ATTN_IDLE);
		}
	}
	
	this->pev->nextthink = gpGlobals->time + theThinkInterval;
}

void AvHNuke::DeathThink()
{
	UTIL_Remove(this);
}

void AvHNuke::Spawn()
{
	AvHBaseBuildable::Spawn();
	
	// Set ActiveThink
	this->mActive = false;
	this->mTimeActivated = -1;

	SetThink(&AvHNuke::ActiveThink);
	this->pev->nextthink = gpGlobals->time + kNukeThinkInterval;
}

char* AvHNuke::GetDeploySound() const
{
	return kNukeDeploy;
}

char* AvHNuke::GetKilledSound() const
{
	return kNukeKilled;
}


const float kInfantryPortalThinkTime = 1.0f;
#define kInfantryPortalLightEffect EF_LIGHT

AvHInfantryPortal::AvHInfantryPortal() : AvHMarineBaseBuildable(TECH_INFANTRYPORTAL, BUILD_INFANTRYPORTAL, kwsInfantryPortal, AVH_USER3_INFANTRYPORTAL)
{
}

void AvHInfantryPortal::Killed(entvars_t* inAttacker, int inGib)
{
	AvHBaseBuildable::Killed(inAttacker, inGib);

	GetGameRules()->MarkDramaticEvent(kIPDeathPriority, this->entindex(), inAttacker);
}

float AvHInfantryPortal::GetReinforceTime() const
{
	float theReinforceTime = BALANCE_VAR(kMarineRespawnTime);

	if(GetGameRules()->GetCheatsEnabled())
	{
		theReinforceTime = 2;
	}

	theReinforceTime = max(0.0f, theReinforceTime);

	return theReinforceTime;
}

void AvHInfantryPortal::PortalThink()
{
	this->UpdateReinforcements();

	AvHBaseBuildable::AnimateThink();

	this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; 
}


void AvHInfantryPortal::ResetEntity()
{
	AvHMarineBaseBuildable::ResetEntity();
	AvHReinforceable::ResetEntity();
}

void AvHInfantryPortal::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	SetThink(&AvHInfantryPortal::PortalThink);
	this->pev->nextthink = gpGlobals->time + kInfantryPortalThinkTime; 
	// this->pev->effects |= kInfantryPortalLightEffect;
}

void AvHInfantryPortal::Precache()
{
	AvHMarineBaseBuildable::Precache();

	PRECACHE_UNMODIFIED_SOUND(kTransportSound);
}


void AvHInfantryPortal::CueRespawnEffect(AvHPlayer* inPlayer)
{
	// Playback teleporting event
	PLAYBACK_EVENT_FULL(0, inPlayer->edict(), gTeleportEventID, 0, inPlayer->pev->origin, inPlayer->pev->angles, 0.0, 0.0, /*theWeaponIndex*/ 0, 0, 0, 0 );

	EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kTransportSound, .8, ATTN_NORM);
}

bool AvHInfantryPortal::GetCanReinforce() const
{
	return this->GetIsBuilt() && !GetGameRules()->GetIsCombatMode();
}

bool AvHInfantryPortal::GetSpawnLocationForPlayer(CBaseEntity* inPlayer, Vector& outLocation) const
{
	// Set player position to on top of gate
	vec3_t thePosition = AvHSHUGetRealLocation(this->pev->origin, this->pev->mins, this->pev->maxs);

	Vector theIPMinSize, theIPMaxSize;
	AvHSHUGetSizeForTech(BUILD_INFANTRYPORTAL, theIPMinSize, theIPMaxSize);
	thePosition.z += theIPMaxSize.z;

	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inPlayer);
	ASSERT(thePlayer);

	// Respawn player on top of portal
	Vector thePlayerMinSize, thePlayerMaxSize;						
	thePlayer->GetSize(thePlayerMinSize, thePlayerMaxSize);

	thePosition.z += (-thePlayerMinSize.z + kRespawnFudgeFactorHeight);
	
	VectorCopy(thePosition, outLocation);
	
	return true;
}

AvHTeamNumber AvHInfantryPortal::GetReinforceTeamNumber() const
{
	return this->GetTeamNumber();
}

void AvHInfantryPortal::ResetReinforcingPlayer(bool inSuccess)
{
	// If we're respawning, we telefrag.  Make sure to telefrag after the player's new location has been set
	bool theTelefrag = false;
	AvHPlayer* thePlayer = this->GetReinforcingPlayer();
	if(inSuccess && thePlayer)
	{
		theTelefrag = true;
	}

	AvHReinforceable::ResetReinforcingPlayer(inSuccess);

	if(theTelefrag)
	{
		//AvHSUKillPlayersTouchingPlayer(thePlayer, this->pev);
		AvHSUPushbackPlayersTouchingPlayer(thePlayer, this->pev);
	}
}

void AvHInfantryPortal::UpdateOnRecycle(void)
{
	this->ResetReinforcingPlayer(false);
	// this->pev->effects &= ~kInfantryPortalLightEffect;
}

void AvHInfantryPortal::UpdateOnRemove(void)
{
	this->ResetReinforcingPlayer(false);
	SetThink(NULL);
}

int AvHInfantryPortal::GetIdleAnimation() const
{
	// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
	return 2;
}	

int AvHInfantryPortal::GetIdle1Animation() const
{
	// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
	return 2;
}	

int AvHInfantryPortal::GetIdle2Animation() const
{
	// AvHBaseBuildable randomly shows Idle2, which we don't have in this model.
	return 2;
}	

// : Uncomment for the new IP from Alpha
//int AvHInfantryPortal::GetDeployAnimation() const
//{
//	return 0;
//}

//int AvHInfantryPortal::GetSpawnAnimation() const
//{
//	return 1;
//}
// :

const int kCommandStationExitAnimation = 12;

AvHCommandStation::AvHCommandStation() : AvHMarineBaseBuildable(TECH_COMMAND_CENTER, BUILD_COMMANDSTATION, kwsTeamCommand, AVH_USER3_COMMANDER_STATION)
{
	this->mCommanderAtThisStation = -1;
	this->mTimeToPlayOnlineSound = -1;
}

int	AvHCommandStation::GetIdleAnimation() const
{
	int theAnimation = 2;

	// If we're in use
	if(this->mCommanderAtThisStation != -1 || GetGameRules()->GetIsCombatMode())
	{
		theAnimation = 3;
	}

	return theAnimation;
}

int AvHCommandStation::GetPointValue() const
{
	return BALANCE_VAR(kScoringCCValue);
}

void AvHCommandStation::Killed( entvars_t *pevAttacker, int iGib )
{
    this->SetInactive();

    AvHMarineBaseBuildable::Killed(pevAttacker, iGib);
}

int	AvHCommandStation::ObjectCaps( void ) 
{ 
	
    // Recycled command stations cannot be used.

    int theObjectCaps = 0;
    
    if ( !(pev->effects & EF_NODRAW) )
    {
        theObjectCaps |= FCAP_CONTINUOUS_USE;
    }

    return theObjectCaps;

}

void AvHCommandStation::CommandTouch(CBaseEntity* pOther)
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pOther);
	if(thePlayer)
	{
		AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
		if(thePlayer->pev->team == theStationTeamNumber)
		{
			// Check to see if we already have a commander and don't make the person a commander if so
			if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
			{
				thePlayer->SendMessage(kHelpTextCSAttractMode, TOOLTIP);
			}
		}
	}
}

void AvHCommandStation::CommandUse( CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value )
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pActivator);

	// Mapper-placed CCs can be killed but they don't go away
	if(thePlayer && !(thePlayer->pev->flags & FL_FAKECLIENT) && !this->GetHasBeenKilled() && thePlayer->GetIsAbleToAct())
	{
		AvHTeam* theTeam = thePlayer->GetTeamPointer();
		if(theTeam && (theTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE))
		{
			AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
			if(thePlayer->pev->team == theStationTeamNumber)
			{
				// Check to see if we already have a commander and don't make the person a commander if so
				if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
				{
					string theErrorMessage;
					if(thePlayer->GetCanCommand(theErrorMessage))
					{
						if(this->pev->health > 0 && !this->GetIsRecycling())
						{
							thePlayer->SetUser3(AVH_USER3_COMMANDER_PLAYER);
							//thePlayer->SetSelection(this->entindex());
						
							EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationStartSound, .8, kCommandStationAttenuation);
						
							SetThink(&AvHCommandStation::CommanderUsingThink);
							this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval;
							
							const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationUseTeamOne : kTargetCommandStationUseTeamTwo;
							FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f);
						
							this->mCommanderAtThisStation = thePlayer->entindex();

							this->PlayAnimationAtIndex(5, true);
							
							this->mTimeToPlayOnlineSound = this->GetTimeAnimationDone();

							GetGameRules()->MarkDramaticEvent(kCCNewCommanderPriority, thePlayer, this);
						}
						else
						{
							thePlayer->SendMessage(kCommandStationDestroyed, TOOLTIP);
						}
					}
					else
					{
						thePlayer->SendMessage(theErrorMessage.c_str());
					}
				}
				else
				{
					// The player somehow touches the command station while still a commander
					if(thePlayer->GetUser3() != AVH_USER3_COMMANDER_PLAYER)
					{
						thePlayer->SendMessage(kCommandStationInUse, TOOLTIP);
					}
				}
			}
			else
			{
				thePlayer->SendMessage(kCommandStationForOtherTeam, TOOLTIP);
			}
		}
	}
}

void AvHCommandStation::Precache(void)
{
	AvHMarineBaseBuildable::Precache();

	PRECACHE_UNMODIFIED_MODEL(kCommandStationModel);
	PRECACHE_UNMODIFIED_SOUND(kCommandStationStartSound);
	PRECACHE_UNMODIFIED_SOUND(kCommandStationEndSound);
}

void AvHCommandStation::ActivateThink(void)
{
	// Don't allow use of the Command station in Combat mode
	if(!GetGameRules()->GetIsCombatMode())
	{
		SetTouch(&AvHCommandStation::CommandTouch);
		SetUse(&AvHCommandStation::CommandUse);
	}
	
	SetThink(&AvHBaseBuildable::AnimateThink);
	this->pev->nextthink = gpGlobals->time + .1f;
}

void AvHCommandStation::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	SetThink(&AvHCommandStation::ActivateThink);
	this->pev->nextthink = gpGlobals->time + 2.0f;
}

void AvHCommandStation::EjectCommander()
{
	// If this command station had a commander, log him out!
	if(this->mCommanderAtThisStation != -1)
	{
		// if the command station is killed, kick out any commander
		FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
			if(theEntity->pev->team == this->pev->team)
			{
				if(theEntity->GetUser3() == AVH_USER3_COMMANDER_PLAYER)
				{
					theEntity->StopTopDownMode();
					theEntity->SetUser3(AVH_USER3_MARINE_PLAYER);
					
					this->PlayAnimationAtIndex(kCommandStationExitAnimation, true);

					GetGameRules()->MarkDramaticEvent(kCCEjectPriority, theEntity, this);

					break;
				}
			}
			END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
	}
	
	this->mCommanderAtThisStation = -1;
}

char* AvHCommandStation::GetKilledSound() const
{
	return kCommandStationDeathSound;
}


void AvHCommandStation::SetInactive()
{
	AvHBaseBuildable::SetInactive();

	this->EjectCommander();
}

void AvHCommandStation::Materialize()
{
	AvHMarineBaseBuildable::Materialize();

	this->mCommanderAtThisStation = -1;
	
	//this->ResetEntity();
}

void AvHCommandStation::Spawn(void)
{
	AvHMarineBaseBuildable::Spawn();

	this->Materialize();
}

int	AvHCommandStation::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
{
	int theReturnCode = 0;

	if(this->pev->health > 0)
	{
		theReturnCode = AvHBaseBuildable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);

		if(this->pev->health <= 0)
		{
			AvHTeamNumber theStationTeamNumber = (AvHTeamNumber)this->pev->team;
			const char* theTarget = (theStationTeamNumber == TEAM_ONE) ? kTargetCommandStationDestroyedTeamOne : kTargetCommandStationDestroyedTeamTwo;
			FireTargets(theTarget, NULL, NULL, USE_TOGGLE, 0.0f);

			GetGameRules()->MarkDramaticEvent(kCCDeathPriority, this->entindex(), false, pevAttacker);
		}
	}
	
	return theReturnCode;
}


void AvHCommandStation::CommanderUsingThink(void)
{
	AvHTeamNumber theStationTeamNumber = this->GetTeamNumber();
	
	// Check to see if we already have a commander and don't make the person a commander if so
	if(GetGameRules()->GetNumCommandersOnTeam(theStationTeamNumber) == 0)
	{
		this->mCommanderAtThisStation = -1;
		this->mTimeToPlayOnlineSound = -1;
		
		EMIT_SOUND(ENT(this->pev), CHAN_AUTO, kCommandStationEndSound, .8, kCommandStationAttenuation);
		this->PlayAnimationAtIndex(kCommandStationExitAnimation, true);

		//SetThink(NULL);
		SetThink(&AvHBaseBuildable::AnimateThink);
		this->pev->nextthink = gpGlobals->time + .1f;
	}
	else
	{
		if(this->mTimeToPlayOnlineSound > 0.0f)
		{
			if(gpGlobals->time > this->mTimeToPlayOnlineSound)
			{
				ASSERT(this->mCommanderAtThisStation > 0);
				
				// If enough time has passed and we haven't played online sound, play it
				AvHPlayer* theCommander = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(g_engfuncs.pfnPEntityOfEntIndex(this->mCommanderAtThisStation)));
				if(theCommander)
				{
					theCommander->PlayHUDSound(HUD_SOUND_MARINE_CCONLINE);
				}
				
				this->mTimeToPlayOnlineSound = -1;
			}
		}
	
		AvHBaseBuildable::AnimateThink();
		
		this->pev->nextthink = gpGlobals->time + kCommandStationThinkInterval;
	}
}

bool AvHCommandStation::GetIsTechnologyAvailable(AvHMessageID inMessageID) const
{
    bool theTechIsAvailable = AvHMarineBaseBuildable::GetIsTechnologyAvailable(inMessageID);

    // Only allow CC to be recycled if it's unoccupied
    if(inMessageID == BUILD_RECYCLE)
    {
        theTechIsAvailable = (this->mCommanderAtThisStation == -1);
    }

    return theTechIsAvailable;
}

AvHTurretFactory::AvHTurretFactory() : AvHMarineBaseBuildable(TECH_TURRET_FACTORY, BUILD_TURRET_FACTORY, kwsTurretFactory, AVH_USER3_TURRET_FACTORY)
{
}

void AvHTurretFactory::CheckTurretEnabledState() const
{
	// Check to see if turrets should come online
	FOR_ALL_ENTITIES(kwsDeployedTurret, AvHMarineTurret*)
	if(theEntity->pev->team == this->pev->team)
	{
		theEntity->CheckEnabledState();
	}
	END_FOR_ALL_ENTITIES(kwsDeployedTurret);
		
	FOR_ALL_ENTITIES(kwsSiegeTurret, AvHSiegeTurret*)
	if(theEntity->pev->team == this->pev->team)
	{
		theEntity->CheckEnabledState();
	}
	END_FOR_ALL_ENTITIES(kwsSiegeTurret)
}

void AvHTurretFactory::TriggerAddTech() const
{
	AvHBuildable::TriggerAddTech();

	this->CheckTurretEnabledState();
}

void AvHTurretFactory::TriggerRemoveTech() const
{
	AvHBuildable::TriggerRemoveTech();

	this->CheckTurretEnabledState();
}

int	AvHTurretFactory::GetIdle1Animation() const
{
	int theAnimation = 3;

	// Different animation when electrified
	if(GetHasUpgrade(this->pev->iuser4, MASK_UPGRADE_11))
	{
		theAnimation = 12;
	}

	return theAnimation;
}

int	AvHTurretFactory::GetIdle2Animation() const
{
	return this->GetIdle1Animation();
}

int AvHTurretFactory::GetResearchAnimation() const
{
	return 4;
}

void AvHTurretFactory::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	// Animate
	SetThink(&AvHBaseBuildable::AnimateThink);
	this->pev->nextthink = gpGlobals->time + .1f;
}

bool AvHTurretFactory::GetSupportsTechID(AvHTechID	inTechID) const
{
	bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID);
	
	if(this->GetTechID() == TECH_ADVANCED_TURRET_FACTORY)
	{
		if(!theSuccess)
		{
			// Adv. turret factory also counts as a turret factory
			theSuccess = (inTechID == TECH_TURRET_FACTORY);
		}
	}
	
	return theSuccess;
}

void AvHTurretFactory::Upgrade()
{
	// Set iuser3
	this->pev->iuser3 = AVH_USER3_ADVANCED_TURRET_FACTORY;

	// Set classname
	this->pev->classname = MAKE_STRING(kwsAdvancedTurretFactory);

	this->mMessageID = TURRET_FACTORY_UPGRADE;
	this->SetTechID(TECH_ADVANCED_TURRET_FACTORY);

	this->SetSelectID(this->pev->iuser3);

	this->SetConstructionComplete(true);

	this->HealthChanged();
}


AvHArmory::AvHArmory() : AvHMarineBaseBuildable(TECH_ARMORY, BUILD_ARMORY, kwsArmory, AVH_USER3_ARMORY)
{
}

int	AvHArmory::GetSequenceForBoundingBox() const
{
	return 2;
}

bool AvHArmory::GetSupportsTechID(AvHTechID	inTechID) const
{
	bool theSuccess = AvHBuildable::GetSupportsTechID(inTechID);

	if(this->GetTechID() == TECH_ADVANCED_ARMORY)
	{
		// Adv. armory also counts as an armory
		if(!theSuccess)
		{
			theSuccess = (inTechID == TECH_ARMORY);
		}
	}

	return theSuccess;
}

int	AvHArmory::GetIdle1Animation() const
{
	int theAnim = 2;
	if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY)
	{
		theAnim = 3;
	}
	return theAnim;
}

int	AvHArmory::GetIdle2Animation() const
{
	return this->GetIdle1Animation();
}

int	AvHArmory::GetActiveAnimation() const
{
	int theAnim = 5;
	if(this->pev->iuser3 == AVH_USER3_ADVANCED_ARMORY)
	{
		theAnim = 12;
	}
	return theAnim;
}

int	AvHArmory::GetResearchAnimation() const
{
	return 5;
}

void AvHArmory::Precache()
{
	AvHMarineBaseBuildable::Precache();
	
	PRECACHE_UNMODIFIED_SOUND(kArmoryResupplySound);
}

void AvHArmory::ResupplyUse(CBaseEntity* inActivator, CBaseEntity* inCaller, USE_TYPE inUseType, float inValue)
{
	AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(inCaller);
	
	if(thePlayer && (thePlayer->pev->team == this->pev->team) && this->GetIsBuilt() && !this->GetIsRecycling() && thePlayer->GetIsAbleToAct())
	{
        if(thePlayer->GetCanBeResupplied())
        {
			// : 1017
//            // Give health back occasionally
//            bool theGiveHealthIfNeeded = (RANDOM_LONG(0, 3) == 0);
//            
			// resupply gives 10 health each use
			thePlayer->Resupply(true);		

            // Always play "getting ammo" sound when ammo or health are needed, to indicate to player when to stop pressing +use
            EMIT_SOUND(thePlayer->edict(), CHAN_WEAPON, kArmoryResupplySound, .3f, ATTN_NORM);
        }
	}
}

void AvHArmory::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	SetUse(&AvHArmory::ResupplyUse);
}

void AvHArmory::Upgrade()
{
	// Set iuser3
	this->pev->iuser3 = AVH_USER3_ADVANCED_ARMORY;

	// Set classname
	this->pev->classname = MAKE_STRING(kwsAdvancedArmory);

	this->mMessageID = ARMORY_UPGRADE;
	this->SetTechID(TECH_ADVANCED_ARMORY);

	this->SetSelectID(this->pev->iuser3);

	this->SetConstructionComplete(true);

	this->HealthChanged();
}


AvHArmsLab::AvHArmsLab() : AvHMarineBaseBuildable(TECH_ARMSLAB, BUILD_ARMSLAB, kwsArmsLab, AVH_USER3_ARMSLAB)
{
}

int	AvHArmsLab::GetResearchAnimation() const
{
	return 5;
}

int	AvHArmsLab::GetSequenceForBoundingBox() const
{
	return 1;
}

AvHPrototypeLab::AvHPrototypeLab() : AvHMarineBaseBuildable(TECH_PROTOTYPE_LAB, BUILD_PROTOTYPE_LAB, kwsPrototypeLab, AVH_USER3_PROTOTYPE_LAB)
{
}


const float kObservatoryThinkTime = 1.0f;
const float kObservatoryStartEnergy = 40;

AvHObservatory::AvHObservatory() : AvHMarineBaseBuildable(TECH_OBSERVATORY, BUILD_OBSERVATORY, kwsObservatory, AVH_USER3_OBSERVATORY)
{
}

int	AvHObservatory::GetSequenceForBoundingBox() const
{
	return 1;
}

void AvHObservatory::ObservatoryThink()
{
	// Remove cloaking from nearby enemies
	if ( !this->GetIsRecycling() ) {
		FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
		if(theEntity->GetIsRelevant() && (theEntity->pev->team != this->pev->team))
		{
			// Check that entity is in range of scan
			float theDistance = VectorDistance(theEntity->pev->origin, this->pev->origin);
			if(theDistance < BALANCE_VAR(kObservatoryXYDetectionRadius))
			{
				// Remove cloaking, if player has it
				theEntity->TriggerUncloak();
			}
		}
		END_FOR_ALL_ENTITIES(kAvHPlayerClassName)
	}

	AvHBaseBuildable::AnimateThink();

	// Update scanning energy
	float theRate = kMarineStructureEnergyRate;
	if(GetGameRules()->GetCheatsEnabled() && !GetGameRules()->GetIsCheatEnabled(kcSlowResearch))
	{
		theRate *= 6;
	}

	this->SetEnergy(this->mEnergy + (kObservatoryThinkTime*theRate));

	this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime;
}

void AvHObservatory::Materialize()
{
	AvHMarineBaseBuildable::Materialize();
}

void AvHObservatory::ResetEntity()
{
	AvHMarineBaseBuildable::ResetEntity();

	this->SetEnergy(0);
}

void AvHObservatory::SetHasBeenBuilt()
{
	AvHBuildable::SetHasBeenBuilt();

	SetThink(&AvHObservatory::ObservatoryThink);
	this->pev->nextthink = gpGlobals->time + kObservatoryThinkTime;

	this->SetEnergy(kObservatoryStartEnergy);
}

void AvHObservatory::Spawn()
{
	AvHMarineBaseBuildable::Spawn();
}

int	AvHObservatory::GetActiveAnimation() const
{
	return 2;
}

int	AvHObservatory::GetIdle1Animation() const
{
	return 3;
}

int	AvHObservatory::GetIdle2Animation() const
{
	return 3;
}

int	AvHObservatory::GetResearchAnimation() const
{
	return 4;
}