//======== (C) Copyright 2002 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: AvHSharedUtil.cpp $
// $Date: 2002/11/12 22:39:25 $
//
//-------------------------------------------------------------------------------
// $Log: AvHSharedUtil.cpp,v $
// Revision 1.33  2002/11/12 22:39:25  Flayra
// - Logging changes for Psychostats compatibility
//
// Revision 1.32  2002/10/24 21:42:32  Flayra
// - Don't give waypoint to selected troops when scanning
//
// Revision 1.31  2002/10/19 21:19:49  Flayra
// - Debugging info for linux
//
// Revision 1.30  2002/10/16 01:07:26  Flayra
// - Added official sizes that HL supports (ugh)
// - Added utility function for drawing range of ghost building, but it's unused
//
// Revision 1.29  2002/10/03 19:07:40  Flayra
// - Hives can never be blocked by func_resources
//
// Revision 1.28  2002/09/25 20:50:58  Flayra
// - Allow small items to be built on entities (health can be dropped right on players)
//
// Revision 1.27  2002/09/23 22:31:40  Flayra
// - Updated team 0 colors so dictation can be read in readyroom
// - Draw range for prototype lab, observatory and sensory chamber
// - Alien building rings
// - Don't allow non-resource buildings built too close to func_resources
// - Added heavy armor and jetpacks
//
// Revision 1.26  2002/09/09 20:06:43  Flayra
// - New observatory artwork
//
// Revision 1.25  2002/08/31 18:01:03  Flayra
// - Work at VALVe
//
// Revision 1.24  2002/08/16 02:47:06  Flayra
// - Fixed bug where not all the entities were iterated through
// - Support for ring-drawing (but not for all entities)
//
// Revision 1.23  2002/08/09 00:51:11  Flayra
// - Cleaned up useless special casing of tracetangible, fixed some problems where buildings could be built on players
//
// Revision 1.22  2002/08/02 21:50:05  Flayra
// - Made more general, by detecting all entities, not just players
//
// Revision 1.21  2002/07/24 18:45:43  Flayra
// - Linux and scripting changes
//
// Revision 1.20  2002/07/23 17:26:49  Flayra
// - Siege don't require a nearby turret factory, only add built buildings for range detection, build problems on client (point contents failing on rough surfaces)
//
// Revision 1.19  2002/07/08 17:17:44  Flayra
// - Reworked colors, moved functions into server util
//
// Revision 1.18  2002/07/01 21:46:39  Flayra
// - Added support for generic building ranges, fixed morphing problems
//
// Revision 1.17  2002/06/25 18:18:00  Flayra
// - Renamed buildings, better is-area-free detection
//
// Revision 1.16  2002/06/03 16:58:13  Flayra
// - Renamed weapons factory and armory, hive is now a buildable
//
// Revision 1.15  2002/05/28 18:13:43  Flayra
// - Changed "entity blocked by whatever" message to be logged for PR/screenshot purposes
//
// Revision 1.14  2002/05/23 02:33:20  Flayra
// - Post-crash checkin.  Restored @Backup from around 4/16.  Contains changes for last four weeks of development.
//
//===============================================================================

#ifdef AVH_SERVER
#include "dlls/extdll.h"
#include "dlls/util.h"
#include "types.h"
#endif

#ifdef AVH_CLIENT
#include "cl_dll/hud.h"
#include "cl_dll/cl_util.h"
#endif

#include "mod/AvHSharedUtil.h"
#include "mod/AvHSelectionHelper.h"
#include "mod/AvHConstants.h"

#ifdef AVH_SERVER
#include "mod/AvHPlayer.h"
#include "mod/AvHServerUtil.h"
#include "mod/AvHEntities.h"
#include "mod/AvHWeldable.h"
#include "mod/AvHGamerules.h"
#include "dlls/cfuncwall.h"
//#include "common/com_model.h"
//int NS_PointContents(const hull_t *hull, int num, float p[3]);
#endif

#include "pm_shared/pm_defs.h"
#include "pm_shared/pm_shared.h"

#ifdef AVH_CLIENT
#include "pm_shared/pm_debug.h"
//extern DebugPointListType				gTriDebugLocations;
//extern DebugPointListType				gSquareDebugLocations;
#endif

#include "util/MathUtil.h"
#include "util/STLUtil.h"

#include "common/vector_util.h"

#ifdef AVH_CLIENT
#include "cl_dll/eventscripts.h"

#include "common/r_efx.h"
#include "common/event_api.h"
#include "common/event_args.h"
#include "cl_dll/in_defs.h"
#endif

#include "common/com_model.h"

#include "mod/AvHSpecials.h"
#include "mod/AvHMarineEquipmentConstants.h"
#include "dlls/turretconst.h"
#include "mod/AvHMarineWeaponConstants.h"
#include "mod/AvHHulls.h"
#include "mod/AvHAlienEquipmentConstants.h"

#include "mod/CollisionUtil.h"
/*
bool NS_BoxesOverlap(float origin1[3], float size1[3], float origin2[3], float size2[3]);
int  NS_PointContents(const hull_t *hull, int num, float p[3]);
int NS_BoxContents(const hull_t *hull, int num, float mins[3], float maxs[3]);
int  NS_GetValveHull(int inHull);
void NS_TraceLine(const hull_t* hull, float srcPoint[3], float dstPoint[3], trace_t* trace);
*/
#include <time.h>
extern playermove_t* pmove;
vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox);


vec3_t gPMDebugPoint;

int kTeamColors[iNumberOfTeamColors][3] =
{
    { 255, 255, 255 },      // White (default)

//  { 125, 165, 210 },      // Blue (marine 1)
    { 0, 153, 255 },        // Blue (marine 1)

//  { 255, 170, 0 },        // HL orange (alien 1)
    { 255, 160, 0 },        // HL orange (alien 1)

    { 145, 215, 140 },      // Green (marine 2)
    { 200, 90, 70 },        // Red (alien 2)
    { 255, 255, 255 }       // White (spectator)
};

float kFTeamColors[iNumberOfTeamColors][3] =
{
    { kTeamColors[0][0] / 255.0f, kTeamColors[0][1] / 255.0f, kTeamColors[0][2] / 255.0f },
    { kTeamColors[1][0] / 255.0f, kTeamColors[1][1] / 255.0f, kTeamColors[1][2] / 255.0f },
    { kTeamColors[2][0] / 255.0f, kTeamColors[2][1] / 255.0f, kTeamColors[2][2] / 255.0f },
    { kTeamColors[3][0] / 255.0f, kTeamColors[3][1] / 255.0f, kTeamColors[3][2] / 255.0f },
    { kTeamColors[4][0] / 255.0f, kTeamColors[4][1] / 255.0f, kTeamColors[4][2] / 255.0f },
    { kTeamColors[5][0] / 255.0f, kTeamColors[5][1] / 255.0f, kTeamColors[5][2] / 255.0f }
};


// Official allowed sizes
// {0, 0, 0 } { 0, 0, 0 } 0x0x0
// { -16, -16, -18 } { 16, 16, 18 } 32x32x36
// { -16, -16, -36 } { 16, 16, 36 } 32x32x72
// { -32, -32, -32 } { 32, 32, 32 } 64x64x64

//#define		kBuilding1MinSize		Vector(-14.33, -14.84, 0.02)
////#define		kBuilding1MaxSize		Vector(21.61, 14.86, 66.9686)
//#define		kBuilding1MaxSize		Vector(14.33, 14.86, 66.9686)
//
//#define		kBuilding2MinSize		Vector(-25.0, -25.0, 0.02)
//#define		kBuilding2MaxSize		Vector(25.0, 25.0, 66.9686)

#define		kResourceMinSize		Vector(-16.0, -16.0, 0.0)
#define		kResourceMaxSize		Vector(16.0, 16.0, 66.9486)

// Tried 100, 110, still jitters.  Shrink tower down a bit?
#define		kAlienResourceMinSize	Vector(-16.0, -16.0, 0.0)
#define		kAlienResourceMaxSize	Vector(16.0, 16.0, 100.0) // was Vector(16.0, 16.0, 80.7443)

//physent_t* AvHSUGetEntity(int inPhysIndex)
//{
//	physent_t* theTarget = NULL;
//	int i = 0;
//
//	if(inPhysIndex > 0)
//	{
//		#ifdef AVH_CLIENT
//		gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
//
//		// Store off the old count
//		//gEngfuncs.pEventAPI->EV_PushPMStates();
//
//		// Now add in all of the players.
//		gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);
//
//		for (i = 0; i < MAX_PHYSENTS; i++)
//		//for (i = 0; i < pmove->numphysent; i++)
//		{
//			physent_t* thePotentialCandidate = gEngfuncs.pEventAPI->EV_GetPhysent(i);
//			if ( thePotentialCandidate && (thePotentialCandidate->info == inPhysIndex))
//			{
//				theTarget = &pmove->physents[i];
//				break;
//			}
//		}
//
//		//gEngfuncs.pEventAPI->EV_PopPMStates();
//		#endif
//
//		// Check phys entities on server
//		#ifdef AVH_SERVER
//		for (i = 0; i < MAX_PHYSENTS; i++)
//		//for (i = 0; i < pmove->numphysent; i++)
//		{
//			if ( pmove->physents[i].info == inPhysIndex )
//			{
//				theTarget = &pmove->physents[i];
//				break;
//			}
//		}
//
//		//CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit);
//		#endif
//	}
//
//	return theTarget;
//}

const char* AvHSHUGetClassNameFromUser3(AvHUser3 inUser3)
{
    const char* theClassName = "";

    switch(inUser3)
    {
    case AVH_USER3_MARINE_PLAYER:
        theClassName = "soldier";
        break;

    case AVH_USER3_COMMANDER_PLAYER:
        theClassName = "commander";
        break;

    case AVH_USER3_ALIEN_PLAYER1:
        theClassName = "skulk";
        break;

    case AVH_USER3_ALIEN_PLAYER2:
        theClassName = "gorge";
        break;

    case AVH_USER3_ALIEN_PLAYER3:
        theClassName = "lerk";
        break;

    case AVH_USER3_ALIEN_PLAYER4:
        theClassName = "fade";
        break;

    case AVH_USER3_ALIEN_PLAYER5:
        theClassName = "onos";
        break;

    case AVH_USER3_ALIEN_EMBRYO:
        theClassName = "gestate";
        break;
    }

    return theClassName;
}


// Pass in -1 to get all entities with a non-zero iuser3
void AvHSHUGetEntities(int inUser3, EntityListType& outEntities)
{
	#ifdef AVH_SERVER
	FOR_ALL_BASEENTITIES();
	if((theBaseEntity->pev->iuser3 == inUser3) || ((inUser3 == -1) && (theBaseEntity->pev->iuser3 > 0)))
	{
		outEntities.push_back(theBaseEntity->entindex());
	}
	END_FOR_ALL_BASEENTITIES();

	#endif

	#ifdef AVH_CLIENT

	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );

	// Store off the old count
	gEngfuncs.pEventAPI->EV_PushPMStates();

	// Now add in all of the players.
	gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);

	physent_t* theEntity = NULL;
	int theNumEnts = pmove->numphysent;
	for (int i = 0; i < theNumEnts; i++)
	{
		theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(i);
		if((theEntity->iuser3 == inUser3) || ((inUser3 == -1) && (theEntity->iuser3 > 0)))
		{
			outEntities.push_back(i);
		}
	}

	gEngfuncs.pEventAPI->EV_PopPMStates();
	#endif
}

bool AvHSHUGetEntityLocation(int inEntity, vec3_t& outLocation)
{
	bool theSuccess = false;

	#ifdef AVH_SERVER
	edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
	if(pEdict && !pEdict->free)
	{
		CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
		if(theEntity)
		{
			outLocation = AvHSHUGetRealLocation(theEntity->pev->origin, theEntity->pev->mins, theEntity->pev->maxs);
			theSuccess = true;
		}
	}
	#endif

	#ifdef AVH_CLIENT
	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
	
	// Store off the old count
	gEngfuncs.pEventAPI->EV_PushPMStates();
	
	// Now add in all of the players.
	gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);

	physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
	
	gEngfuncs.pEventAPI->EV_PopPMStates();

	if(theEntity)
	{
		outLocation = AvHSHUGetRealLocation(theEntity->origin, theEntity->mins, theEntity->maxs);
		theSuccess = true;
	}
	#endif

	return theSuccess;
}


bool AvHSHUGetEntityIUser3(int inEntity, AvHUser3& outIUser3)
{
	bool theSuccess = false;

	#ifdef AVH_SERVER
	edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
	if(pEdict && !pEdict->free)
	{
		CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
		if(theEntity)
		{
			outIUser3 = (AvHUser3)theEntity->pev->iuser3;
			theSuccess = true;
		}
	}
#endif

#ifdef AVH_CLIENT
	// Store off the old count
	gEngfuncs.pEventAPI->EV_PushPMStates();
	
	// Now add in all of the players.
	gEngfuncs.pEventAPI->EV_SetSolidPlayers (-1);

	physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
	if(theEntity)
	{
		outIUser3 = (AvHUser3)theEntity->iuser3;
		theSuccess = true;
	}
	gEngfuncs.pEventAPI->EV_PopPMStates();

#endif
	return theSuccess;
}


bool AvHSHUGetEntityIUser4(int inEntity, int& outIUser4)
{
	bool theSuccess = false;

	#ifdef AVH_SERVER
	edict_t* pEdict = g_engfuncs.pfnPEntityOfEntIndex(inEntity);
	if(pEdict && !pEdict->free)
	{
		CBaseEntity* theEntity = CBaseEntity::Instance(pEdict);
		if(theEntity)
		{
			outIUser4 = theEntity->pev->iuser4;
			theSuccess = true;
		}
	}
#endif

#ifdef AVH_CLIENT
	physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(inEntity);
	if(theEntity)
	{
		outIUser4 = theEntity->iuser4;
		theSuccess = true;
	}
#endif

	return theSuccess;
}


vec3_t AvHSHUGetRealLocation(const vec3_t& inLocation, const vec3_t& inMinBox, const vec3_t& inMaxBox)
{
	vec3_t outLocation;
	VectorCopy(inLocation, outLocation);

	if((outLocation.x == outLocation.y) && (outLocation.y == outLocation.z) && (outLocation.z == 0.0f))
	{
		outLocation.x = (inMinBox.x + inMaxBox.x)/2.0f;
		outLocation.y = (inMinBox.y + inMaxBox.y)/2.0f;
		outLocation.z = (inMinBox.z + inMaxBox.z)/2.0f;
	}
	return outLocation;
}

// Returns range of user3, whatever that means.  Returns -1 for no meaningful range.
int AvHSHUGetDrawRangeForUser3(AvHUser3 inUser3)
{
	int theRange = -1;

	switch(inUser3)
	{
	case AVH_USER3_COMMANDER_STATION:
		theRange = BALANCE_VAR(kCommandStationBuildDistance);
		break;

	case AVH_USER3_FUNC_RESOURCE:
		theRange = BALANCE_VAR(kResourceTowerBuildDistanceTolerance);
		break;

	case AVH_USER3_TURRET_FACTORY:
	case AVH_USER3_ADVANCED_TURRET_FACTORY:
		theRange = BALANCE_VAR(kTurretFactoryBuildDistance);
		break;

	case AVH_USER3_ARMORY:
	case AVH_USER3_ADVANCED_ARMORY:
		theRange = BALANCE_VAR(kArmoryBuildDistance);
		break;

	case AVH_USER3_TURRET:
		theRange = BALANCE_VAR(kTurretRange);
		break;

	case AVH_USER3_SIEGETURRET:
		// TODO: Figure out a way to return minimum range also?
		theRange = BALANCE_VAR(kSiegeTurretRange);
		break;

	case AVH_USER3_PROTOTYPE_LAB:
		theRange = BALANCE_VAR(kArmorDropRange);
		break;

	case AVH_USER3_OBSERVATORY:
		theRange = BALANCE_VAR(kObservatoryXYDetectionRadius);
		break;

	case AVH_USER3_SENSORY_CHAMBER:
		theRange = BALANCE_VAR(kSensoryChamberRange);
		break;
	}

	return theRange;
}

int AvHSHUGetDrawRangeForMessageID(AvHMessageID inMessageID)
{
	AvHUser3 theUser3 = AVH_USER3_NONE;

	switch(inMessageID)
	{
	case BUILD_COMMANDSTATION:
		theUser3 = AVH_USER3_COMMANDER_STATION;
		break;

	case BUILD_TURRET:
		theUser3 = AVH_USER3_TURRET;
		break;

	case BUILD_SIEGE:
		theUser3 = AVH_USER3_SIEGETURRET;
		break;

	case BUILD_OBSERVATORY:
		theUser3 = AVH_USER3_OBSERVATORY;
		break;

	case BUILD_TURRET_FACTORY:
		theUser3 = AVH_USER3_TURRET_FACTORY;
		break;

	case BUILD_PROTOTYPE_LAB:
		theUser3 = AVH_USER3_PROTOTYPE_LAB;
		break;

	case BUILD_ARMORY:
		theUser3 = AVH_USER3_ARMORY;
		break;
	}

	return AvHSHUGetDrawRangeForUser3(theUser3);
}


// Specify whether we draw rings for this entity or not, and draw them at different sizes for aesthetics.  The scalar might go away when the
// artwork all has the correct bounding boxes.
bool AvHSHUGetDrawRingsForUser3(AvHUser3 inUser3, float& outScalar)
{
	bool theDrawRings = false;
	float theScalar = 1.0f;

	switch(inUser3)
	{
	case AVH_USER3_MARINE_PLAYER:
		theScalar = .9f;
		theDrawRings = true;
		break;

	case AVH_USER3_RESTOWER:
		theScalar = 1.6f;
		theDrawRings = true;
		break;

	case AVH_USER3_INFANTRYPORTAL:
		theScalar = 1.35f;
		theDrawRings = true;
		break;

	case AVH_USER3_ARMORY:
	case AVH_USER3_ADVANCED_ARMORY:
		theScalar = 1.35f;
		theDrawRings = true;
		break;

	case AVH_USER3_COMMANDER_STATION:
		theScalar = 1.4f;
		theDrawRings = true;
		break;

	case AVH_USER3_PHASEGATE:
		theScalar = 2.0f;
		theDrawRings = true;
		break;

	case AVH_USER3_OBSERVATORY:
		theScalar = .9f;
		theDrawRings = true;
		break;

	case AVH_USER3_TURRET_FACTORY:
	case AVH_USER3_ADVANCED_TURRET_FACTORY:
		theScalar = 1.6f;
		theDrawRings = true;
		break;

	case AVH_USER3_TURRET:
		theScalar = .7f;
		theDrawRings = true;
		break;

	case AVH_USER3_NUKEPLANT:
		theScalar = 1.5f;
		theDrawRings = true;
		break;

	case AVH_USER3_ARMSLAB:
	case AVH_USER3_PROTOTYPE_LAB:
	case AVH_USER3_CHEMLAB:
	case AVH_USER3_MEDLAB:
	case AVH_USER3_SIEGETURRET:
		theScalar = 1.3f;
		theDrawRings = true;
		break;

	// Alien buildings
	case AVH_USER3_DEFENSE_CHAMBER:
	case AVH_USER3_OFFENSE_CHAMBER:
	case AVH_USER3_MOVEMENT_CHAMBER:
	case AVH_USER3_SENSORY_CHAMBER:
	case AVH_USER3_ALIENRESTOWER:
		theScalar = 1.3f;
		theDrawRings = true;
		break;

	case AVH_USER3_ALIEN_PLAYER1:
		theScalar = .6f;
		theDrawRings = true;
		break;
	case AVH_USER3_ALIEN_PLAYER2:
		theScalar = 1.0f;
		theDrawRings = true;
		break;
	case AVH_USER3_ALIEN_PLAYER3:
		theScalar = 1.0;
		theDrawRings = true;
		break;
	case AVH_USER3_ALIEN_PLAYER4:
		theScalar = 1.3f;
		theDrawRings = true;
		break;
	case AVH_USER3_ALIEN_PLAYER5:
		theScalar = 1.2f;
		theDrawRings = true;
		break;
	}

	if(theDrawRings)
	{
		outScalar = theScalar;
	}

	return theDrawRings;
}

bool AvHSHUGetBuildRegions(AvHMessageID inMessageID, EntityListType& outEntities, IntList& outRanges, float& outZAdjustment, bool& outSnapToLocation)
{
	bool theFoundTech = false;

	typedef vector<AvHUser3> User3ListType;
	User3ListType theUser3s;

	outZAdjustment = 0.0f;
	outSnapToLocation = false;

	switch(inMessageID)
	{
	case BUILD_RESOURCES:
	case ALIEN_BUILD_RESOURCES:
		theUser3s.push_back(AVH_USER3_FUNC_RESOURCE);
		outZAdjustment = (kFuncResourceMaxSize.z - kFuncResourceMinSize.z);
		outSnapToLocation = true;
		break;

	case BUILD_INFANTRYPORTAL:
		theUser3s.push_back(AVH_USER3_COMMANDER_STATION);
		break;

	case BUILD_TURRET:
		theUser3s.push_back(AVH_USER3_TURRET_FACTORY);
		theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY);
		break;

	case BUILD_SIEGE:
		theUser3s.push_back(AVH_USER3_ADVANCED_TURRET_FACTORY);
		break;

	case BUILD_MINES:
	case BUILD_WELDER:
	case BUILD_SHOTGUN:
		theUser3s.push_back(AVH_USER3_ARMORY);
		theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY);
		break;

	case BUILD_HMG:
	case BUILD_GRENADE_GUN:
		theUser3s.push_back(AVH_USER3_ADVANCED_ARMORY);
		break;

	case BUILD_HEAVY:
	case BUILD_JETPACK:
		theUser3s.push_back(AVH_USER3_PROTOTYPE_LAB);
		break;
	}

	if(theUser3s.size() > 0)
	{
		for(User3ListType::iterator theUser3Iter = theUser3s.begin(); theUser3Iter != theUser3s.end(); theUser3Iter++)
		{
			EntityListType theEntities;
			AvHSHUGetEntities(*theUser3Iter, theEntities);

			for(EntityListType::iterator theEntityIter = theEntities.begin(); theEntityIter != theEntities.end(); theEntityIter++)
			{
				// Only add buildings that are fully built
				int theIuser4 = 0;
				if(AvHSHUGetEntityIUser4(*theEntityIter, theIuser4))
				{
					if(!GetHasUpgrade(theIuser4, MASK_BUILDABLE))
					{
						outEntities.push_back(*theEntityIter);
						outRanges.push_back(AvHSHUGetDrawRangeForUser3(*theUser3Iter));
					}
				}
			}
		}
		theFoundTech = true;
	}

	return theFoundTech;
}


// tankefugl: 0000291 -- allows listed structures to be dropped on resource towers
bool AvHSHUGetIsDroppableOnRTs(AvHMessageID inMessageID)
{
	switch (inMessageID)
	{
		case BUILD_HEALTH:
		case BUILD_AMMO:
		case BUILD_MINES:
		case BUILD_WELDER:
		case BUILD_SHOTGUN:
		case BUILD_HMG:
		case BUILD_GRENADE_GUN:
		case BUILD_CAT:
		case BUILD_HEAVY:
		case BUILD_JETPACK:
		case BUILD_RESOURCES:
		case ALIEN_BUILD_RESOURCES:
		case ALIEN_BUILD_HIVE:
			return true;
		default:
			return false;
	}
}
// :tankefugl

bool AvHSHUGetIsMarineStructure(AvHMessageID inMessageID)
{

    switch (inMessageID)
    {

    case BUILD_INFANTRYPORTAL:
    case BUILD_RESOURCES:
    case BUILD_TURRET_FACTORY:
    case BUILD_ARMSLAB:
    case BUILD_PROTOTYPE_LAB:
    case BUILD_ARMORY:
    case BUILD_NUKE_PLANT:
    case BUILD_OBSERVATORY:
    case BUILD_PHASEGATE:
    case BUILD_TURRET:
    case BUILD_SIEGE:
    case BUILD_COMMANDSTATION:
        return true;
    default:
        return false;

    }

}

bool AvHSHUGetIsMarineStructure(AvHUser3 inUser3)
{

    switch (inUser3)
    {
    case AVH_USER3_COMMANDER_STATION:
    case AVH_USER3_TURRET_FACTORY:
    case AVH_USER3_ARMORY:
    case AVH_USER3_ADVANCED_ARMORY:
    case AVH_USER3_ARMSLAB:
    case AVH_USER3_PROTOTYPE_LAB:
    case AVH_USER3_OBSERVATORY:
    case AVH_USER3_CHEMLAB:
    case AVH_USER3_MEDLAB:
    case AVH_USER3_NUKEPLANT:
    case AVH_USER3_TURRET:
    case AVH_USER3_SIEGETURRET:
    case AVH_USER3_RESTOWER:
    case AVH_USER3_INFANTRYPORTAL:
    case AVH_USER3_PHASEGATE:
    case AVH_USER3_ADVANCED_TURRET_FACTORY:
        return true;
    default:
        return false;
    }

}

void AvHSHUGetMinBuildRadiusViolations(AvHMessageID inMessageID, vec3_t& inLocation, EntityListType& outViolations)
{

    // Enforce a minimum build radius for marine structures.

    if (AvHSHUGetIsMarineStructure(inMessageID))
    {
        EntityListType theEntities;
        AvHSHUGetEntities(-1, theEntities);

        vec3_t theMinSize, theMaxSize;

        AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize);
        float theMaxRadius1 = max(-min(theMinSize.x, theMinSize.y), max(theMaxSize.x, theMaxSize.y));

        for (EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++)
		{

            AvHUser3 theUser3;
            AvHSHUGetEntityIUser3(*theIter, theUser3);

			bool theEntityCouldBlock = AvHSHUGetIsMarineStructure(theUser3);
			if(inMessageID != BUILD_RESOURCES)
			{
				theEntityCouldBlock |= (theUser3 == AVH_USER3_FUNC_RESOURCE);
			}

			bool theEntityIsSolid = false;

#ifdef AVH_SERVER
			edict_t* theEntity = INDEXENT(*theIter);

			if(theEntity)
				theEntityIsSolid = (theEntity->v.solid == SOLID_BBOX);
#endif

#ifdef AVH_CLIENT

			physent_t* theEntity = gEngfuncs.pEventAPI->EV_GetPhysent(*theIter);

			if(theEntity) 
				theEntityIsSolid = (theEntity->solid == SOLID_BBOX);
				
#endif	
			// joev: 0000291
			// It's possible to place "on" marines if you're offset a little from center. This code and
			// associated changes below and in AvHHudRender.cpp is to enforce a build distance around players
			// in the same way as buildings to prevent this exploit.
			if (theUser3 == AVH_USER3_MARINE_PLAYER)
			{
				theEntityIsSolid = true;
				theEntityCouldBlock = true;
			}

			if (theEntityCouldBlock && theEntityIsSolid)
            {
                AvHSHUGetSizeForUser3(theUser3, theMinSize, theMaxSize);
                float theMaxRadius2 = max(max(theMinSize.x, theMaxSize.x), max(theMinSize.y, theMaxSize.y));

	            vec3_t theLocation;
			    if(AvHSHUGetEntityLocation(*theIter, theLocation))
			    {
			        vec3_t theXYInLocation = inLocation;
                    vec3_t theXYTheLocation = theLocation;

                    theXYInLocation.z = 0;
                    theXYTheLocation.z = 0;

                    float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation);
					// joev: 0000291
					// It's possible to place "on" marines if you're offset a little from center. This code and
					// associated changes above and in AvHHudRender.cpp is to enforce a build distance around players
					// in the same way as buildings to prevent this exploit.
 					float theMinMarineBuildDistance;
               		if (theUser3 == AVH_USER3_MARINE_PLAYER) {
						theMinMarineBuildDistance = BALANCE_VAR(kMinMarinePlayerBuildDistance);
					}
					else
					{
						theMinMarineBuildDistance = BALANCE_VAR(kMinMarineBuildDistance);
					}
					// :joev
                    if (theDistance < theMinMarineBuildDistance + theMaxRadius1 + theMaxRadius2)
                    {
                        outViolations.push_back(*theIter);
                    }
                }
            }
        }
    }
}

bool AvHSHUGetAreSpecialBuildingRequirementsMet(AvHMessageID inMessageID, vec3_t& inLocation)
{
	bool theRequirementsMet = false;
	EntityListType theEntities;
	IntList theDistanceRequirements;
	float theZAdjustment = 0.0f;
	bool theSnapToLocation = false;

	if(inMessageID == ALIEN_BUILD_HIVE)
	{
		// Look for inactive hives within radius
		EntityListType theEntities;

		// Look for a unoccupied hive spot within range
		AvHSHUGetEntities(AVH_USER3_HIVE, theEntities);

		for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++)
		{
			vec3_t theLocation;
			if(AvHSHUGetEntityLocation(*theIter, theLocation))
			{
				// Set z's equal to check 2D distance only
				inLocation.z = theLocation.z;
				float theDistance = VectorDistance((float*)&inLocation, (float*)&theLocation);

				if(theDistance <= kHiveXYDistanceTolerance)
				{
					// Make sure this hive isn't already active
					#ifdef AVH_SERVER
					CBaseEntity* theEntity = AvHSUGetEntityFromIndex(*theIter);
					ASSERT(theEntity);
					if(theEntity->pev->team == 0)
					{
					#endif

						theRequirementsMet = true;
						inLocation = theLocation;

					#ifdef AVH_SERVER
					}
					#endif

					break;
				}
			}
		}
	}
	else if(AvHSHUGetBuildRegions(inMessageID, theEntities, theDistanceRequirements, theZAdjustment, theSnapToLocation))
	{
		ASSERT(theEntities.size() == theDistanceRequirements.size());
		vec3_t theBestLocation;

		int i = 0;
		float theClosestDistance = kMaxMapDimension;
		for(EntityListType::iterator theIter = theEntities.begin(); theIter != theEntities.end(); theIter++, i++)
		{
			vec3_t theLocation;
			if(AvHSHUGetEntityLocation(*theIter, theLocation))
			{
				// Only check xy distance
				vec3_t theXYInLocation = inLocation;
				vec3_t theXYTheLocation = theLocation;
				theXYInLocation.z = 0;
				theXYTheLocation.z = 0;

				float theDistance = VectorDistance((float*)&theXYInLocation, (float*)&theXYTheLocation);

				int theDistanceRequirement = theDistanceRequirements[i];
				if((theDistance <= theDistanceRequirement) || (theDistanceRequirement == -1))
				{
					// Pick the closest one, in case there are multiples in range
					if(theDistance < theClosestDistance)
					{
						theClosestDistance = theDistance;
						VectorCopy(theLocation,theBestLocation);
						theRequirementsMet = true;
					}
				}
			}
		}
		if(theRequirementsMet && theSnapToLocation)
		{
			inLocation = theBestLocation;
			inLocation.z += theZAdjustment;
		}
	}
	else
	{
		theRequirementsMet = true;
	}

    EntityListType theBuildRadiusViolations;
    AvHSHUGetMinBuildRadiusViolations(inMessageID, inLocation, theBuildRadiusViolations);

    if (theBuildRadiusViolations.size() > 0)
    {
        theRequirementsMet = false;
    }

	// Anti-llama/newbie tactic: don't allow non-resource buildings to be placed such that they block access to nozzles
	// Make sure generic building isn't being placed on top of resource nozzles
	// tankefugl: 0000291
	// allow equipment, rts and hives to be dropped around nodes
	if(AvHSHUGetIsDroppableOnRTs(inMessageID) == false)
	{
	// :tankefugl
		// If building is too close to an empty nozzle, don't allow it
		float theResourceBuildingRadius, theTotalMinRadius;
		vec3_t theMinSize, theMaxSize, theMinRadius, theFlattenedInLocation, theLocation;

		theFlattenedInLocation[0] = inLocation[0];
		theFlattenedInLocation[1] = inLocation[1];
		theFlattenedInLocation[2] = 0;

		theResourceBuildingRadius = 60;

		EntityListType theEntities;
		AvHSHUGetEntities(AVH_USER3_FUNC_RESOURCE,theEntities);
		if(AvHSHUGetSizeForTech(inMessageID,theMinSize,theMaxSize))
		{
			EntityListType::iterator end = theEntities.end();
			for(EntityListType::iterator current = theEntities.begin(); current < end; ++current)
			{
				if(AvHSHUGetEntityLocation(*current,theLocation))
				{
					//flatten to 2 dimensions
					theLocation[2] = 0;

					//space = radius of both buildings combined
					theTotalMinRadius = theResourceBuildingRadius;
					theTotalMinRadius += max(-min(theMinSize.x,theMinSize.y),max(theMaxSize.x,theMaxSize.y));

					if(VectorDistance((float*)&theFlattenedInLocation,(float*)&theLocation) < theTotalMinRadius)
					{
						theRequirementsMet = false;
						break;
					}
				}
			}
		}
	}

	return theRequirementsMet;
}

bool AvHSHUGetBuildTechClassName(AvHMessageID inMessageID, char*& outClassName)
{
	bool theSuccess = true;

	switch(inMessageID)
	{
	// Buildings
	case BUILD_RESOURCES:
		outClassName = kwsResourceTower;
		break;

	//case BUILD_REINFORCEMENTS:
	//	outClassName = kwsInfantryPortal;
	//	break;

	case BUILD_INFANTRYPORTAL:
		outClassName = kwsInfantryPortal;
		break;

	case BUILD_COMMANDSTATION:
		outClassName = kwsTeamCommand;
		break;

	case BUILD_TURRET_FACTORY:
		outClassName = kwsTurretFactory;
		break;

	case BUILD_ARMSLAB:
		outClassName = kwsArmsLab;
		break;

	case BUILD_PROTOTYPE_LAB:
		outClassName = kwsPrototypeLab;
		break;

	case BUILD_ARMORY:
		outClassName = kwsArmory;
		break;

	case ARMORY_UPGRADE:
		outClassName = kwsAdvancedArmory;
		break;

	case BUILD_NUKE_PLANT:
		outClassName = kwsNukePlant;
		break;

	case BUILD_OBSERVATORY:
		outClassName = kwsObservatory;
		break;

	case BUILD_SCAN:
		outClassName = kwsScan;
		break;

	case BUILD_PHASEGATE:
		outClassName = kwsPhaseGate;
		break;

	case BUILD_TURRET:
		outClassName = kwsDeployedTurret;
		break;

	case BUILD_SIEGE:
		outClassName = kwsSiegeTurret;
		break;

	// Equipment
	case BUILD_HEALTH:
		outClassName = kwsHealth;
		break;

    case BUILD_CAT:
        outClassName = kwsCatalyst;
        break;

	case BUILD_JETPACK:
		outClassName = kwsJetpack;
		break;

	case BUILD_HEAVY:
		outClassName = kwsHeavyArmor;
		break;

	case BUILD_AMMO:
		outClassName = kwsGenericAmmo;
		break;

	case BUILD_WELDER:
		outClassName = kwsWelder;
		break;

    case BUILD_MINES:
		outClassName = kwsMine;
		break;

	case BUILD_SHOTGUN:
		outClassName = kwsShotGun;
		break;

	case BUILD_HMG:
		outClassName = kwsHeavyMachineGun;
		break;

	case BUILD_NUKE:
		outClassName = kwsNuke;
		break;

	case BUILD_GRENADE_GUN:
		outClassName = kwsGrenadeGun;
		break;

	//case BUILD_MEDKIT:
	//	break;

	case ALIEN_BUILD_RESOURCES:
		outClassName = kwsAlienResourceTower;
		break;

	case ALIEN_BUILD_OFFENSE_CHAMBER:
		outClassName = kwsOffenseChamber;
		break;

	case ALIEN_BUILD_DEFENSE_CHAMBER:
		outClassName = kwsDefenseChamber;
		break;

	case ALIEN_BUILD_SENSORY_CHAMBER:
		outClassName = kwsSensoryChamber;
		break;

	case ALIEN_BUILD_MOVEMENT_CHAMBER:
		outClassName = kwsMovementChamber;
		break;

	case ALIEN_BUILD_HIVE:
		outClassName = kesTeamHive;
		break;

	default:
		theSuccess = false;
		break;
	}

	return theSuccess;
}

bool AvHSHUGetResearchTechName(AvHMessageID inResearchID, char*& outResearchTechName)
{
	bool theSuccess = true;

	switch(inResearchID)
	{
	case RESEARCH_ELECTRICAL:
		outResearchTechName = "research_electrical";
		break;

	case RESEARCH_ARMOR_ONE:
		outResearchTechName = "research_armorl1";
		break;

	case RESEARCH_ARMOR_TWO:
		outResearchTechName = "research_armorl2";
		break;

	case RESEARCH_ARMOR_THREE:
		outResearchTechName = "research_armorl3";
		break;

	case RESEARCH_WEAPONS_ONE:
		outResearchTechName = "research_weaponsl1";
		break;

	case RESEARCH_WEAPONS_TWO:
		outResearchTechName = "research_weaponsl2";
		break;

	case RESEARCH_WEAPONS_THREE:
		outResearchTechName = "research_weaponsl3";
		break;

	case ARMORY_UPGRADE:
		outResearchTechName = "research_advarmory";
		break;

	case TURRET_FACTORY_UPGRADE:
		outResearchTechName = "research_advturretfactory";
		break;

	case RESEARCH_JETPACKS:
		outResearchTechName = "research_jetpacks";
		break;

	case RESEARCH_HEAVYARMOR:
		outResearchTechName = "research_heavyarmor";
		break;

	case RESEARCH_DISTRESSBEACON:
		outResearchTechName = "research_distressbeacon";
		break;

	case RESEARCH_HEALTH:
		outResearchTechName = "research_health";
		break;

    case RESEARCH_CATALYSTS:
        outResearchTechName = "research_catalysts";
        break;

	case MESSAGE_CANCEL:
		outResearchTechName = "research_cancel";
		break;

	case RESEARCH_MOTIONTRACK:
		outResearchTechName = "research_motiontracking";
		break;

	case RESEARCH_PHASETECH:
		outResearchTechName = "research_phasetech";
		break;

    case RESEARCH_GRENADES:
        outResearchTechName = "research_grenades";
        break;

	default:
		theSuccess = false;
		break;
	}

	return theSuccess;
}


bool AvHSHUGetCenterPositionForGroup(int inGroupNumber, float* inPlayerOrigin, float* outCenterPosition)
{
	bool theSuccess = false;
	vec3_t thePosition;
	float theX, theY;

	#ifdef AVH_CLIENT
	theSuccess = gHUD.GetCenterPositionForGroup(inGroupNumber, thePosition);
	theX = thePosition[0];
	theY = thePosition[1];
	#endif

	#ifdef AVH_SERVER

	// Loop through players, find the closest player to inPlayerOrigin, to see which player is being predicted.  Is there a better way?
	AvHPlayer* theClosestPlayer = NULL;
	float theClosestDistance = 10000;

	FOR_ALL_ENTITIES(kAvHPlayerClassName, AvHPlayer*)
		float theDistance = VectorDistance(theEntity->pev->origin, inPlayerOrigin);
		if(theDistance < theClosestDistance)
		{
			theClosestPlayer = theEntity;
			theClosestDistance = theDistance;
		}
	END_FOR_ALL_ENTITIES(kAvHPlayerClassName)

	if(theClosestPlayer)
	{
		theSuccess = theClosestPlayer->GetCenterPositionForGroup(inGroupNumber, theX, theY);
	}
	#endif

	if(theSuccess)
	{
		outCenterPosition[0] = theX;
		outCenterPosition[1] = theY;
	}

	return theSuccess;
}


bool AvHSHUGetIsBuilding(AvHMessageID inMessageID)
{
	bool theIsBuilding = false;

	switch(inMessageID)
	{
		// Buildings
	case BUILD_RESOURCES:
	case BUILD_INFANTRYPORTAL:
	case BUILD_COMMANDSTATION:
	case BUILD_TURRET_FACTORY:
	case BUILD_ARMSLAB:
	case BUILD_PROTOTYPE_LAB:
	case BUILD_ARMORY:
	case BUILD_NUKE_PLANT:
	case BUILD_OBSERVATORY:
	case BUILD_PHASEGATE:
	case BUILD_TURRET:
	case BUILD_SIEGE:

	// Alien buildings
	case ALIEN_BUILD_OFFENSE_CHAMBER:
	case ALIEN_BUILD_DEFENSE_CHAMBER:
	case ALIEN_BUILD_SENSORY_CHAMBER:
	case ALIEN_BUILD_MOVEMENT_CHAMBER:
	case ALIEN_BUILD_HIVE:
		theIsBuilding = true;
		break;
	}

	return theIsBuilding;
}

bool AvHSHUGetIsBuildTech(AvHMessageID inMessageID)
{
	bool theIsBuildTech = false;

	switch(inMessageID)
	{
	// Buildings
	case BUILD_RESOURCES:
	case BUILD_INFANTRYPORTAL:
	case BUILD_COMMANDSTATION:
	case BUILD_TURRET_FACTORY:
	case BUILD_ARMSLAB:
	case BUILD_PROTOTYPE_LAB:
	case BUILD_ARMORY:
	//case UPGRADE_ADVANCED_WEAPON_FACTORY:
	case BUILD_NUKE_PLANT:
	case BUILD_OBSERVATORY:
	case BUILD_SCAN:
	case BUILD_PHASEGATE:
	case BUILD_TURRET:
	case BUILD_SIEGE:
	case BUILD_HEAVY:
	case BUILD_JETPACK:

	// Equipment
	case BUILD_AMMO:
	case BUILD_HEALTH:
    case BUILD_CAT:
	case BUILD_WELDER:
    case BUILD_MINES:
	case BUILD_SHOTGUN:
	case BUILD_HMG:
	case BUILD_NUKE:
	case BUILD_GRENADE_GUN:
	//case BUILD_MEDKIT:
	//case BUILD_STIMPACK:

	// Alien buildings
	case ALIEN_BUILD_OFFENSE_CHAMBER:
	case ALIEN_BUILD_DEFENSE_CHAMBER:
	case ALIEN_BUILD_SENSORY_CHAMBER:
	case ALIEN_BUILD_MOVEMENT_CHAMBER:
		theIsBuildTech = true;
		break;
	}

	return theIsBuildTech;
}

bool AvHSHUGetIsWeaponFocusable(AvHWeaponID inWeaponID)
{
    bool theIsFocusable = false;

    switch(inWeaponID)
    {
    case AVH_WEAPON_BITE:
    case AVH_WEAPON_SPIT:
    case AVH_WEAPON_BITE2:
    case AVH_WEAPON_SWIPE:
    case AVH_WEAPON_CLAWS:
        theIsFocusable = true;
        break;
    }

    return theIsFocusable;
}

bool AvHSHUGetDoesTechCostEnergy(AvHMessageID inMessageID)
{
	bool theTechCostsEnergy = false;

	switch(inMessageID)
	{
	case BUILD_SCAN:
		theTechCostsEnergy = true;
		break;
	}

	return theTechCostsEnergy;
}

bool AvHSHUGetIsCombatModeTech(AvHMessageID inMessageID)
{
	bool theIsCombatModeTech = false;

	switch(inMessageID)
	{
	case BUILD_SHOTGUN:
	case BUILD_GRENADE_GUN:
	case BUILD_HMG:
	case BUILD_WELDER:
	case BUILD_MINES:
	case BUILD_JETPACK:
	case BUILD_HEAVY:
	case BUILD_SCAN:
		theIsCombatModeTech = true;
		break;
	}

	return theIsCombatModeTech;
}

bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID)
{
	bool theIsResearchTech = false;

	switch(inMessageID)
	{
	case RESEARCH_ELECTRICAL:
	case RESEARCH_ARMOR_ONE:
	case RESEARCH_ARMOR_TWO:
	case RESEARCH_ARMOR_THREE:
	case RESEARCH_WEAPONS_ONE:
	case RESEARCH_WEAPONS_TWO:
	case RESEARCH_WEAPONS_THREE:
	case TURRET_FACTORY_UPGRADE:
	case RESEARCH_JETPACKS:
	case RESEARCH_HEAVYARMOR:
	case RESEARCH_DISTRESSBEACON:
	case RESEARCH_HEALTH:
    case RESEARCH_CATALYSTS:
	case MESSAGE_CANCEL:
	case RESEARCH_MOTIONTRACK:
	case RESEARCH_PHASETECH:
    case RESEARCH_GRENADES:

	case RESOURCE_UPGRADE:
	case ARMORY_UPGRADE:
		theIsResearchTech = true;
		break;
	}

	return theIsResearchTech;
}




//Here's TFC's code that checks whether a player's allowed to build a sentry
//or not.
//I can't remember if there was any good reason why we used
//UTIL_FindEntitiesInSphere()
//instead of UTIL_EntitiesInBox().
//
////=========================================================================
//// Returns 0 if the area around obj is safe to build in
//int CBaseEntity::CheckArea( CBaseEntity *pIgnore )
//{
//	TraceResult tr;
//	Vector vecOrg = pev->origin;
//
//	// Check the origin
//	int iContents = UTIL_PointContents(vecOrg);
//	if ( iContents != CONTENT_EMPTY && iContents != CONTENT_WATER )
//		return CAREA_BLOCKED;
//
//	Vector vecIgnoreOrg = pIgnore->pev->origin;
//	// Get the player's origin irrelevant of crouching
//	if ( pIgnore->pev->flags & FL_DUCKING )
//	{
//		vecIgnoreOrg = vecIgnoreOrg + (VEC_DUCK_HULL_MIN -
//			VEC_HULL_MIN);
//	}
//	// Trace a hull
//	UTIL_TraceHull( vecIgnoreOrg, pev->origin, ignore_monsters,
//		large_hull, edict(), &tr );
//	CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
//	if (tr.flFraction != 1 || tr.fAllSolid == 1)
//		return CAREA_BLOCKED;
//
//	// Check for solid entities in the area
//	CBaseEntity *pEnt = NULL;
//	while ( (pEnt = UTIL_FindEntityInSphere( pEnt, pev->origin, 48 )) !=
//		NULL )
//	{
//		// If it's not the engineer, and its a solid entity, fail
//		if (pEnt != pIgnore && pEnt != this && pEnt->pev->solid >
//			SOLID_TRIGGER)
//			return CAREA_BLOCKED;
//	}
//
//	// Cycle through all the Nobuild zones in the map and make sure this
//	isn't in one of them
//		CBaseEntity *pNoBuild = UTIL_FindEntityByClassname( NULL,
//		"func_nobuild" );
//	while ( pNoBuild )
//	{
//		// Check to see if we're building in this zone
//		if ( vecOrg.x >= pNoBuild->pev->mins.x && vecOrg.y >=
//			pNoBuild->pev->mins.y && vecOrg.z >= pNoBuild->pev->mins.z &&
//			vecOrg.x <= pNoBuild->pev->maxs.x &&
//			vecOrg.y <= pNoBuild->pev->maxs.y && vecOrg.z <= pNoBuild->pev->maxs.z )
//			return CAREA_NOBUILD;
//
//		pNoBuild = UTIL_FindEntityByClassname( pNoBuild,
//			"func_nobuild" );
//	}
//
//	// Check below
//	UTIL_TraceLine( vecOrg, vecOrg + Vector(0,0,-64),
//		dont_ignore_monsters, edict(), &tr );
//	if ( tr.flFraction == 1.0 )
//		return CAREA_BLOCKED;
//
//	return CAREA_CLEAR;
//}

//bool AvHSHUGetIsEnoughRoom(const vec3_t& inCenter, const vec3_t& inMin, const vec3_t& inMax)
//{
//	bool theEnoughRoom = false;
//
//	// Do a traceline from center to center + min
//	// Hit nothing?
//		// Do a traceline from center to center + max
//		// Hit nothing?
//			// Success
//			theEnoughRoom = true;
//
//	return theEnoughRoom;
//}

bool AvHSHUGetIsGroupMessage(AvHMessageID inMessageID)
{
	bool theIsGroupMessage = false;

	switch(inMessageID)
	{
	case GROUP_CREATE_1:
	case GROUP_CREATE_2:
	case GROUP_CREATE_3:
	case GROUP_CREATE_4:
	case GROUP_CREATE_5:
	case GROUP_SELECT_1:
	case GROUP_SELECT_2:
	case GROUP_SELECT_3:
	case GROUP_SELECT_4:
	case GROUP_SELECT_5:
		theIsGroupMessage = true;
	}

	return theIsGroupMessage;
}

bool AvHSHUGetSizeForTech(AvHMessageID inMessageID, Vector& outMinSize, Vector& outMaxSize, bool inGetSizeToPlace)
{
	bool theSuccess = false;

	// Onos-sized
	//const int theOnosHeightNeededToSpawn = HULL3_MAXZ - HULL3_MINZ + kRespawnFudgeFactorHeight;
	//const int theOnosWidthNeededToSpawn = HULL3_MAXY - HULL3_MINY + kRespawnFudgeFactorHeight;

	// Marine-sized
	const int theMarineHeightNeededToSpawn = HULL0_MAXZ - HULL0_MINZ + kRespawnFudgeFactorHeight;

	switch(inMessageID)
	{
	case BUILD_INFANTRYPORTAL:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 14.49);

		// Make sure there is room above it for players to spawn
		if(inGetSizeToPlace)
		{
			outMaxSize.z += theMarineHeightNeededToSpawn;
		}

		theSuccess = true;
		break;

	case BUILD_PHASEGATE:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 14.49);

		// Make sure there is remove above it for players to spawn
		if(inGetSizeToPlace)
		{
			//outMinSize.x = outMinSize.y = -theOnosWidthNeededToSpawn;
			//outMaxSize.x = outMaxSize.y = theOnosWidthNeededToSpawn;
			//outMaxSize.z += theOnosHeightNeededToSpawn;
			outMaxSize.z += theMarineHeightNeededToSpawn;
		}

		theSuccess = true;
		break;

	case BUILD_RESOURCES:
		outMinSize = kResourceMinSize;
		outMaxSize = kResourceMaxSize;
		theSuccess = true;
		break;

	case ALIEN_BUILD_RESOURCES:
		outMinSize = kAlienResourceMinSize;
		outMaxSize = kAlienResourceMaxSize;
		theSuccess = true;
		break;

	default:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 66.9486);
		theSuccess = true;
		break;

	case BUILD_ARMSLAB:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 72.0 /*66.9486*/);
		theSuccess = true;
		break;

	case BUILD_TURRET:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 42.0);
		theSuccess = true;
		break;

	case ALIEN_BUILD_OFFENSE_CHAMBER:
	case ALIEN_BUILD_DEFENSE_CHAMBER:
	case ALIEN_BUILD_SENSORY_CHAMBER:
	case ALIEN_BUILD_MOVEMENT_CHAMBER:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 44.0);
		theSuccess = true;
		break;

	case BUILD_COMMANDSTATION:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 73.0 /*70.34*/);
		theSuccess = true;
		break;

	case BUILD_TURRET_FACTORY:
		outMinSize = Vector(-16, -16, 0);
		//outMaxSize = Vector(16.0, 16.0, 55.68);
		outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/);
		theSuccess = true;
		break;

	case BUILD_ARMORY:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/);
		theSuccess = true;
		break;

	case BUILD_PROTOTYPE_LAB:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 73.0 /*67.7443*/);
		theSuccess = true;
		break;

	case BUILD_OBSERVATORY:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 80.7443);
		theSuccess = true;
		break;

	case BUILD_SIEGE:
		outMinSize = Vector(-16, -16, 0);
		outMaxSize = Vector(16.0, 16.0, 73.0 /*62.1931*/ /*50.6678*/);
		theSuccess = true;
		break;

	case BUILD_HEALTH:
		outMinSize = kHealthMinSize;
		outMaxSize = kHealthMaxSize;
		theSuccess = true;
		break;

    case BUILD_CAT:
		outMinSize = kCatalystMinSize;
		outMaxSize = kCatalystMaxSize;
		theSuccess = true;
		break;

	case BUILD_HEAVY:
		outMinSize = kHeavyMinSize;
		outMaxSize = kHeavyMaxSize;
		theSuccess = true;
		break;

	case BUILD_JETPACK:
		outMinSize = kJetpackMinSize;
		outMaxSize = kJetpackMaxSize;
		theSuccess = true;
		break;

	case BUILD_AMMO:
		outMinSize = kAmmoMinSize;
		outMaxSize = kAmmoMaxSize;
		theSuccess = true;
		break;

	case BUILD_MINES:
		outMinSize = kWeaponMinSize;
		outMaxSize = Vector(16, 16, 40);
		theSuccess = true;
		break;

	case BUILD_SHOTGUN:
	case BUILD_HMG:
	case BUILD_WELDER:
	case BUILD_NUKE:
	case BUILD_GRENADE_GUN:
		outMinSize = kWeaponMinSize;
		outMaxSize = kWeaponMaxSize;
		theSuccess = true;
		break;

	case ALIEN_BUILD_HIVE:
		outMinSize = kHiveMinSize;
		outMaxSize = kHiveMaxSize;
		theSuccess = true;
		break;
	}

	return theSuccess;
}

bool AvHSHUGetSizeForPlayerUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize, bool inDucking)
{
	bool theSuccess = false;

	// Now set size
	switch(inUser3)
	{
	case AVH_USER3_NONE:
	case AVH_USER3_MARINE_PLAYER:
	case AVH_USER3_COMMANDER_PLAYER:
	case AVH_USER3_ALIEN_PLAYER4:
		// Get simple case working first
		if(inDucking)
		{
			outMinSize = HULL1_MIN;
			outMaxSize = HULL1_MAX;
		}
		else
		{
			outMinSize = HULL0_MIN;
			outMaxSize = HULL0_MAX;
		}
		theSuccess = true;
		break;

	case AVH_USER3_ALIEN_PLAYER1:
	case AVH_USER3_ALIEN_PLAYER2:
	case AVH_USER3_ALIEN_PLAYER3:
	case AVH_USER3_ALIEN_EMBRYO:
		outMinSize = HULL1_MIN;
		outMaxSize = HULL1_MAX;
		theSuccess = true;
		break;

	case AVH_USER3_ALIEN_PLAYER5:
		if(inDucking)
		{
			outMinSize = HULL0_MIN;
			outMaxSize = HULL0_MAX;
		}
		else
		{
			outMinSize = HULL3_MIN;
			outMaxSize = HULL3_MAX;
		}
		theSuccess = true;
		break;
	}

	return theSuccess;
}

bool AvHSHUGetSizeForUser3(AvHUser3 inUser3, Vector& outMinSize, Vector& outMaxSize)
{
	bool theSuccess = false;

	// If it's a player, get max size he can be (assuming he's not ducking)
	theSuccess = AvHSHUGetSizeForPlayerUser3(inUser3, outMinSize, outMaxSize, false);
	if(!theSuccess)
	{
		AvHMessageID theMessageID = MESSAGE_NULL;

		// Convert it to a AvHMessageID if possible
		if(AvHSHUUser3ToMessageID(inUser3, theMessageID))
		{
			theSuccess = AvHSHUGetSizeForTech(theMessageID, outMinSize, outMaxSize);
		}
	}

	return theSuccess;
}

bool AvHSHUUser3ToMessageID(AvHUser3 inUser3, AvHMessageID& outMessageID)
{
	AvHMessageID theMessageID = MESSAGE_NULL;
	bool theSuccess = false;

	switch(inUser3)
	{
	case AVH_USER3_FUNC_RESOURCE:
	case AVH_USER3_RESTOWER:
		theMessageID = BUILD_RESOURCES;
		break;

	case AVH_USER3_COMMANDER_STATION:
		theMessageID = BUILD_COMMANDSTATION;
		break;

	case AVH_USER3_TURRET_FACTORY:
	case AVH_USER3_ADVANCED_TURRET_FACTORY:
		theMessageID = BUILD_TURRET_FACTORY;
		break;

	case AVH_USER3_ARMORY:
	case AVH_USER3_ADVANCED_ARMORY:
		theMessageID = BUILD_ARMORY;
		break;

	case AVH_USER3_ARMSLAB:
		theMessageID = BUILD_ARMSLAB;
		break;

	case AVH_USER3_PROTOTYPE_LAB:
		theMessageID = BUILD_PROTOTYPE_LAB;
		break;

	case AVH_USER3_OBSERVATORY:
		theMessageID = BUILD_OBSERVATORY;
		break;

	case AVH_USER3_TURRET:
		theMessageID = BUILD_TURRET;
		break;

	case AVH_USER3_SIEGETURRET:
		theMessageID = BUILD_SIEGE;
		break;

	case AVH_USER3_INFANTRYPORTAL:
		theMessageID = BUILD_INFANTRYPORTAL;
		break;

	case AVH_USER3_PHASEGATE:
		theMessageID = BUILD_PHASEGATE;
		break;

	case AVH_USER3_HEAVY:
		theMessageID = BUILD_HEAVY;
		break;

	case AVH_USER3_JETPACK:
		theMessageID = BUILD_JETPACK;
		break;

	case AVH_USER3_DEFENSE_CHAMBER:
		theMessageID = ALIEN_BUILD_DEFENSE_CHAMBER;
		break;

	case AVH_USER3_MOVEMENT_CHAMBER:
		theMessageID = ALIEN_BUILD_MOVEMENT_CHAMBER;
		break;

	case AVH_USER3_OFFENSE_CHAMBER:
		theMessageID = ALIEN_BUILD_OFFENSE_CHAMBER;
		break;

	case AVH_USER3_SENSORY_CHAMBER:
		theMessageID = ALIEN_BUILD_SENSORY_CHAMBER;
		break;

	case AVH_USER3_ALIENRESTOWER:
		theMessageID = ALIEN_BUILD_RESOURCES;
		break;

	case AVH_USER3_HIVE:
		theMessageID = ALIEN_BUILD_HIVE;
		break;

	case AVH_USER3_ALIEN_PLAYER1:
		theMessageID = ALIEN_LIFEFORM_ONE;
		break;

	case AVH_USER3_ALIEN_PLAYER2:
		theMessageID = ALIEN_LIFEFORM_TWO;
		break;

	case AVH_USER3_ALIEN_PLAYER3:
		theMessageID = ALIEN_LIFEFORM_THREE;
		break;

	case AVH_USER3_ALIEN_PLAYER4:
		theMessageID = ALIEN_LIFEFORM_FOUR;
		break;

	case AVH_USER3_ALIEN_PLAYER5:
		theMessageID = ALIEN_LIFEFORM_FIVE;
		break;
	}

	if(theMessageID != MESSAGE_NULL)
	{
		outMessageID = theMessageID;
		theSuccess = true;
	}

	return theSuccess;
}

bool AvHSHUMessageIDToUser3(AvHMessageID inMessageID, AvHUser3& outUser3)
{
	bool theSuccess = false;

	AvHUser3 theUser3 = AVH_USER3_NONE;

	switch(inMessageID)
	{
	case BUILD_RESOURCES:
		theUser3 = AVH_USER3_RESTOWER;
		break;

	case BUILD_COMMANDSTATION:
		theUser3 = AVH_USER3_COMMANDER_STATION;
		break;

	case BUILD_TURRET_FACTORY:
		theUser3 = AVH_USER3_TURRET_FACTORY;
		break;

	case TURRET_FACTORY_UPGRADE:
		theUser3 = AVH_USER3_ADVANCED_TURRET_FACTORY;
		break;

	case BUILD_ARMORY:
		theUser3 = AVH_USER3_ARMORY;
		break;

	case ARMORY_UPGRADE:
		theUser3 = AVH_USER3_ADVANCED_ARMORY;
		break;

	case BUILD_ARMSLAB:
		theUser3 = AVH_USER3_ARMSLAB;
		break;

	case BUILD_PROTOTYPE_LAB:
		theUser3 = AVH_USER3_PROTOTYPE_LAB;
		break;

	case BUILD_OBSERVATORY:
		theUser3 = AVH_USER3_OBSERVATORY;
		break;

	case BUILD_TURRET:
		theUser3 = AVH_USER3_TURRET;
		break;

	case BUILD_SIEGE:
		theUser3 = AVH_USER3_SIEGETURRET;
		break;

	case BUILD_INFANTRYPORTAL:
		theUser3 = AVH_USER3_INFANTRYPORTAL;
		break;

	case BUILD_PHASEGATE:
		theUser3 = AVH_USER3_PHASEGATE;
		break;

	case BUILD_HEAVY:
		theUser3 = AVH_USER3_HEAVY;
		break;

	case BUILD_JETPACK:
		theUser3 = AVH_USER3_JETPACK;
		break;

	// Menus
	case MENU_BUILD:
		theUser3 = AVH_USER3_MENU_BUILD;
		break;
	case MENU_BUILD_ADVANCED:
		theUser3 = AVH_USER3_MENU_BUILD_ADVANCED;
		break;
	case MENU_ASSIST:
		theUser3 = AVH_USER3_MENU_ASSIST;
		break;
	case MENU_EQUIP:
		theUser3 = AVH_USER3_MENU_EQUIP;
		break;

	// Weapons
	case BUILD_MINES:
		theUser3 = AVH_USER3_MINE;
		break;

	// Lifeforms
	case ALIEN_LIFEFORM_ONE:
		theUser3 = AVH_USER3_ALIEN_PLAYER1;
		break;

	case ALIEN_LIFEFORM_TWO:
		theUser3 = AVH_USER3_ALIEN_PLAYER2;
		break;

	case ALIEN_LIFEFORM_THREE:
		theUser3 = AVH_USER3_ALIEN_PLAYER3;
		break;

	case ALIEN_LIFEFORM_FOUR:
		theUser3 = AVH_USER3_ALIEN_PLAYER4;
		break;

	case ALIEN_LIFEFORM_FIVE:
		theUser3 = AVH_USER3_ALIEN_PLAYER5;
		break;
	}

	if(theUser3 != AVH_USER3_NONE)
	{
		outUser3 = theUser3;
		theSuccess = true;
	}

	return theSuccess;
}


float AvHSHUGetTime()
{
	float theTime = 0;

	#ifdef AVH_SERVER
	theTime = gpGlobals->time;
	#else
	theTime = gEngfuncs.GetClientTime();
	#endif

	return theTime;
}



// Note, all these models must be precached already
char* AvHSHUGetBuildTechModelName(AvHMessageID inMessageID)
{
	char* theModelName = NULL;

	switch(inMessageID)
	{
	case BUILD_RESOURCES:
		theModelName = kResourceTowerModel;
		break;

	//case BUILD_REINFORCEMENTS:
	//	theModelName = kInfantryPortalModel;
	//	break;

	case BUILD_INFANTRYPORTAL:
		theModelName = kInfantryPortalModel;
		break;

	case BUILD_COMMANDSTATION:
		theModelName = kCommandStationModel;
		break;

	case BUILD_TURRET_FACTORY:
		theModelName = kTurretFactoryModel;
		break;

	case BUILD_ARMSLAB:
		theModelName = kArmsLabModel;
		break;

	case BUILD_PROTOTYPE_LAB:
		theModelName = kPrototypeLabModel;
		break;

	case BUILD_ARMORY:
		theModelName = kArmoryModel;
		break;

	case ARMORY_UPGRADE:
		theModelName = kAdvancedWeaponFactoryModel;
		break;

	case BUILD_OBSERVATORY:
		theModelName = kObservatoryModel;
		break;

	case BUILD_SCAN:
		theModelName = kScanModel;
		break;

	case BUILD_PHASEGATE:
		theModelName = kPhaseGateModel;
		break;

	case BUILD_TURRET:
		theModelName = kDeployedTurretModel;
		break;

	case BUILD_NUKE:
		theModelName = kNukeModel;
		break;

	case BUILD_SIEGE:
		theModelName = kSiegeTurretModel;
		//theModelName = kDeployedTurretModel;
		break;

    case BUILD_CAT:
        theModelName = kCatalystModel;
        break;

	case BUILD_HEALTH:
		theModelName = kHealthModel;
		break;

	case BUILD_HEAVY:
		theModelName = kHeavyModel;
		break;

	case BUILD_JETPACK:
		theModelName = kJetpackModel;
		break;

	case BUILD_AMMO:
		theModelName = kAmmoModel;
		break;

	case BUILD_WELDER:
		theModelName = kWelderWModel;
		break;

	case BUILD_MINES:
		theModelName = kTripmineW2Model;
		break;

	case BUILD_SHOTGUN:
		theModelName = kSGWModel;
		break;

	case BUILD_HMG:
		theModelName = kHMGWModel;
		break;

	case BUILD_GRENADE_GUN:
		theModelName = kGGWModel;
		break;

	// Alien buildings
	case ALIEN_BUILD_HIVE:
		theModelName = kHiveModel;
		break;

	case ALIEN_BUILD_RESOURCES:
		theModelName = kAlienResourceTowerModel;
		break;

	case ALIEN_BUILD_OFFENSE_CHAMBER:
		theModelName = kOffenseChamberModel;
		break;

	case ALIEN_BUILD_DEFENSE_CHAMBER:
		theModelName = kDefenseChamberModel;
		break;

	case ALIEN_BUILD_SENSORY_CHAMBER:
		theModelName = kSensoryChamberModel;
		break;

	case ALIEN_BUILD_MOVEMENT_CHAMBER:
		theModelName = kMovementChamberModel;
		break;
	}

	return theModelName;
}

bool AvHSHUGetBuildTechRange(AvHMessageID inMessageID, float& outRange)
{
	bool theSuccess = false;

	//	switch(inMessageID)
	//	{
	//	case BUILD_CAMERA:
	//		outRange = kResourceTowerSightRange;
	//		theSuccess = true;
	//		break;
	//	case BUILD_PHASEGATE:
	//		break;
	//	case BUILD_TURRET:
	//		outRange = TURRET_RANGE;
	//		theSuccess = true;
	//		break;
	//	case BUILD_SIEGE:
	//		outRange = kSiegeTurretMaxRange;
	//		theSuccess = true;
	//		break;
	//	}

	return theSuccess;
}

bool AvHSHUTraceLineIsAreaFree(Vector& inStart, Vector& inEnd, edict_t* inIgnoreEntity, bool inIgnorePlayers)
{
	bool theAreaIsFree = true;

#ifdef AVH_SERVER
	TraceResult theTR;
	bool theIsDone = false;

	// Do tracelines between the corners, to make sure there's no geometry inside the box
	int theNumIters = 0;

    IGNORE_MONSTERS theIgnoreMonsters = dont_ignore_monsters;

    if (inIgnorePlayers)
    {
        theIgnoreMonsters = ignore_monsters;
    }

	while(!theIsDone)
	{
		UTIL_TraceLine(inStart, inEnd, theIgnoreMonsters, inIgnoreEntity, &theTR);
		if(theTR.flFraction != 1.0f)
		{
			CBaseEntity* theEntity = CBaseEntity::Instance(ENT(theTR.pHit));
			if(theEntity && (theEntity->pev->solid != SOLID_BBOX) && (theEntity->pev->solid != SOLID_SLIDEBOX) && (theEntity->pev->solid != SOLID_BSP) && (inIgnorePlayers != !!theEntity->IsPlayer()) )
			{
				VectorCopy(theTR.vecEndPos, inStart);
			}
			else
			{
				theIsDone = true;
				theAreaIsFree = false;
			}
		}
		else
		{
			theIsDone = true;
		}

		if(theNumIters++ > 50)
		{
			theIsDone = true;
			theAreaIsFree = false;
		}
	}
#endif

//	int theIndex;
//	vec3_t theEndLocation;
//	AvHTeamNumber theTeam;
//	bool thePlayerHit;
//	int theUserThree;
//	int theUserFour;
//
//	if(AvHSHUTraceTangible(inStart, inEnd, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour))
//	{
//		if(theIndex >= 0)
//		{
//			theAreaIsFree = false;
//		}
//	}

	return theAreaIsFree;
}

float AvHTraceLineAgainstWorld(Vector& vecStart, Vector& vecEnd)
{

#ifdef AVH_SERVER

    TraceResult tr;
    UTIL_TraceLine(vecStart, vecEnd, ignore_monsters, dont_ignore_glass, NULL, &tr);

    return tr.flFraction;

#endif

#ifdef AVH_CLIENT

    // This may not be the most efficient way, but it seems to get the job done.

    float theFraction = 1;

    for (int i = 0; i < kMaxEntities && theFraction > 0; ++i)
    {

        cl_entity_t* theEntity = gEngfuncs.GetEntityByIndex(i);

        if (theEntity != NULL && theEntity->model != NULL && theEntity->model->type == mod_brush)
        {

            vec3_t localStart;
            vec3_t localEnd;

            // Translate the start and end into the model's frame of reference.

            VectorSubtract(vecStart, theEntity->origin, localStart);
            VectorSubtract(vecEnd, theEntity->origin, localEnd);

            // Rotate the start and end into the model's frame of reference.

            if (theEntity->angles[0] ||
                theEntity->angles[1] ||
                theEntity->angles[2])
            {

                vec3_t forward;
                vec3_t right;
                vec3_t up;

                AngleVectors(theEntity->angles, forward, right, up);

                vec3_t temp;

                VectorCopy(localStart, temp);
                localStart[0] =  DotProduct(temp, forward);
                localStart[1] = -DotProduct(temp, right);
                localStart[2] =  DotProduct(temp, up);

                VectorCopy(localEnd, temp);
                localEnd[0] =  DotProduct(temp, forward);
                localEnd[1] = -DotProduct(temp, right);
                localEnd[2] =  DotProduct(temp, up);

            }

            trace_t tr;
            NS_TraceLine(&theEntity->model->hulls[0], localStart, localEnd, &tr);

            if (tr.fraction < theFraction)
            {
                theFraction = tr.fraction;
            }

        }

    }

    return theFraction;

#endif

}


bool AvHSHUGetCanBeBuiltOnPlayers(AvHMessageID inMessageID)
{
	bool theCanBeBuiltOnPlayers = false;

	switch(inMessageID)
	{
		case ALIEN_BUILD_HIVE:
		case BUILD_SCAN:
		case BUILD_AMMO:
		case BUILD_HEALTH:
		case BUILD_CAT:
		case BUILD_MINES:
		case BUILD_WELDER:
		case BUILD_SHOTGUN:
		case BUILD_HMG:
		case BUILD_GRENADE_GUN:
		case BUILD_HEAVY:
		case BUILD_JETPACK:
			theCanBeBuiltOnPlayers = true;
			break;
	}

	return theCanBeBuiltOnPlayers;
}

bool AvHSHUGetIsSiteValidForBuild(AvHMessageID inMessageID, Vector* inLocation, int inIgnoredEntityIndex)
{
	//TODO: - check entity returned by client side commander trace for drop validity before we ever get this far

	bool theSuccess = false;

	// Check that there's enough room for building
	vec3_t theMinSize, theMaxSize;
	if(AvHSHUGetSizeForTech(inMessageID, theMinSize, theMaxSize, true))
	{

		// If it's a build with special placement requirements, check that here (this could modify the location also)
		if(AvHSHUGetAreSpecialBuildingRequirementsMet(inMessageID, *inLocation))
		{

			// Assume mappers place hives correctly (it's such a showstopper if aliens can't create a hive because it's too close to a wall)
			// KGP 4/28/04 - add res towers to hives for the same reason -- if a mapper didn't give room, it's a showstopper.
			bool theIgnorePlayers = AvHSHUGetCanBeBuiltOnPlayers(inMessageID);
			float theSlopeTangent = theIgnorePlayers ? kMaxEquipmentDropSlope : kMaxBuildingDropSlope;
			bool theSkipDropCheck = false;
			switch(inMessageID)
			{
				case BUILD_RESOURCES:
					theSkipDropCheck = true;
					break;
				case ALIEN_BUILD_RESOURCES:
					theSkipDropCheck = true;
					break;
				case ALIEN_BUILD_HIVE:
					theSkipDropCheck = true;
					break;
			}
			if(theSkipDropCheck || AvHSHUGetCanDropItem(*inLocation, theMinSize, theMaxSize, theSlopeTangent, inIgnoredEntityIndex, theIgnorePlayers))
			{
				//TODO: - better check for entity below drop, since it's often off center.
				// Check to make sure building isn't being created on top of another building
				vec3_t thePositionBelowBuild = *inLocation;
				thePositionBelowBuild.z = -kMaxMapDimension;

				int theIndex;
				vec3_t theEndLocation;
				AvHTeamNumber theTeam;
				bool thePlayerHit;
				int theUserThree;
				int theUserFour;

				// Don't allow building on any entities, except allow resource towers on top of func_resources

				//TODO : use full list instead of physents, which isn't accounting for items the commander can't see
				bool theHitSomething = AvHSHUTraceTangible(*inLocation, thePositionBelowBuild, theIndex, theEndLocation, theTeam, thePlayerHit, theUserThree, theUserFour);

				//res building case
				if(inMessageID == BUILD_RESOURCES || inMessageID == ALIEN_BUILD_RESOURCES)
				{
#ifdef AVH_SERVER	//on server, return false if occupied
					FOR_ALL_ENTITIES(kesFuncResource,AvHFuncResource*)
                        if(!theEntity->GetIsOccupied()) // open for use
						{
							if(VectorDistance(theEntity->pev->origin,*inLocation) < 20) //small enough that we don't check the wrong nozzle
							{
								theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE);
								break;
							}
						}
					END_FOR_ALL_ENTITIES(kesFuncResource)
#else //on client the occupied function isn't available
					theSuccess = (!theHitSomething || theIndex <= 0 || theUserThree == AVH_USER3_FUNC_RESOURCE);
#endif
				}
				else if ( inMessageID == ALIEN_BUILD_HIVE ) 
				{
					theSuccess = true;
					//theSuccess = ( !theHitSomething || ( *inLocation[2] - theEndLocation[2] > 40.0f ) );
//#ifdef AVH_SERVER
//					ALERT(at_console, "theHitSomething=%d theSuccess=%d\n", theHitSomething, theSuccess);
//					ALERT(at_console, "%f\n", *inLocation[2] - theEndLocation[2]);
//					ALERT(at_console, "{%f, %f, %f} : { %f, %f, %f }\n", 
//						inLocation[0], inLocation[1], inLocation[2], 
//						theEndLocation[0], theEndLocation[1], theEndLocation[2]);
//#endif
				}
				else if(!theHitSomething || (theIgnorePlayers && thePlayerHit) || theIndex <= 0)
				{
					// THEN it's a legal build spot and the building shows up green.
					theSuccess = true;
				}
			}
		}
	}
	return theSuccess;
}

int AvHSHUGetPointContents(vec3_t inPoint)
{
	int thePointContents = 0;

	#ifdef AVH_SERVER
	thePointContents = UTIL_PointContents(inPoint);
	#endif

	#ifdef AVH_CLIENT
	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
	gEngfuncs.pEventAPI->EV_PushPMStates();
	gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1);
	gEngfuncs.pEventAPI->EV_SetTraceHull(2);

	thePointContents = gEngfuncs.PM_PointContents(inPoint, NULL);

	gEngfuncs.pEventAPI->EV_PopPMStates();
	#endif

	return thePointContents;
}

 /*
 * Check to see if the build or item is overlapping the world.
 */
bool AvHSHUGetIsVolumeContentNonsolid(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
{

	float theMins[3], theMaxs[3];
	VectorAdd(inCenter,inMinSize,theMins);
	VectorAdd(inCenter,inMaxSize,theMaxs);

	bool theIsNonsolid = true; //innocent until proven guilty

	extern playermove_t *pmove;
    if (pmove != NULL && pmove->physents[0].model != NULL)
    {
		const hull_t* theHull = &pmove->physents[0].model->hulls[NS_GetValveHull(2)];
		int theContents = NS_BoxContents(theHull,theHull->firstclipnode,theMins,theMaxs);
		theIsNonsolid = (theContents != CONTENT_SOLID);
	}
    else //fallback if we're just starting a round
    {
        int theContents = AvHSHUGetPointContents(inCenter);
		if(theContents != CONTENT_SOLID)
		{
			//trace between each corner pair once looking for obstructions - 28 total comparisons
			const static int MIN = 0;
			const static int MAX = 1;
			const static int NUM_CORNERS = 8;
			int theIndex;
			Vector theCorners[NUM_CORNERS];

			for(int XPos = MIN; XPos <= MAX; ++XPos)
			{
				for(int YPos = MIN; YPos <= MAX; ++YPos)
				{
					for(int ZPos = MIN; ZPos <= MAX; ++ZPos)
					{
						theIndex = XPos+YPos*2+ZPos*4;
						theCorners[theIndex].x = inCenter.x + ((XPos == MIN) ? inMinSize.x : inMaxSize.x);
						theCorners[theIndex].y = inCenter.y + ((YPos == MIN) ? inMinSize.y : inMaxSize.y);
						theCorners[theIndex].z = inCenter.z + ((ZPos == MIN) ? inMinSize.z : inMaxSize.z);
					}
				}
			}

			for(int startCorner = 0; startCorner < NUM_CORNERS; ++startCorner)
			{
				for(int endCorner = startCorner+1; endCorner < NUM_CORNERS; ++endCorner)
				{
					if(!AvHSHUTraceLineIsAreaFree(theCorners[startCorner],theCorners[endCorner],inIgnoreEntity,inIgnorePlayers))
					{
						theIsNonsolid = false;
						break;
					}
				}
				if(!theIsNonsolid)
				{
					break;
				}
			}
		}
		else
		{
			theIsNonsolid = false;
		}
	}
	return theIsNonsolid;
}

bool AvHSHUGetIsAreaFree(vec3_t inCenter, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
{
	bool theAreaIsFree = AvHSHUGetIsVolumeContentNonsolid(inCenter,inMinSize,inMaxSize,inIgnoreEntity,inIgnorePlayers);

	if(theAreaIsFree)
	{
		theAreaIsFree = !AvHSHUGetEntitiesBlocking(inCenter, inMinSize, inMaxSize, inIgnoreEntity, inIgnorePlayers);
	}

	return theAreaIsFree;
}

bool AvHSHUGetEntitiesBlocking(Vector& inOrigin, Vector& inMinSize, Vector& inMaxSize, edict_t* inIgnoreEntity, bool inIgnorePlayers)
{
	bool theEntBlocking = false;

	typedef vector< pair<int, Vector> > PhysEntListType;
	PhysEntListType theEntList;

	const int kSearchRadius = 800;

	// Populate phys ent list
	#ifdef AVH_SERVER
	CBaseEntity* theEntity = NULL;
	while ((theEntity = UTIL_FindEntityInSphere(theEntity, inOrigin, kSearchRadius)) != NULL)
	{

        // If entity is visible non-world object, add it
		if((theEntity->pev->team != TEAM_IND) && (theEntity->pev->solid != SOLID_NOT && theEntity->pev->solid != SOLID_TRIGGER))
		{

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

            if(theEntity->edict() != inIgnoreEntity && !(thePlayer != NULL && inIgnorePlayers))
			{
				theEntList.push_back(make_pair(theEntity->pev->iuser3, theEntity->pev->origin));
			}
		}
	}
	#endif

	#ifdef AVH_CLIENT
	// Haven't implemented this, so depend on it not being passed in
	ASSERT(inIgnoreEntity == NULL);

	for(int i = 0; i < pmove->numphysent; i++)
	{
		physent_t* thePhysEnt = pmove->physents + i;
		if((thePhysEnt->team != TEAM_IND) && (thePhysEnt->solid != SOLID_NOT && thePhysEnt->solid != SOLID_TRIGGER))
		{
			theEntList.push_back(make_pair(thePhysEnt->iuser3, thePhysEnt->origin));
		}
	}
	#endif

	Vector theAbsMax;
	VectorAdd(inOrigin, inMaxSize, theAbsMax);

	Vector theAbsMin;
	VectorAdd(inOrigin, inMinSize, theAbsMin);

	Vector theOrigin1;
	VectorAdd(theAbsMax, theAbsMin, theOrigin1);
	theOrigin1 = theOrigin1*.5f;

	Vector theSize1;
	VectorSubtract(theAbsMax, theAbsMin, theSize1);
	theSize1 = theSize1*.5f;

	// Do collision on list, making sure none are too close to inOrigin
	for(PhysEntListType::iterator theIter = theEntList.begin(); theIter != theEntList.end(); theIter++)
	{
		// Get size for tech
		Vector theMinSize, theMaxSize;
		if(AvHSHUGetSizeForUser3(AvHUser3(theIter->first), theMinSize, theMaxSize))
		{
			Vector theEntOrigin = theIter->second;

			Vector theAbsMax;
			VectorAdd(theEntOrigin, theMaxSize, theAbsMax);

			Vector theAbsMin;
			VectorAdd(theEntOrigin, inMinSize, theAbsMin);

			Vector theOrigin2;
			VectorAdd(theAbsMax, theAbsMin, theOrigin2);
			theOrigin2 = theOrigin2*.5f;

			Vector theSize2;
			VectorSubtract(theAbsMax, theAbsMin, theSize2);
			theSize2 = theSize2*.5f;

			// Do simple box collision here
			if(NS_BoxesOverlap((float*)theOrigin1, (float*)theSize1, (float*)theOrigin2, (float*)theSize2))
			{
				theEntBlocking = true;
				break;
			}
		}
	}

	return theEntBlocking;
}



void AvHSHUMakeViewFriendlyKillerName(string& ioKillerName)
{
	string theOutputName = ioKillerName;
	int theStrLen = (int)ioKillerName.length();

	if(!strncmp(ioKillerName.c_str(), "weapon_", 7))
	{
		theOutputName = ioKillerName.substr(7);
	}
	else if(!strncmp(ioKillerName.c_str(), "monster_", 8))
	{
		theOutputName = ioKillerName.substr(8);
	}
	else if(!strncmp(ioKillerName.c_str(), "func_", 5))
	{
		theOutputName = ioKillerName.substr(5);
	}

	ioKillerName = theOutputName;
}

bool AvHSHUGetCanDropItem(vec3_t& ioCenter, Vector& inMinSize, Vector& inMaxSize, float inMaxSlopeTangent, int inIgnoreIndex, bool inIgnorePlayers)
{

	float theMaxXWidth = max(-inMinSize[0],inMaxSize[0]);
	float theMaxYWidth = max(-inMinSize[1],inMaxSize[1]);
	float theRadius = sqrt(theMaxXWidth*theMaxXWidth + theMaxYWidth * theMaxYWidth);
	float theHeight = inMaxSize[2] - inMinSize[2];
	//adjust origin to be base
	float theOrigin[3] = { ioCenter[0], ioCenter[1], ioCenter[2] + inMinSize[2] };

	CollisionChecker Checker(pmove,kHLPointHullIndex,CollisionChecker::HULL_TYPE_ALL,inIgnorePlayers,CollisionChecker::IGNORE_NONE,inIgnoreIndex);
	bool theCanDropItem = (Checker.GetContentsInCylinder(theOrigin,theRadius,theHeight) != CONTENTS_SOLID);


	if(!theCanDropItem) //can't place it -- can we drop it?
	{

		float theMaxDropHeight = theRadius * inMaxSlopeTangent;

		float theDropOrigin[3] = { theOrigin[0], theOrigin[1], theOrigin[2] + theMaxDropHeight };
		theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID);

		if(theCanDropItem) //can drop it -- get as low to ground as possible for drop
		{

			float theBestDropHeight = theMaxDropHeight;
			float theShiftFraction = 1.0f;
			for(float theShiftAdjust = 0.5f; theShiftAdjust > 0.05f; theShiftAdjust /= 2)
			{
				theShiftFraction += theShiftAdjust * (theCanDropItem ? -1 : 1); //try lower if last was a success
				theDropOrigin[2] = theMaxDropHeight;
				theDropOrigin[2] *= theShiftFraction;
				theDropOrigin[2] += theOrigin[2];

				theCanDropItem = (Checker.GetContentsInCylinder(theDropOrigin,theRadius,theHeight) != CONTENTS_SOLID);
				if(theCanDropItem)
				{
					theBestDropHeight = theShiftFraction;
					theBestDropHeight *= theMaxDropHeight;
				}
			}

			//adjust center to best position
			ioCenter[2] += theBestDropHeight;
			theCanDropItem = true;
		}
	}

	return theCanDropItem;
}

bool AvHSHUTraceAndGetIsSiteValidForBuild(AvHMessageID inMessageID, const Vector& inPointOfView, const Vector& inNormRay, Vector* outLocation)
{
	bool theSuccess = false;

	// TODO: Check if area is within the mapextents

	int theUser3;
	bool theTraceSuccess = AvHSHUTraceTangible(inPointOfView, inNormRay, &theUser3, outLocation);

	// tankefugl: 0000291
	// ignore trace for scans (removed due to cost being paid when drop failed)
	//if (inMessageID == BUILD_SCAN)
	//{
	//	theSuccess = true;
	//}
    //else
	// :tankefugl
	if(theTraceSuccess)
	{
		// tankefugl: 0000291
		if((inMessageID == BUILD_SCAN) || (AvHSHUGetIsSiteValidForBuild(inMessageID, outLocation)))
		// :tankefugl
		{
			theSuccess = true;
		}

	}
	return theSuccess;
}

#ifdef AVH_CLIENT
bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex)
{
	bool theSuccess = false;

	// Offset starting position a little so we don't select ourselves
	Vector theStartPosition;
	VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPosition);

	Vector theEndPos;
	VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos);

	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );

	// Store off the old count
	gEngfuncs.pEventAPI->EV_PushPMStates();

	// Now add in all of the players.
	int theNumPlayers = gEngfuncs.GetMaxClients();
	gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 );

	bool theDone = false;
	int theEntityIndex = -1;

	vec3_t theNewStartPos;
	AvHSHUGetFirstNonSolidPoint(theStartPosition, theEndPos, theNewStartPos);
	VectorCopy(theNewStartPos, theStartPosition);

	do
	{
		pmtrace_t tr;
		gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
		gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPosition, theEndPos, PM_NORMAL, theEntityIndex, &tr );

		physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
		theEntityIndex = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );

		if(pEntity)
		{
			int theUser3 = pEntity->iuser3;
			if(theEntityIndex > 0)
			{
				outEntIndex = theEntityIndex;
				theSuccess = true;
				theDone = true;
			}
		}

		if((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction < kFloatTolerance))
		{
			theDone = true;
		}
		else
		{
			VectorCopy(tr.endpos, theStartPosition);
		}

	} while(!theDone);

	gEngfuncs.pEventAPI->EV_PopPMStates();

	return theSuccess;
}
#endif

#ifdef AVH_SERVER
bool AvHSHUGetEntityAtRay(const Vector& inPointOfView, const Vector& inNormRay, int& outEntIndex)
{
	TraceResult		tr;
	Vector			theStartPos;
	Vector			theEndPos;
	bool			theSuccess = false;
	bool			theDone = false;

	VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theStartPos);
	VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theEndPos);

	CBaseEntity* theEntityHit = NULL;
	edict_t* theEdictToIgnore = NULL;

	vec3_t theNewStartPos;
	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
	VectorCopy(theNewStartPos, theStartPos);

	do
	{
		UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, ignore_glass, theEdictToIgnore, &tr);

		theEntityHit = CBaseEntity::Instance(tr.pHit);
		if(theEntityHit)
		{
			if(theEntityHit->entindex() > 0)
			{
				outEntIndex = theEntityHit->entindex();
				theSuccess = true;
				theDone = true;
			}
		}


		if((tr.flFraction > (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance))
		{
			theDone = true;
		}
		else
		{
			if(theEntityHit)
			{
				theEdictToIgnore = ENT(theEntityHit->pev);
			}
			VectorCopy(tr.vecEndPos, theStartPos);
		}
	} while(!theDone);

	return theSuccess;
}
#endif

const AvHMapExtents& AvHSHUGetMapExtents()
{
	#ifdef AVH_CLIENT
	const AvHMapExtents& theMapExtents = gHUD.GetMapExtents();
	#endif

	#ifdef AVH_SERVER
	const AvHMapExtents& theMapExtents = GetGameRules()->GetMapExtents();
	#endif

	return theMapExtents;
}


#ifdef AVH_CLIENT
bool AvHSUClientTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
{
	physent_t*	theEntity = NULL;
	vec3_t		theStartPos;
	vec3_t		theEndPos;
	int			theFoundEntity = -1;
	bool		theSuccess = false;
	bool		theDone = false;

	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inEndPos, theEndPos);

	vec3_t theNormal;
	VectorSubtract(inEndPos, inStartPos, theNormal);
	VectorNormalize(theNormal);

	outIndex = -1;

	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );

	// Store off the old count
	gEngfuncs.pEventAPI->EV_PushPMStates();

	// Now add in all of the players.
	int theNumPlayers = gEngfuncs.GetMaxClients();
	gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 );

	vec3_t theNewStartPos;
	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
	VectorCopy(theNewStartPos, theStartPos);

	do
	{
		pmtrace_t tr;
		gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
		gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theFoundEntity, &tr );

		physent_t *pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
		theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );

		if(pEntity)
		{
			if(pEntity->iuser3 != AVH_USER3_NONE)
			{
				VectorCopy(tr.endpos, outLocation);
				outIndex = theFoundEntity;
				theEntity = pEntity;
				outUserThree = pEntity->iuser3;
				outUserFour = pEntity->iuser4;
				outTeamNumber = (AvHTeamNumber)(pEntity->team);

				if(pEntity->player)
				{
					outPlayerWasHit = true;
				}

				theSuccess = true;
				theDone = true;
			}
		}

		if(tr.fraction >= (1.0f - kFloatTolerance))
		{
			theDone = true;
		}
		else
		{
			vec3_t theDiff = theNormal*kHitOffsetAmount;
			float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
			if(theLength > kFloatTolerance)
			{
				VectorAdd(tr.endpos, theDiff, theStartPos);
			}
			else
			{
				theDone = true;
			}
			// Offset a bit so we don't hit again
			//VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos);
		}
	} while(!theDone);

	gEngfuncs.pEventAPI->EV_PopPMStates();

	// If we didn't hit any special targets, see if it's a valid area to build or be ordered to
	if(!theSuccess)
	{
		WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS;
		theSuccess = AvHSHUClientTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode);
		bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP);

		if(theSuccess || theWaypointTooSteep)
		{
			outUserThree = AVH_USER3_WAYPOINT;
			outPlayerWasHit = false;
			outIndex = -1;
			outTeamNumber = TEAM_IND;
			theSuccess = true;
		}

		// Treat too steep as success, but mark it as nobuild
		if(theWaypointTooSteep)
		{
			outUserThree = AVH_USER3_NOBUILD;
		}
	}

	return theSuccess;
}
#endif

#ifdef AVH_SERVER
bool AvHSUServerTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
{
	bool theSuccess = false;
	bool theDone = false;
	edict_t* theEdictToIgnore = NULL;
	vec3_t theStartPos;
	vec3_t theEndPos;

	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inEndPos, theEndPos);

	vec3_t theNormal;
	VectorSubtract(inEndPos, inStartPos, theNormal);
	VectorNormalize(theNormal);

//	vec3_t theNewStartPos;
//	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
//	VectorCopy(theNewStartPos, theStartPos);

	do
	{
		TraceResult tr;
		UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr);
		CBaseEntity* theEntityHit = NULL;

		// Return the entity in special way
		if(tr.flFraction < 1 && tr.pHit)
		{
			theEntityHit = CBaseEntity::Instance(tr.pHit);
			AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theEntityHit);
			AvHWeldable* theWeldable = dynamic_cast<AvHWeldable*>(theEntityHit);

			AvHBasePlayerWeapon* theWeapon = dynamic_cast<AvHBasePlayerWeapon*>(theEntityHit);

			if(AvHSUGetIsDebugging())
			{
				// Create an entity where the trace hit
				Vector theAngles(0, 0, 0);

				CBaseEntity* pEnt = CBaseEntity::Create(kwsDebugEntity, tr.vecEndPos, theAngles);
				ASSERT(pEnt);
				pEnt->pev->movetype = MOVETYPE_FLY;
				pEnt->pev->solid = SOLID_NOT;
			}

			if(theEntityHit && (theEntityHit->pev->iuser3 != AVH_USER3_NONE))
			{
				// Don't hit seethroughs
				if(theEntityHit->pev->iuser3 != AVH_USER3_ALPHA)
				{
					outIndex = ENTINDEX(tr.pHit);
					VectorCopy(tr.vecEndPos, outLocation);
					outTeamNumber = (AvHTeamNumber)theEntityHit->pev->team;
					outUserThree = theEntityHit->pev->iuser3;
					outUserFour = theEntityHit->pev->iuser4;

					if(thePlayer)
					{
						outPlayerWasHit = true;
					}

					theSuccess = true;
					theDone = true;
				}
			}

			theEdictToIgnore = theEntityHit->edict();
		}

		if((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance))
		{
			theDone = true;
		}
		else
		{
			vec3_t theDiff = theNormal*kHitOffsetAmount;
			float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
			if(theLength > kFloatTolerance)
			{
				VectorAdd(theStartPos, theDiff, theStartPos);
			}
			else
			{
				theDone = true;
			}
			// Offset a bit so we don't hit again
			//VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos);
		}
	} while(!theDone);

	if(!theSuccess)
	{
		WaypointReturnCode theReturnCode = WAYPOINT_SUCCESS;
		theSuccess = AvHSHUServerTraceWaypoint(inStartPos, inEndPos, &outLocation, &theReturnCode);
		bool theWaypointTooSteep = (theReturnCode == WAYPOINT_TOOSTEEP);
		if(theSuccess || theWaypointTooSteep)
		{
			outUserThree = AVH_USER3_WAYPOINT;
			outPlayerWasHit = false;
			outIndex = -1;
			outTeamNumber = TEAM_IND;
			theSuccess = true;
		}
		// Treat too steep as success, but mark it as nobuild
		if(theWaypointTooSteep)
		{
			outUserThree = AVH_USER3_NOBUILD;
		}
	}

	return theSuccess;
}
#endif



#ifdef AVH_CLIENT
bool AvHSHUClientTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode)
{
	vec3_t		theStartPos;
	vec3_t		theEndPos;
	int			theFoundEntity = -1;
	int			theEntityToIgnore = -1;
	bool		theDone = false;
	bool		theLegalToBuild = false;

	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inEndPos, theEndPos);

	vec3_t theNormal;
	VectorSubtract(inEndPos, inStartPos, theNormal);
	VectorNormalize(theNormal);

	int theNumPlayers = gEngfuncs.GetMaxClients();
	pmtrace_t tr;

	//DebugPoint theDebugPoint(theStartPos[0], theStartPos[1], theStartPos[2]);
	//gSquareDebugLocations.push_back(theDebugPoint);

	gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
	gEngfuncs.pEventAPI->EV_PushPMStates();
	gEngfuncs.pEventAPI->EV_SetSolidPlayers(-1);
	gEngfuncs.pEventAPI->EV_SetTraceHull(2);

	vec3_t theNewStartPos;
	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
	VectorCopy(theNewStartPos, theStartPos);

	do
	{
		gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL/*PM_GLASS_IGNORE*/, theEntityToIgnore, &tr );

		physent_t* pEntity = gEngfuncs.pEventAPI->EV_GetPhysent( tr.ent );
		theFoundEntity = gEngfuncs.pEventAPI->EV_IndexFromTrace( &tr );

		// Trace until we hit a worldbrush or non-seethrough
		if(!pEntity || (pEntity->iuser3 != AVH_USER3_ALPHA))
		{
			// If entity is a "no waypoint" entity we can't build here and we're done
			if(pEntity && (pEntity->iuser3 == AVH_USER3_NOBUILD))
			{
				if(outReturnCode)
				{
					*outReturnCode = WAYPOINT_NOBUILD;
				}
				theDone = true;
			}
			else
			{
				// else if texture is NOBUILD, we're done
				const char* theTextureHitCStr = gEngfuncs.pEventAPI->EV_TraceTexture(theFoundEntity, tr.endpos, theEndPos);
				if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture)))
				{
					if(outReturnCode)
					{
						*outReturnCode = WAYPOINT_NOBUILD;
					}
					theDone = true;
				}
				else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture)))
				{
					// Not valid, but allow it to pass through
					if(outReturnCode)
					{
						*outReturnCode = WAYPOINT_SEETHROUGH;
					}
				}
				else
				{
					// Trace texture sometimes seems to miss entities that the start point lies within.  Don't count
					// the trace texture if it found the texture of the next entity below it
						// else if surface is more flat than vertical
					if(tr.plane.normal[2] >= 0.7f)
					{
						// and if surface isn't under water, lava, in the sky, etc.
						int thePointContents = gEngfuncs.PM_PointContents(tr.endpos, NULL);
						if(thePointContents == CONTENTS_EMPTY)
						{
							// and if there's enough room to build

							// we can build here, and we're done
							theLegalToBuild = true;
							theDone = true;

							if(outReturnCode)
							{
								*outReturnCode = WAYPOINT_SUCCESS;
							}
						}
						else
						{
							if(outReturnCode)
							{
								*outReturnCode = WAYPOINT_CONTENTSFULL;
								theDone = true;
							}
						}
					}
					else
					{
						if(outReturnCode)
						{
							if(theTextureHitCStr)
							{
								*outReturnCode = WAYPOINT_TOOSTEEP;
							}
							else
							{
								*outReturnCode = WAYPOINT_ENTITYHIT;
							}
							theDone = true;
						}
					}
				}
			}
		}

		if(theFoundEntity != 0)
		{
			theEntityToIgnore = theFoundEntity;
		}

		if(((tr.fraction >= (1.0f - kFloatTolerance)) || (tr.fraction == 0.0f)) /*&& !tr.startsolid && !tr.allsolid*/)
		{
			theDone = true;
		}
		else
		{
			vec3_t theDiff = theNormal*kHitOffsetAmount;
			float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
			if(theLength > kFloatTolerance)
			{
				VectorAdd(tr.endpos, theDiff, theStartPos);
			}
			else
			{
				theDone = true;
			}
			// Offset a bit so we don't hit again
			//VectorMA(tr.endpos, kHitOffsetAmount, theNormal, theStartPos);
		}
	} while(!theDone);

	gEngfuncs.pEventAPI->EV_PopPMStates();

	// Always set end location to show where invalid position is
	*outLocation = tr.endpos;

	return theLegalToBuild;
}
#endif

#ifdef AVH_CLIENT
void AvHSHUClientGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
{
	vec3_t theStartPos;
	vec3_t theEndPos;
	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inStartPos, outNonSolidPoint);
	VectorCopy(inEndPos, theEndPos);

	pmtrace_t tr;
	gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_NORMAL, -1, &tr );

	// Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine)
	if((tr.startsolid) && (tr.fraction == 0.0f))
	{
		int			theFoundEntity = -1;
		bool		theDone = false;

		vec3_t theStartToEnd;
		VectorSubtract(inEndPos, inStartPos, theStartToEnd);

		gEngfuncs.pEventAPI->EV_SetTraceHull(2);

		float theIncrement = 10.0f/theStartToEnd.Length();
		float theT = 0.0f;
		int theNumIterations = 0;

		do
		{
			theStartPos = inStartPos + theT*theStartToEnd;
			gEngfuncs.pEventAPI->EV_PlayerTrace( theStartPos, theEndPos, PM_WORLD_ONLY, -1, &tr );

			theNumIterations++;

			// If start point is solid, bisect area and move start point 1/2 between current start and current end
			if(tr.startsolid)
			{
				theT += theIncrement;
			}
			// else if start point isn't solid, bisect area and move start point back towards original start point
			else
			{
				theDone = true;
			}
		} while(!theDone && (theNumIterations < 200));

		// Always set end location to show where invalid position is
		if(!theDone)
		{
			outNonSolidPoint = inStartPos;
		}
		else
		{
			outNonSolidPoint = theStartPos;
		}
	}
}
#endif

#ifdef AVH_SERVER
void AvHSHUServerGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
{
	vec3_t theStartPos;
	vec3_t theEndPos;
	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inStartPos, outNonSolidPoint);
	VectorCopy(inEndPos, theEndPos);

	TraceResult tr;
	UTIL_TraceLine(theStartPos, theEndPos, ignore_monsters, NULL, &tr);

	bool theDone = false;

	// Put this workaround in because a bug in EV_PlayerTrace means that when it starts solid, tr.fraction isn't returned (but it is in UTIL_TraceLine)
	if((tr.fStartSolid) || (tr.flFraction == 0.0f))
	{
		int			theFoundEntity = -1;

		vec3_t theStartToEnd;
		VectorSubtract(inEndPos, inStartPos, theStartToEnd);

		float theIncrement = 10.0f/theStartToEnd.Length();
		float theT = 0.0f;
		int theNumIterations = 0;
		int thePointContents = 0;

		do
		{
			theStartPos = inStartPos + theT*theStartToEnd;
			thePointContents = UTIL_PointContents(theStartPos);

			theNumIterations++;

			// If start point is solid, bisect area and move start point 1/2 between current start and current end
			if((thePointContents == CONTENTS_SOLID) || (thePointContents == CONTENTS_SKY))
			{
				theT += theIncrement;
			}
			// else if start point isn't solid, bisect area and move start point back towards original start point
			else
			{
				theDone = true;
			}
		} while(!theDone && (theNumIterations < 200));

		// Always set end location to show where invalid position is
		outNonSolidPoint = theStartPos;
	}
	else
	{
		VectorCopy(tr.vecEndPos, outNonSolidPoint);
		theDone = true;
	}

	if(!theDone)
	{
		// When we don't hit anything, return start point
		VectorCopy(inStartPos, outNonSolidPoint);
	}

	VectorCopy(outNonSolidPoint, gPMDebugPoint);
}
#endif

void AvHSHUGetFirstNonSolidPoint(float* inStartPos, float* inEndPos, float* outNonSolidPoint)
{
	vec3_t theStartPos;
	vec3_t theEndPos;

	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inEndPos, theEndPos);

	vec3_t theNonSolidPoint;
	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNonSolidPoint);

	VectorCopy(theNonSolidPoint, outNonSolidPoint);
}

void AvHSHUGetFirstNonSolidPoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t& outNonSolidPoint)
{
	#ifdef AVH_CLIENT
	AvHSHUClientGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint);

	//extern DebugPointListType gSquareDebugLocations;
	//DebugPoint theDebugPoint(outNonSolidPoint[0], outNonSolidPoint[1], outNonSolidPoint[2]);
	//gSquareDebugLocations.push_back(theDebugPoint);
	#endif

	#ifdef AVH_SERVER
	AvHSHUServerGetFirstNonSolidPoint(inStartPos, inEndPos, outNonSolidPoint);
	#endif
}

#ifdef AVH_SERVER
bool AvHSHUServerTraceWaypoint(const vec3_t& inStartPos, const vec3_t& inEndPos, vec3_t* outLocation, WaypointReturnCode* outReturnCode)
{
	bool theLegalToBuild = false;
	bool theDone = false;
	edict_t* theEdictToIgnore = NULL;
	TraceResult tr;
	vec3_t theStartPos;
	vec3_t theEndPos;

	VectorCopy(inStartPos, theStartPos);
	VectorCopy(inEndPos, theEndPos);

	vec3_t theNormal;
	VectorSubtract(inEndPos, inStartPos, theNormal);
	VectorNormalize(theNormal);

	vec3_t theNewStartPos;
	AvHSHUGetFirstNonSolidPoint(theStartPos, theEndPos, theNewStartPos);
	VectorCopy(theNewStartPos, theStartPos);

	do
	{
		UTIL_TraceLine(theStartPos, theEndPos, dont_ignore_monsters, theEdictToIgnore, &tr);

		// Trace until we hit a worldbrush or non-seethrough
		CBaseEntity* theEntityHit = CBaseEntity::Instance(tr.pHit);
		CWorld* theWorld = dynamic_cast<CWorld*>(theEntityHit);
		bool theHitSeethrough = (theEntityHit->pev->iuser3 == AVH_USER3_ALPHA);
		if(!theHitSeethrough)
		{
			// If entity is a "no waypoint" entity we can't build here and we're done
			if(theEntityHit && (theEntityHit->pev->iuser3 == AVH_USER3_NOBUILD))
			{
				if(outReturnCode)
				{
					*outReturnCode = WAYPOINT_NOBUILD;
				}
				theDone = true;
			}
			else
			{
				edict_t* theEdict = ENT(theEntityHit->pev);
				const char* theTextureHitCStr = TRACE_TEXTURE(theEdict, tr.vecEndPos, theEndPos);
				if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kNoBuildTexture)))
				{
					if(outReturnCode)
					{
						*outReturnCode = WAYPOINT_NOBUILD;
					}
					theDone = true;
				}
				else if(theTextureHitCStr && (LowercaseString(theTextureHitCStr) == LowercaseString(kSeeThroughTexture)))
				{
					// Do nothing, but allow it to pass through
					if(outReturnCode)
					{
						*outReturnCode = WAYPOINT_SEETHROUGH;
					}
				}
				else
				{
					// else if surface is more flat than vertical
					if(tr.vecPlaneNormal.z >= .7f)
					{
						// and if surface isn't under water, lava, sky
						int thePointContents = UTIL_PointContents(tr.vecEndPos);
						if(thePointContents == CONTENTS_EMPTY)
						{
							// and if there's enough room to build
							if(outReturnCode)
							{
								*outReturnCode = WAYPOINT_SUCCESS;
							}

							// we can build here, and we're done
							theLegalToBuild = true;
							theDone = true;
						}
						else
						{
							if(outReturnCode)
							{
								*outReturnCode = WAYPOINT_CONTENTSFULL;
							}
						}
					}
					else
					{
						if(theTextureHitCStr)
						{
							*outReturnCode = WAYPOINT_TOOSTEEP;
						}
						else
						{
							*outReturnCode = WAYPOINT_ENTITYHIT;
						}
						theDone = true;
					}
				}
			}

			if(!theWorld)
			{
				theEdictToIgnore = theEntityHit->edict();
			}

			if(((tr.flFraction >= (1.0f - kFloatTolerance)) || (tr.flFraction < kFloatTolerance)) /*&& !tr.fStartSolid && !tr.fAllSolid*/)
			{
				theDone = true;
			}
			else
			{
				vec3_t theDiff = theNormal*kHitOffsetAmount;
				float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
				if(theLength > kFloatTolerance)
				{
					//VectorAdd(tr.vecEndPos, theDiff, theStartPos);
					VectorAdd(theStartPos, theDiff, theStartPos);
				}
				else
				{
					theDone = true;
				}
				// Offset a bit so we don't hit again
				//VectorMA(tr.vecEndPos, kHitOffsetAmount, theNormal, theStartPos);
			}
		}
		else
		{
			theEdictToIgnore = theEntityHit->edict();
			if(tr.vecEndPos == theStartPos)
			{
				vec3_t theDiff = theNormal*kHitOffsetAmount;
				float theLength = sqrt(theDiff.x*theDiff.x + theDiff.y*theDiff.y + theDiff.z*theDiff.z);
				if(theLength > kFloatTolerance)
				{
					//VectorAdd(tr.vecEndPos, theDiff, theStartPos);
					VectorAdd(theStartPos, theDiff, theStartPos);
				}
			}
			else
			{
				VectorCopy(tr.vecEndPos, theStartPos);
			}
//			vec3_t theEntityPosition = AvHSHUGetRealLocation(theEntityHit->pev->origin, theEntityHit->pev->mins, theEntityHit->pev->maxs);
//			//VectorCopy(theEntityPosition, theStartPos);
//			theStartPos[2] = theEntityPosition[2];
		}
	} while(!theDone);

	// Always set end location to show where invalid position is
	*outLocation = tr.vecEndPos;

	return theLegalToBuild;
}
#endif

bool AvHSHUTraceTangible(const vec3_t& inStartPos, const vec3_t& inEndPos, int& outIndex, vec3_t& outLocation, AvHTeamNumber& outTeamNumber, bool& outPlayerWasHit, int& outUserThree, int& outUserFour)
{
	bool theSuccess = false;

	outPlayerWasHit = false;
	outUserThree = 0;
	outUserFour = 0;

	// On client, set up players for prediction and do a PM_TraceLine
	#ifdef AVH_CLIENT
	theSuccess = AvHSUClientTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour);
	#endif

	// On the server, do a UTIL_TraceLine
	#ifdef AVH_SERVER
	theSuccess = AvHSUServerTraceTangible(inStartPos, inEndPos, outIndex, outLocation, outTeamNumber, outPlayerWasHit, outUserThree, outUserFour);
	#endif

	return theSuccess;
}

bool AvHSHUTraceTangible(const Vector& inPointOfView, const Vector& inNormRay, int* outUserThree, vec3_t* outLocation, AvHTeamNumber* outTeamNumber, bool* outPlayerWasHit)
{
	bool theSuccess = false;
	vec3_t theTraceStart;
	vec3_t theTraceEnd;
	vec3_t theFoundLocation;
	int theFoundIndex = -1;
	int theUserThree = 0;
	int theUserFour = 0;
	AvHTeamNumber theTeamOfThingHit;
	bool thePlayerHit = false;

	// Offset a little so we don't hit the commander
	VectorMA(inPointOfView, kSelectionStartRange, inNormRay, theTraceStart);
	VectorMA(inPointOfView, kSelectionEndRange, inNormRay, theTraceEnd);

	if(!outUserThree)
		outUserThree = &theUserThree;

	if(!outLocation)
		outLocation = &theFoundLocation;

	if(!outTeamNumber)
		outTeamNumber = &theTeamOfThingHit;

	if(!outPlayerWasHit)
		outPlayerWasHit = &thePlayerHit;

	theSuccess = AvHSHUTraceTangible(theTraceStart, theTraceEnd, theFoundIndex, *outLocation, *outTeamNumber, *outPlayerWasHit, *outUserThree, theUserFour);

	return theSuccess;
}

bool AvHSHUTraceVerticalTangible(float inX, float inY, float inZ, int& outUserThree, float& outHeight)
{
	bool theSuccess = false;

	// Create a start pos above
	vec3_t theStartPos(inX, inY, inZ - 100.0f);
	//vec3_t theEndPos(inX, inY, inZ - 800.0f/*-4096.0f*/);
	vec3_t theEndPos(inX, inY, (float)kWorldMinimumZ);

//	#ifdef AVH_CLIENT
//	extern DebugPointListType				gTriDebugLocations;
//	bool theDrawDebug = true;//(rand() % 100 == 0);
//
//	if(theDrawDebug)
//	{
//		DebugPoint thePoint;
//		thePoint.x = theStartPos.x;
//		thePoint.y = theStartPos.y;
//		thePoint.z = theStartPos.z;
//		gTriDebugLocations.push_back(thePoint);
//
//		thePoint.z = theEndPos.z;
//		gTriDebugLocations.push_back(thePoint);
//	}
//
//	#endif

	int theIndex;
	vec3_t theEndLocation;
	AvHTeamNumber theTeam;
	bool thePlayerHit;
	int theUserFour;

	theSuccess = AvHSHUTraceTangible(theStartPos, theEndPos, theIndex, theEndLocation, theTeam, thePlayerHit, outUserThree, theUserFour);
	if(theSuccess)
	{
		outHeight = theEndLocation.z;
		ASSERT(outHeight <= inZ);
	}

	return theSuccess;
}

const char* AvHSHUGetCommonSoundName(bool inIsAlien, WeaponHUDSound inHUDSound)
{
	const char* theSoundName = NULL;

	switch(inHUDSound)
	{
	case WEAPON_SOUND_HUD_ON:
		theSoundName = inIsAlien ? "common/wpn_hudon-a.wav" : "common/wpn_hudon.wav";
		break;
	case WEAPON_SOUND_HUD_OFF:
		theSoundName = inIsAlien ? "common/wpn_hudoff-a.wav" : "common/wpn_hudoff.wav";
		break;
	case WEAPON_SOUND_MOVE_SELECT:
		theSoundName = inIsAlien ? "common/wpn_moveselect-a.wav" : "common/wpn_moveselect.wav";
		break;
	case WEAPON_SOUND_SELECT:
		theSoundName = inIsAlien ? "common/wpn_select-a.wav" : "common/wpn_select.wav";
		break;
	case WEAPON_SOUND_DENYSELECT:
		theSoundName = inIsAlien ? "common/wpn_denyselect-a.wav" : "common/wpn_denyselect.wav";
		break;
	}

	return theSoundName;
}


// Values for fuser1: 0-1 means it's building.  2-3 means it's researching (see kResearchFuser1Base), 3-4 is "energy" (returns both building and researching)
const float	kResearchFuser1Base = 2.0f;
const float	kEnergyFuser1Base = 3.0f;

void AvHSHUGetBuildResearchState(int inUser3, int inUser4, float inFuser1, bool& outIsBuilding, bool& outIsResearching, float& outNormalizedPercentage)
{
	outIsBuilding = outIsResearching = false;

	float theStatus = (inFuser1/kNormalizationNetworkFactor);

	if((GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD)) && !GetHasUpgrade(inUser4, MASK_RECYCLING))
	{
		if((theStatus >= 0.0f) && (theStatus <= 1.0f))
		{
			outNormalizedPercentage = theStatus;
			outIsBuilding = true;
		}
		if(inUser3 == AVH_USER3_WELD)
		{
			if(outNormalizedPercentage == -1)
			{
				outIsBuilding = outIsResearching = false;
			}
		}
	}
	else
	{
		if((theStatus > kResearchFuser1Base) && (theStatus <= (1.0f + kResearchFuser1Base)))
		{
			outNormalizedPercentage = theStatus - kResearchFuser1Base;
			outIsResearching = true;
		}
		else if((theStatus > kEnergyFuser1Base) && (theStatus <= (1.0f + kEnergyFuser1Base)))
		{
			// Set "energy"
			outNormalizedPercentage = theStatus - kEnergyFuser1Base;
			outIsBuilding = outIsResearching = true;
		}
		else
		{
			outNormalizedPercentage = 1.0f;
		}
	}
}

void AvHSHUSetEnergyState(int inUser3, float &outFuser1, float inNormalizedPercentage)
{
	outFuser1 = (kEnergyFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor;
}

void AvHSHUSetBuildResearchState(int inUser3, int inUser4, float &outFuser1, bool inTrueBuildOrFalseResearch, float inNormalizedPercentage)
{
	//ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) /*|| GetHasUpgrade(inUser4, MASK_SELECTABLE)*/ || (inUser3 == AVH_USER3_WELD));

	if(inNormalizedPercentage != -1)
	{
		inNormalizedPercentage = min(inNormalizedPercentage, 1.0f);
		inNormalizedPercentage = max(inNormalizedPercentage, 0.0f);
	}

	// Build
	if(inTrueBuildOrFalseResearch)
	{
		//ASSERT(GetHasUpgrade(inUser4, MASK_BUILDABLE) || (inUser3 == AVH_USER3_WELD));

		outFuser1 = inNormalizedPercentage*kNormalizationNetworkFactor;
	}
	// Research
	else
	{
		outFuser1 = (kResearchFuser1Base + inNormalizedPercentage)*kNormalizationNetworkFactor;
	}
}


string AvHSHUBuildExecutableScriptName(const string& inScriptName, const string& inCurrentMapName)
{
	string theExecutableScriptName = string(getModDirectory()) + string("/") + kScriptsDirectory + string("/") + inCurrentMapName + string("/") + inScriptName;

	return theExecutableScriptName;
}

string AvHSHUGetTimeDateString()
{
	string theTimeDateString("<date unknown>");

	time_t theTimeThingie = time ((time_t *) NULL);
	struct tm* theTime = localtime(&theTimeThingie);

	if(theTime)
	{
		// 04/22/03, 16:59:12
		char theBuffer[512];
		sprintf(theBuffer, "%.2d/%.2d/%.2d, %.2d:%.2d:%.2d", (theTime->tm_mon + 1), theTime->tm_mday, (theTime->tm_year - 100), theTime->tm_hour, theTime->tm_min, theTime->tm_sec);
		theTimeDateString = theBuffer;
	}

	return theTimeDateString;
}

bool AvHSHUGetNameOfLocation(const AvHBaseInfoLocationListType& inLocations, vec3_t inLocation, string& outLocation)
{
	bool theSuccess = false;

	// Look at our current position, and see if we lie within of the map locations
	for(AvHBaseInfoLocationListType::const_iterator theIter = inLocations.begin(); theIter != inLocations.end(); theIter++)
	{
		if(theIter->GetIsPointInRegion(inLocation))
		{
			outLocation = theIter->GetLocationName();
			theSuccess = true;
			break;
		}
	}

	return theSuccess;
}

// Also check AvHSUGetAlwaysPlayAlert to make sure some alerts are always triggered
bool AvHSHUGetForceHUDSound(AvHHUDSound inHUDSound)
{
    bool theForceSound = false;

    switch(inHUDSound)
    {
    case HUD_SOUND_ALIEN_HIVE_COMPLETE:
    case HUD_SOUND_ALIEN_HIVE_DYING:
    case HUD_SOUND_ALIEN_HIVE_ATTACK:
    case HUD_SOUND_MARINE_CCUNDERATTACK:
    case HUD_SOUND_SQUAD1:
    case HUD_SOUND_SQUAD2:
    case HUD_SOUND_SQUAD3:
    case HUD_SOUND_SQUAD4:
    case HUD_SOUND_SQUAD5:
        theForceSound = true;
        break;
    }

    return theForceSound;
}