#include "MW4Headers.hpp"
#include "adept\application.hpp"
#include "rail_move.hpp"
//#include "quadedge.h"
#include <MW4\MWMission.hpp>
#include <Adept\Map.hpp>
#include <Adept\CollisionGrid.hpp>
#include <ElementRenderer\StateChange.hpp>
#include <adept\collisionvolume.hpp>
#include "path.hpp"
#include "bridge.hpp"
#include "cultural.hpp"
#include "mwplayer.hpp"
#include "move_rect.hpp"
#include "windows.h"
#include "shooterai.hpp"

#pragma warning (push)
#include <vector>
#include <map>
#pragma warning (pop)

using namespace MW4AI;
using namespace MechWarrior4;
using namespace ElementRenderer;

#define BADSLOPE 0.707f

namespace MW4AI
{
	void GetMapData(Stuff::Scalar x, Stuff::Scalar z,CRailSquare *who)
//	void GetMapData(Stuff::Scalar x, Stuff::Scalar z,Stuff::Scalar &y,Normal3D& normal,BYTE& material,int& objectid)
	{
		Stuff::Scalar i,j;
		Stuff::Line3D line;
		Point3D point;
		Stuff::Scalar water_depth=0.0f;

		who->normal.x = FLT_MAX;
		who->normal.y = FLT_MAX;
		who->normal.z = FLT_MAX;
		for (i=-5.0f;i<=5.0f;i+=1.0f)
		{
			for (j=-5.0f;j<=5.0f;j+=1.0f)
			{
				point.x = x+i;
				point.z = z+j;
				point.y = 800.0f;

				line.m_length = 1000.0f;
				line.m_direction = Vector3D::Down;
				line.SetOrigin(point);

				//
				// Prepare query
				//	
				Stuff::Normal3D inormal;
				Stuff::Line3D iline;
				Stuff::Normal3D fred;
				Entity::CollisionQuery *query;

				//
				// Cast ray for map surface
				//
				query = new Entity::CollisionQuery (&line, &fred, Entity::CanBeWalkedOnFlag, NULL);
				Adept::Entity *entity_hit;
				Check_Object(CollisionGrid::Instance);
				entity_hit = CollisionGrid::Instance->ProjectLine(query);

				if (entity_hit != NULL)
				{
					line.FindEnd(&point);
					who->height = point.y;
					Check_Object (Map::GetInstance());
					Check_Object (Map::GetInstance()->GetGameModel ());
					if ((Map::GetInstance()->GetGameModel ()->m_waterLevel - point.y) > water_depth)
						water_depth = (Map::GetInstance()->GetGameModel ()->m_waterLevel - point.y);
				who->material = query->m_material;
				if (query->m_normal->y < who->normal.y)
				{
					who->normal.x = query->m_normal->x;
					who->normal.y = query->m_normal->y;
					who->normal.z = query->m_normal->z;
				}
				Check_Object (&who->normal);
				}



				delete query;
			}
		}

		if ((water_depth >= ShallowWaterDepth) && (water_depth < MidWaterDepth))
		{
			who->material = FakeShallowWaterMaterial;
		}
		else if ((water_depth >= MidWaterDepth) && (water_depth < DeepWaterDepth))
		{
			who->material = FakeMidWaterMaterial;
		}
		else if (water_depth >= DeepWaterDepth)
		{
			who->material = FakeOceanicWaterMaterial;
		}
		who->objectid = -1;
	}

	Stuff::Scalar MinZ, MaxZ, MinX, MaxX;
}

void CRailNode::CheckRadius()
{
	if (m_Radius < 1.0f)
		m_Radius = 1.0f;

	if (m_Location.x < MinX + 5.0f)
		m_Location.x = MinX + 5.0f;
	if (m_Location.z < MinZ + 5.0f)
		m_Location.z = MinZ + 5.0f;
	if (m_Location.x > MaxX - 5.0f)
		m_Location.x = MaxX - 5.0f;
	if (m_Location.z > MaxZ - 5.0f)
		m_Location.z = MaxZ - 5.0f;

	if (m_Location.x - m_Radius < MinX)
		m_Radius = m_Location.x - MinX - 1.0f;
	if (m_Location.z - m_Radius < MinZ)
		m_Radius = m_Location.z - MinZ - 1.0f;
	if (m_Location.x + m_Radius > MaxX)
		m_Radius = MaxX - m_Location.x - 1.0f;
	if (m_Location.z + m_Radius > MaxZ)
		m_Radius = MaxZ - m_Location.z - 1.0f;

	Verify(m_Radius > 0);
}

void CRailGraph::CalcEditorDistances (void)
{
	int i,j,size;

	gos_PushCurrentHeap (g_RailHeap);
	size = m_Nodes.size ();
	for (i=0;i<size;i++)
	{
		if (m_Nodes[i])
		{
			m_Nodes[i]->m_DistanceValid = true;
			m_Nodes[i]->m_EditorDistance.reserve (size);
			for (j=0;j<size;j++)
				m_Nodes[i]->m_EditorDistance.push_back (FLT_MAX);
		}
	}
	for (i=0;i<(size-1);i++)
	{
		for (j=i+1;j<size;j++)
		{
			Stuff::Point3D loc1,loc2,extra;
			float dist;

			if ((!m_Nodes[i]) && (!m_Nodes[j]))
			{
				continue;
			}
			if (!m_Nodes[i])
			{
				m_Nodes[j]->m_EditorDistance[i] = FLT_MAX;
				continue;
			}
			if (!m_Nodes[j])
			{
				m_Nodes[i]->m_EditorDistance[j] = FLT_MAX;
				continue;
			}
			loc1 = m_Nodes[i]->Location ();
			loc2 = m_Nodes[j]->Location ();
			loc1.y = 0;
			loc2.y = 0;
			extra.Subtract (loc1,loc2);
			dist = extra.GetLength ();
			m_Nodes[i]->m_EditorDistance[j] = dist;
			m_Nodes[j]->m_EditorDistance[i] = dist;
		}
	}
	gos_PopCurrentHeap ();
}


CRailSquare::CRailSquare (void)
{
	height = 0;
	material = 0;
	objectid = -1;
	objectheight = 1.0f;
	waterlevel = 0;
	normal = Normal3D (0,0,1);
	bridge = -1;
}

void CRailSquare::Construct (Stuff::Scalar x,Stuff::Scalar z)
{
//	GetMapData (x+5.0f,z+5.0f,height,normal,material,objectid);
	GetMapData (x+5.0f,z+5.0f,this);
#if defined(LAB_ONLY)
	switch (material)
	{
		case WaterMaterial:
			PAUSE (("ran into a water material, should be a water depth based material"));
			break;
		case NoMaterial:
		case LavaMaterial:
		case GreyDirtMaterial:
		case BrownDirtMaterial:
		case DarkGreyDirtMaterial:
		case DarkBrownDirtMaterial:
		case RockMaterial:
		case DarkRockMaterial:
		case SteelMaterial:
		case BlacktopMaterial:
		case UsMaterial:
		case ThemMaterial:
		case GlassMaterial:
		case GrassMaterial:
		case WoodMaterial:
		case ConcreteMaterial:
		case DarkConcreteMaterial:
		case SnowMaterial:
		case FakeShallowWaterMaterial:
		case FakeMidWaterMaterial:
		case FakeOceanicWaterMaterial:
// MSL 5.03 Lava
		case LightMineralMaterial:
		case DarkMineralMaterial:
		case AshMaterial:
		case CrackedLavaMaterial:
		case OpenLavaMaterial:
			break;
		default:
			PAUSE (("ran into an unknown material"));
			break;
	}
#endif
}

CRailWorkGrid::CRailWorkGrid (void)
{
	m_Grid.clear ();
}

void CRailWorkGrid::MarkBuilding (ExtentBox& box,int id,bool bridge,CMoveGrid *movegrid,CAirMoveGrid *airmovegrid,bool walkthruflag)
{
	int x,z;
	int minx,maxx,minz,maxz;
	int offx,offz;

	minx = (int) box.minX;
	maxx = (int) box.maxX;
	minz = (int) box.minZ;
	maxz = (int) box.maxZ;
	if (minx < MinX)
		minx = (int) MinX;
	if (minz < MinZ)
		minz = (int) MinZ;
	if (maxx >= (MaxX))
		maxx = (int) (MaxX)-1;
	if (maxz >= (MaxZ))
		maxz = (int) (MaxZ)-1;
	minx /= BLOCK_GRID_RES;
	maxx /= BLOCK_GRID_RES;
	minz /= BLOCK_GRID_RES;
	maxz /= BLOCK_GRID_RES;

	offx = (int) MinX/BLOCK_GRID_RES;
	offz = (int) MinZ/BLOCK_GRID_RES;
	if (bridge)
	{
		for (x=minx;x<=maxx;x++)
		{
			for (z=minz;z<=maxz;z++)
			{
				m_Grid[x-offx][z-offz].objectid = id;
				if (bridge)
					m_Grid[x-offx][z-offz].bridge = id;
				m_Grid[x-offx][z-offz].objectheight = box.maxY;
				movegrid->PassableLocalOverride (x,z,0);
			}
		}
		if ((maxx-minx) > (maxz-minz))
		{
			for (z=minz;z<=maxz;z++)
			{
				movegrid->PassableLocalOverride (minx-3,z,0);
				movegrid->PassableLocalOverride (maxx+3,z,0);
				movegrid->PassableLocalOverride (minx-2,z,0);
				movegrid->PassableLocalOverride (maxx+2,z,0);
				movegrid->PassableLocalOverride (minx-1,z,0);
				movegrid->PassableLocalOverride (maxx+1,z,0);
			}
			for (x=minx;x<=maxx;x++)
			{
				movegrid->PassableLocalOverride (x,minz-2,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,maxz+2,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,minz-3,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,maxz+3,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,minz-1,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,maxz+1,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,minz,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (x,maxz,BLOCKED_FLAGS);
			}
		}
		else
		{
			for (x=minx;x<=maxx;x++)
			{
				movegrid->PassableLocalOverride (x,minz-2,0);
				movegrid->PassableLocalOverride (x,maxz+2,0);
				movegrid->PassableLocalOverride (x,minz-3,0);
				movegrid->PassableLocalOverride (x,maxz+3,0);
				movegrid->PassableLocalOverride (x,minz-1,0);
				movegrid->PassableLocalOverride (x,maxz+1,0);
			}
			for (z=minz;z<=maxz;z++)
			{
				movegrid->PassableLocalOverride (minx-3,z,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (maxx+3,z,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (minx-2,z,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (maxx+2,z,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (minx-1,z,BLOCKED_FLAGS);
				movegrid->PassableLocalOverride (maxx+1,z,BLOCKED_FLAGS);
			}
		}
	}
	else
	{
		for (x=minx;x<=maxx;x++)
		{
			for (z=minz;z<=maxz;z++)
			{
				m_Grid[x-offx][z-offz].walkthruflag = walkthruflag;
				m_Grid[x-offx][z-offz].objectid = id;
				m_Grid[x-offx][z-offz].objectheight = box.maxY - box.minY;
			}
		}
	}
}

bool CRailWorkGrid::AnalyzeNode (QuadTreeNode<int> *node)
{
	int sx,ex,sy,ey,cx,cy;
	bool flag;

	sx = node->Left ();
	ex = node->Right ();
	sy = node->Top ();
	ey = node->Bottom ();
	flag = false;
	if (m_Grid[sx][sy].objectid != -1)
		flag = true;
	for (cx = sx;cx<=ex;cx++)
	{
		for (cy = sy;cy<=ey;cy++)
		{
			if (flag)
			{
				if (m_Grid[cx][cy].objectid == -1)
					return true;
			}
			else
			{
				if (m_Grid[cx][cy].objectid != -1)
					return true;
			}
		}
	}
	return false;
}

bool QuadTree<int>::TraverseFunction (QuadTreeNode<int> *node,int level,DWORD param)
{
	CRailWorkGrid *grid;
	if (param)
	{
		grid = (CRailWorkGrid *) param;
		if (grid->AnalyzeNode (node))
		{
			node->Subdivide ();
		}
	}
	return true;
}

void CRailWorkGrid::Construct (WorkCallBack callback,float maxpercent,CMoveGrid *movegrid,CAirMoveGrid *airmovegrid)
{
	int i,j,sizex,sizey;
	Stuff::Scalar x,z;
	stlport::vector<CRailSquare> fred;
	float curpercent;
	float dpercent;

	x = MaxX - MinX;
	x /= 10.0f;
	z = MaxZ - MinZ;
	z /= 10.0f;
	m_Grid.assign ((int) x,fred);
	for (i=0;i<x;i++)
	{
		m_Grid[i].assign ((int) z,CRailSquare ());
	}

	curpercent = 0;
	sizex = m_Grid.size ();
	dpercent = maxpercent / sizex;
	movegrid->Clear ();
	airmovegrid->Clear ();
	for (i=0;i<sizex;i++)
	{
		curpercent += dpercent;
		if (callback)
			callback (curpercent);
		sizey = m_Grid[i].size ();
		for (j=0;j<sizey;j++)
		{
			x = i*10.0f;
			x += MinX;
			z = j*10.0f;
			z += MinZ;
			m_Grid[i][j].Construct (x,z);
		}
	}

	int offx = (int) (MinX / BLOCK_GRID_RES);
	int offz = (int) (MinZ / BLOCK_GRID_RES);
	for (i=0;i<sizex;i++)
	{
		sizey = m_Grid[i].size ();
		for (j=0;j<sizey;j++)
		{
			switch (m_Grid[i][j].material)
			{
				case FakeShallowWaterMaterial:
					movegrid->PassableLocalOverride (i+offx,j+offz,SHALLOW_WATER<<WATER_SHIFT);
					movegrid->PassableLocal (i+offx,j+offz,TRACKPASS_FLAG+WHEELPASS_FLAG+WATERPASS_FLAG);
					break;
				case FakeMidWaterMaterial:
					movegrid->PassableLocalOverride (i+offx,j+offz,MEDIUM_WATER<<WATER_SHIFT);
					movegrid->PassableLocal (i+offx,j+offz,TRACKPASS_FLAG+WHEELPASS_FLAG);
					break;
// MSL 5.03 Lava
				case CrackedLavaMaterial:
				case OpenLavaMaterial:
				case FakeOceanicWaterMaterial:
					movegrid->PassableLocalOverride (i+offx,j+offz,DEEP_WATER<<WATER_SHIFT);
					movegrid->PassableLocal (i+offx,j+offz,LEGPASS_FLAG+TRACKPASS_FLAG+WHEELPASS_FLAG);
					break;
				default:
					movegrid->PassableLocalOverride (i+offx,j+offz,WATERPASS_FLAG);
					break;
			}
		}
	}

	UserConstants *constants;
	constants = UserConstants::Instance ();
	Check_Pointer (constants);

	for (i=0;i<sizex;i++)
	{
		sizey = m_Grid[i].size ();
		for (j=0;j<sizey;j++)
		{
			float nx,nz,ny;
			nx = m_Grid[i][j].normal.x;
			nz = m_Grid[i][j].normal.z;
			ny = m_Grid[i][j].normal.y;
			if ((nz <= 0) && (nx >= -Pi_Over_8) && (nx <= Pi_Over_8))
			{
				movegrid->PassableLocal (i+offx,j+offz,0 << SLOPEDIR_SHIFT);
			}
			else if ((nz <= 0) && (nx >= Pi_Over_8) && (nx <= (Pi_Over_2-Pi_Over_8)))
			{
				movegrid->PassableLocal (i+offx,j+offz,1 << SLOPEDIR_SHIFT);
			}
			else if ((nx >= 0) && (nz>=-Pi_Over_8) && (nz <= Pi_Over_8))
			{
				movegrid->PassableLocal (i+offx,j+offz,2 << SLOPEDIR_SHIFT);
			}
			else if ((nz >= 0) && (nx >= Pi_Over_8) && (nx <= (Pi_Over_2-Pi_Over_8)))
			{
				movegrid->PassableLocal (i+offx,j+offz,3 << SLOPEDIR_SHIFT);
			}
			else if ((nz >= 0) && (nx >= -Pi_Over_8) && (nx <= Pi_Over_8))
			{
				movegrid->PassableLocal (i+offx,j+offz,4 << SLOPEDIR_SHIFT);
			}
			else if ((nz >= 0) && (nx <= -Pi_Over_8) && (nx >= (-Pi_Over_2+Pi_Over_8)))
			{
				movegrid->PassableLocal (i+offx,j+offz,5 << SLOPEDIR_SHIFT);
			}
			else if ((nx <= 0) && (nz>=-Pi_Over_8) && (nz <= Pi_Over_8))
			{
				movegrid->PassableLocal (i+offx,j+offz,6 << SLOPEDIR_SHIFT);
			}
			else if ((nz <= 0) && (nx <= -Pi_Over_8) && (nx >= (-Pi_Over_2+Pi_Over_8)))
			{
				movegrid->PassableLocal (i+offx,j+offz,7 << SLOPEDIR_SHIFT);
			}
			else
			{
				STOP (("unknown slope direction value"));
			}

			if (ny < constants->Get (UserConstants::leg_down_slope))
			{
				movegrid->PassableLocal (i+offx,j+offz,LEGPASS_FLAG);
			}
			if (ny < constants->Get (UserConstants::track_down_slope))
			{
				movegrid->PassableLocal (i+offx,j+offz,TRACKPASS_FLAG);
			}
			if (ny < constants->Get (UserConstants::wheel_down_slope))
			{
				movegrid->PassableLocal (i+offx,j+offz,WHEELPASS_FLAG);
			}
			if (ny < constants->Get (UserConstants::hover_down_slope))
			{
				if (((movegrid->PassableLocal (i+offx,j+offz) & WATER_MASK) >> WATER_SHIFT) == NO_WATER)
				{
					movegrid->PassableLocal (i+offx,j+offz,HOVERPASS_FLAG);
				}
			}

			if ((ny < constants->Get (UserConstants::leg_up_slope)) && (ny > constants->Get (UserConstants::leg_down_slope)))
			{
				movegrid->PassableLocal (i+offx,j+offz,LEGDOWN_SLOPE<<STEEP_SHIFT);
			}
			if ((ny < constants->Get (UserConstants::track_up_slope)) && (ny > constants->Get (UserConstants::track_down_slope)))
			{
				movegrid->PassableLocal (i+offx,j+offz,TRACKDOWN_SLOPE<<STEEP_SHIFT);
			}
			if ((ny < constants->Get (UserConstants::wheel_up_slope)) && (ny > constants->Get (UserConstants::wheel_down_slope)))
			{
				movegrid->PassableLocal (i+offx,j+offz,WHEELDOWN_SLOPE<<STEEP_SHIFT);
			}
			if ((ny < constants->Get (UserConstants::hover_up_slope)) && (ny > constants->Get (UserConstants::hover_down_slope)))
			{
				if (((movegrid->PassableLocal (i+offx,j+offz) & WATER_MASK) >> WATER_SHIFT) == NO_WATER)
				{
					movegrid->PassableLocal (i+offx,j+offz,HOVERPASS_FLAG);
				}
			}
		}
	}


	int tablescan[] =
	{
    NameTable::CulturalArray,
		NameTable::BuildingArray,
		NameTable::TurretArray,
		NameTable::VehicleArray
	};
	int size;
	Scalar maxheight=0;
	NameTable *table = NameTable::GetInstance();
	Check_Object (table);
	for (i=0;i<sizeof (tablescan)/sizeof (int);i++)
	{
		size = table->nameTableArray[tablescan[i]].GetLength ();
		for (j=0;j<size;j++)
		{
			NameTableEntry *data;
			Entity *who;

			data = &table->nameTableArray [tablescan[i]][j];

			if (!strnicmp (data->objectName,"water",5))
			{
				continue;
			}

			who = data->dataPointer->GetCurrent ();

			if (!who)
				continue;
			if ((!who->GetSolidVolume ()) && (!who->GetHierarchicalVolume ()))
				continue;
			OBB *obb;
			CollisionVolume *col;
			col = who->GetHierarchicalVolume ();
			if (!col)
				col = who->GetSolidVolume ();
			OBB world_bounds;
			world_bounds.Multiply(col->m_localSpaceBounds, who->GetLocalToWorld());
			obb = &(world_bounds);
			ExtentBox box (*obb);

			if (who->IsDerivedFrom (Vehicle::DefaultData))
			{
				if (maxheight < (box.maxY - box.minY))
				{
					maxheight = (box.maxY - box.minY);
				}
			}

			bool walkthruflag = false;
			{
				MWObject *obj;
				obj = Cast_Object (MWObject *,who);
				if (obj->GetWalkThruType () == Entity::AnythingCanWalkThruType)
					continue;
				if ((obj->GetAI ()) && (tablescan[i] == NameTable::VehicleArray))
				{
					if (!obj->GetAI ()->IsDerivedFrom (ShooterAI::DefaultData))
						continue;
				}
				if (!obj->CanOBBCollide ())
					continue;
			}


			Point3D loc = (Point3D) obb->localToParent;

			if (who->IsDerivedFrom (Bridge::DefaultData))
				MarkBuilding (box,data->GetObjectID (),true,movegrid,airmovegrid,walkthruflag);
			else
				MarkBuilding (box,data->GetObjectID (),false,movegrid,airmovegrid,walkthruflag);
		}
	}

	maxheight += 5.0f;
	offx = (int) (MinX / BLOCK_GRID_RES);
	offz = (int) (MinZ / BLOCK_GRID_RES);
	for (i=0;i<sizex;i++)
	{
		curpercent += dpercent;
		if (callback)
			callback (curpercent);
		sizey = m_Grid[i].size ();
		for (j=0;j<sizey;j++)
		{
			x = i*10.0f;
			x += MinX;
			z = j*10.0f;
			z += MinZ;
			x += 5.0f;
			z += 5.0f;
			if (m_Grid[i][j].bridge != -1)
			{
//				movegrid->PassableLocal (i+offx,j+offx,BRIDGE_FLAG);
			}
			else if (m_Grid[i][j].objectid != -1)
			{
//				if (!m_Grid[i][j].walkthruflag)
					movegrid->PassableLocal (i+offx,j+offx,BLOCKED_FLAGS);
//				else
//					movegrid->PassableLocal (i+offx,j+offx,0x3e);				//mechs can move through
				if (m_Grid[i][j].objectheight >= maxheight)
				{
					airmovegrid->Passable (x,z,AIRPASS_FLAG);
				}
			}
		}
	}

}

void CRailGraph::UpdateNodes (WorkCallBack callback,float percentstart)
{
	stlport::vector<CRailNode *>::iterator iter;
	int i,size,min;
	Stuff::Scalar max;
	float curpercent,dpercent;

	CreateGrid (callback,80.0f);
	Check_Pointer (m_WorkGrid);
	CalcEditorDistances ();

	curpercent = percentstart;
	dpercent = 10.0f / m_Nodes.size ();

	iter = m_Nodes.begin ();
	while (iter != m_Nodes.end ())
	{
		if (!(*iter))
		{
			iter++;
			continue;
		}
		curpercent += dpercent;
		if (callback)
			callback (curpercent);
		size = (*iter)->m_EditorDistance.size ();
		min = 0;
		for (i=1;i<size;i++)
		{
			if ((*iter)->m_EditorDistance[i] < (*iter)->m_EditorDistance[min])
				min = i;
		}
		max = (*iter)->m_EditorDistance[min];
		if (((*iter)->Location ().x - MinX) < max)
			max = (*iter)->Location ().x - MinX;
		if (((*iter)->Location ().z - MinZ) < max)
			max = (*iter)->Location ().z - MinZ;

		if ((MaxX - (*iter)->Location ().x) < max)
			max = MaxX - (*iter)->Location ().x;
		if ((MaxZ - (*iter)->Location ().z) < max)
			max = MaxZ - (*iter)->Location ().z;

		max -= 1.0f;

		(*iter)->m_Radius = m_WorkGrid->CalcRadius ((*iter)->Location (),max);
		if ((*iter)->m_Radius < 20.0)
		{
			DeleteNode ((*iter)->ID ());
		}
		else
		{
			(*iter)->m_MaxRadius = (*iter)->m_Radius;
			(*iter)->m_RadiusSquared = (*iter)->m_Radius * (*iter)->m_Radius;
		}
		iter++;
	}
	KillGrid ();
}

void CRailGraph::CreateRectData (WorkCallBack callback)
{
	delete m_RectList;
	gos_PushCurrentHeap (g_RailHeap);
	m_RectList = new CMoveRectList ();
	gos_PopCurrentHeap ();
	m_RectList->Create (callback);
}

void CRailGraph::CreateBasicNodes (int num,WorkCallBack callback)
{
	float curx,cury;
	float offx,offy;
	float dx,dy;
	float temp;
	int x,y;
	float width,height;
	int i,size;
	float maxx,maxy;
	float curpercent,dpercent;

	if (num >= MAXNODES)
		num = (MAXNODES-1);
	maxx = MaxX - MinX - 10.0f;
	maxy = MaxZ - MinZ - 10.0f;

	size = m_Nodes.size ();
	for (i=0;i<size;i++)
		DeleteNode (i);
	m_Nodes.clear ();

	CreateGrid (callback,80.0f);
	Check_Pointer (m_WorkGrid);

	temp = (float) sqrt (num);
	width = temp;
	height = ((float) num)/temp;

	dx = maxx / (width+1);
	dy = maxy / (height+1);
	if (dx < 100.0f)
	{
		dx = 100.0f;
		width = maxx / dx;
	}
	if (dy < 100.0f)
	{
		dy = 100.0f;
		height = maxy / dy;
	}
	width-=1;
	height -=1;
	curx = MinX + dx + 5.0f;
	cury = MinZ + dy + 5.0f;
	curpercent = 80.0f;
	dpercent = 10.0f / height;
	offx = MinX;
	offy = MinZ;
	gos_PushCurrentHeap (g_RailHeap);
	for (y=0;y<=height;y++)
	{
  	curx = MinX + dx + 5.0f;
		curpercent += dpercent;
		if (callback)
			callback (curpercent);
		for (x=0;x<=width;x++)
		{
			CRailNode *node;

			if (!m_WorkGrid->BuildingAt (Stuff::Point3D (curx-offx,0,cury-offy)))
			{
				node = new CRailNode (Stuff::Point3D (curx,0,cury),50);
				AddNode (node);
			}
			curx += dx;
		}
		cury += dy;
	}
	gos_PopCurrentHeap ();
	UpdateNodes (callback,90);
	KillGrid ();
}

bool CRailWorkGrid::BuildingAt (const Stuff::Point3D& pt)
{
	float cx,cy;
	int ix,iy;

	cx = pt.x;
	cy = pt.z;
	cx /= 10.0f;
	cy /= 10.0f;
	ix = (int) cx;
	iy = (int) cy;

	return m_Grid[ix][iy].objectid != -1;
}

Stuff::Scalar CRailWorkGrid::Slope (int ix,int iy)
{
	Stuff::Scalar toret;

	toret = m_Grid[ix][iy].normal.y;					// since straight up line is 0,1,0 and length 1
	return (toret);
}

Stuff::Scalar CRailWorkGrid::Slope (const Stuff::Point3D& loc)
{
	float cx,cy;
	int ix,iy;
	Stuff::Scalar toret;

	cx = loc.x;
	cy = loc.z;
	cx /= 10.0f;
	cy /= 10.0f;
	ix = (int) cx;
	iy = (int) cy;

	toret = m_Grid[ix][iy].normal.y;					// since straight up line is 0,1,0 and length 1
	return (toret);
}

void MW4AI::FillCircle (stlport::vector<gridpoint>& points,Stuff::Point3D center,Stuff::Scalar radcomp)
{
	float cx,cy;
	float curx,cury,maxx,maxy;

	gos_PushCurrentHeap (g_RailHeap);
	cx = center.x/10.0f;
	cy = center.z/10.0f;

	maxx = radcomp/10.0f;
	maxy = radcomp/10.0f;
	radcomp /=10.0f;
	radcomp *= radcomp;

	for (curx = 0;curx<=maxx;curx++)
	{
		for (cury = 0;cury<=maxy;cury++)
		{
			if (((curx*curx) + (cury*cury)) <= radcomp)
			{
				points.push_back (gridpoint((cx-curx),(cy-cury)));
				points.push_back (gridpoint((cx-curx),(cy+cury)));
				points.push_back (gridpoint((cx+curx),(cy-cury)));
				points.push_back (gridpoint((cx+curx),(cy+cury)));
			}

		}
	}
	gos_PopCurrentHeap ();
}

Stuff::Scalar CRailWorkGrid::CalcRadius (const Stuff::Point3D& center, Stuff::Scalar maxrad)
{
	float radcomp;
	float currad;
	int cellcount,filledcount;
	bool flag;
	bool bridgeflag;
//	int minx,miny,maxx,maxy;

	int offx = (int) MinX/BLOCK_GRID_RES;
	int offz = (int) MinZ/BLOCK_GRID_RES;
	gos_PushCurrentHeap (g_RailHeap);
	stlport::vector<gridpoint> points;
	stlport::map<int,bool> objects;
	stlport::stack<gridpoint> fillstack;

	if (maxrad > 500.0f)
		maxrad = 500.0f;
	for (currad = maxrad;currad >= 10;currad-=5)
	{
		cellcount = filledcount = 0;
		radcomp = currad;
		points.clear ();
		objects.clear ();
//		FillCircle (points,center,radcomp,minx,miny,maxx,maxy);
		FillCircle (points,center,radcomp);

		int x,y;

		for (x=0;x<2048;x++)
		{
			for (y=0;y<2048;y++)
			{
				workarray[x][y] = true;
			}
		}
		stlport::vector<gridpoint>::iterator iter;
		iter = points.begin ();
		while (iter != points.end ())
		{
			workarray[iter->x-offx][iter->y-offz] = false;
			iter++;
		}
		bridgeflag = false;
		iter = points.begin ();
		while (iter != points.end ())
		{
			cellcount++;
			int obj;
//			if (m_Grid[iter->x-offx][iter->y-offz].bridge != -1)
//				bridgeflag = true;
			obj = m_Grid[iter->x-offx][iter->y-offz].objectid;
			if (obj != -1)
			{
				objects[obj] = true;
				filledcount++;
				workarray[iter->x-offx][iter->y-offz] = true;
			}
			else if (Slope (iter->x-offx,iter->y-offz) < BADSLOPE)
			{
				filledcount++;
				workarray[iter->x-offx][iter->y-offz] = true;
			}
			iter++;
		}
		if (bridgeflag)
			continue;

		flag = false;
		float t;
		float step;

		step = 2.0f*(currad/10.0f)*Pi;
		step = 1.0f/step;
		step /= 1.5f;
		for (t=0;t<1.0f;t+=step)
		{
			float x,y;

			x = currad * (float) cos (2.0f*Pi*t);
			y = currad * (float) sin (2.0f*Pi*t);
			x += center.x;
			y += center.z;
			x /= 10.0f;
			y /= 10.0f;
			Verify (x>=(MinX/10));
			Verify (y>=(MinZ/10));
			Verify (x<(float) m_Grid.size ());
			Verify (y<(float) m_Grid[(unsigned int) (x-offx)].size ());
//			if (m_Grid[(unsigned int) (x-offx)][(unsigned int) (y-offz)].bridge>=0)
//				flag = true;
//			if (m_Grid[(unsigned int) x][(unsigned int) y].objectid>=0)
//				flag = true;
			if (Slope ((int) x-offx,(int) y-offz) < BADSLOPE)
				flag = true;
			if (flag)
				break;
		}
		if (flag)
		{
			continue;
		}

		while (!fillstack.empty ())
			fillstack.pop ();
		fillstack.push(gridpoint (center.x/10,center.z/10));
		while (!fillstack.empty ())
		{
			gridpoint t = fillstack.top ();
			fillstack.pop ();
			if (workarray[t.x-offx][t.y-offz])
				continue;
			workarray[t.x-offx][t.y-offz] = true;
			if (t.x-1>=MinX/10)
				fillstack.push (gridpoint (t.x-1,t.y));
			if (t.y-1>=MinZ/10)
				fillstack.push (gridpoint (t.x,t.y-1));

			if (t.x+1<(MaxX/10))
				fillstack.push (gridpoint (t.x+1,t.y));
			if (t.y+1<(MaxZ/10))
				fillstack.push (gridpoint (t.x,t.y+1));
		}

		while (!fillstack.empty ())
			fillstack.pop ();

		iter = points.begin ();
		while (iter != points.end ())
		{
			if (!workarray[iter->x-offx][iter->y-offz])
				flag = true;
			iter++;
		}

		float ratio;
		ratio = (float) filledcount / (float) cellcount;
		if ((ratio <= 0.5f) && !flag)
			break;
	}

	gos_PopCurrentHeap ();
	return currad;
}


float CRailWorkGrid::CheckSquare (int cx,int cy,int movetype)
{
	int sqrindex[8][2] = {
		{-1,-1},
		{0,-1},
		{1,-1},
		{-1,0},
		{1,0},
		{-1,1},
		{0,1},
		{1,1}
	};
	int i;

	for (i=0;i<8;i++)
	{
		int ix,iy;

		ix = cx+sqrindex[i][0];
		iy = cy+sqrindex[i][1];
		if (MaterialMode (ix,iy,movetype) == FLT_MAX)
			return -1.0f;
		if (m_Grid[ix][iy].objectid != -1)
		{
			switch (movetype)
			{
				case JUMPINDEX:
					break;
				case TRACKINDEX:
				case WHEELINDEX:
				case LEGINDEX:
				case HOVERINDEX:
				case WATERINDEX:
					if (m_Grid[ix][iy].objectheight > 0)
						return -1.0f;
					break;
				case HELIINDEX:
				case FLYINDEX:
					if (m_Grid[ix][iy].objectheight > 50.0)
						return -1.0f;
					break;
				case BRIDGEINDEX:
					break;
				default:
					Verify (false);
					break;
			}
		}
	}
	return 0;
}

Stuff::Scalar CRailWorkGrid::CalcWeight (Stuff::Point3D src, Stuff::Point3D dest,int movetype)
{
	Stuff::Point3D extra;
	Stuff::Scalar sx,sy,dx,dy;
	int ix,iy,ex,ey;
	Stuff::Scalar curweight,tempweight;
	Stuff::Scalar lastheight,curheight,dh;
	int dir;
	int count,mask;

	if (movetype == FLYINDEX)
		return -1.0f;
	if (movetype == HELIINDEX)
		return -1.0f;

	int offx = (int) MinX;
	int offz = (int) MinZ;

	src.x -= offx;
	src.z -= offz;
	dest.x -= offx;
	dest.z -= offz;

	dx = (float) dest.x - src.x;
	dy = (float) dest.z - src.z;
	sx = dx < 0 ? dx*-1.0f : dx;
	sy = dy < 0 ? dy*-1.0f : dy;

	if (sx > sy)
	{
		dy = dy / sx;
		count = (int) (dx<0.0 ? dx*-1.0f : dx);
		dx = dx<0 ? -1.0f : 1.0f;
		if (dx == 1.0f)
		{
			if (dy < -0.25f)
				dir = 1;
			else if (dy > 0.25f)
				dir = 3;
			else
				dir = 2;
		}
		else
		{
			if (dy < -0.25f)
				dir = 7;
			else if (dy > 0.25f)
				dir = 5;
			else
				dir = 6;
		}
	}
	else
	{
		dx = dx / sy;
		count = (int) (dy<0.0 ? dy*-1.0f : dy);
		dy = dy<0 ? -1.0f : 1.0f;
		if (dy == 1.0f)
		{
			if (dx < -0.25f)
				dir = 5;
			else if (dx > 0.25f)
				dir = 3;
			else
				dir = 4;
		}
		else
		{
			if (dx < -0.25f)
				dir = 7;
			else if (dx > 0.25f)
				dir = 1;
			else
				dir = 0;
		}
	}

	extra.Subtract (dest,src);

	dest.x /= 10.0f;
	dest.z /= 10.0f;
	src.x /= 10.0f;
	src.z /= 10.0f;
	Verify (dest.x >= 0);
	Verify (dest.z >= 0);
	Verify (src.x >= 0);
	Verify (src.z >= 0);

	dx = dest.x - src.x;
	dy = dest.z - src.z;
	sx = dx < 0 ? dx*-1.0f : dx;
	sy = dy < 0 ? dy*-1.0f : dy;

	if (sx > sy)
	{
		dy = dy / sx;
		count = (int) (dx<0.0 ? dx*-1.0f : dx);
		dx = dx<0 ? -1.0f : 1.0f;
	}
	else
	{
		dx = dx / sy;
		count = (int) (dy<0.0 ? dy*-1.0f : dy);
		dy = dy<0 ? -1.0f : 1.0f;
	}

	mask = BLOCKED_FLAGS;
	switch (movetype)
	{
		case JUMPINDEX:
			mask = JUMPPASS_FLAG;
			break;
		case TRACKINDEX:
			mask = TRACKPASS_FLAG;
			break;
		case WHEELINDEX:
			mask = WHEELPASS_FLAG;
			break;
		case LEGINDEX:
			mask = LEGPASS_FLAG;
			break;
		case HOVERINDEX:
			mask = HOVERPASS_FLAG;
			break;
		case WATERINDEX:
			mask = WATERPASS_FLAG;
			break;
	}

	sx = src.x;
	sy = src.z;
	ex = (int) dest.x;
	ey = (int) dest.z;
	ix = (int) sx;
	iy = (int) sy;
	lastheight = m_Grid[ix][iy].height;
	curweight = 0;
	while (count)
	{
		count--;
		sx += dx;
		sy += dy;
		ix = (int) sx;
		iy = (int) sy;
		Verify (ix >= 0);
		Verify (iy >= 0);

		if (!g_MissionGraph->MoveTo (ix+(offx/BLOCK_GRID_RES),iy+(offz/BLOCK_GRID_RES),dir,mask))
			return -1.0f;

//		if (m_Grid[ix][iy].bridge != -1)
//			return -1.0f;

		if (m_Grid[ix][iy].objectid != -1)
		{
			switch (movetype)
			{
				case JUMPINDEX:
					if (m_Grid[ix][iy].objectheight > 12.0)
						return -1.0f;
					break;
				case BRIDGEINDEX:
					break;
				case TRACKINDEX:
				case WHEELINDEX:
				case LEGINDEX:
				case HOVERINDEX:
					if (m_Grid[ix][iy].objectheight > 0)
						return -1.0f;
					break;
				case HELIINDEX:
				case FLYINDEX:
					if (m_Grid[ix][iy].objectheight > 50.0)
						return -1.0f;
					break;
				case WATERINDEX:
					if (m_Grid[ix][iy].objectheight > 0)
						return -1.0f;
					break;
				default:
					Verify (false);
					break;
			}
		}
		if (movetype != JUMPINDEX)
		{
			curheight = m_Grid[ix][iy].height;
			dh = lastheight - curheight;
			dh = dh < 0 ? dh*-1.0f : dh;
			dh /= 10.0f;

			curweight += (1.0f + dh);
		}
		tempweight = MaterialMode (ix,iy,movetype);
		if (tempweight == FLT_MAX)
		{
			return -1.0f;
		}
		if (CheckSquare (ix,iy,movetype) == -1)
			return -1.0f;
		curweight += tempweight;
	}

	if (movetype == JUMPINDEX)
		curweight *= 1.25f;

	return curweight*10.0f;
}

Stuff::Scalar CRailWorkGrid::MaterialMode (int ix,int iy,int movetype)
{
	BYTE mat;
	UserConstants *data;
	data = UserConstants::Instance ();
	Check_Pointer (data);

	Verify (ix >= 0);
	Verify (iy >= 0);
	mat = m_Grid[ix][iy].material;
	if (movetype == WATERINDEX)
	{
		switch (mat)
		{
		case FakeShallowWaterMaterial:
			return FLT_MAX;
		case FakeMidWaterMaterial:
			return 0.25;
		case FakeOceanicWaterMaterial:
			return 0;
		default:
			return FLT_MAX;
		}
	}
	if (movetype == JUMPINDEX)
		return 0;
	if ((movetype == FLYINDEX) || (movetype == HELIINDEX))
		return 0;
	if (movetype == BRIDGEINDEX)
		return 0;
	Verify ((movetype == TRACKINDEX) || (movetype == WHEELINDEX) || (movetype == LEGINDEX) || (movetype == HOVERINDEX));
	switch (mat)
	{
		case WaterMaterial:
			gosREPORT (false,"ran into a water material");
			return 0;
		

		case NoMaterial:
		case GreyDirtMaterial:
		case BrownDirtMaterial:
		case DarkGreyDirtMaterial:
		case DarkBrownDirtMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_dirt_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_dirt_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_dirt_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_dirt_cost);
			}
// MSL 5.03 Lava
		case CrackedLavaMaterial:
			switch (movetype)
			{
				case TRACKINDEX:
					return FLT_MAX;
				case WHEELINDEX:
					return FLT_MAX;
				case LEGINDEX:
					return data->Get (UserConstants::leg_crackedlava_cost);
				case HOVERINDEX:
					return FLT_MAX;
			}
		break;

		case OpenLavaMaterial:
			switch (movetype)
			{
				case TRACKINDEX:
					return FLT_MAX;
				case WHEELINDEX:
					return FLT_MAX;
				case LEGINDEX:
					return data->Get (UserConstants::leg_openlava_cost);
				case HOVERINDEX:
					return FLT_MAX;
			}
		break;


		case AshMaterial:
		case LightMineralMaterial:
		case DarkMineralMaterial:

		case DarkRockMaterial:
		case LavaMaterial:
		case RockMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_rock_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_rock_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_rock_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_rock_cost);
			}
		case SteelMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_steel_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_steel_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_steel_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_steel_cost);
			}
		case BlacktopMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_blacktop_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_blacktop_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_blacktop_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_blacktop_cost);
			}
		case GlassMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_glass_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_glass_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_glass_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_glass_cost);
			}
		case GrassMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_grass_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_grass_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_grass_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_grass_cost);
			}
		case WoodMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_wood_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_wood_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_wood_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_wood_cost);
			}
		case DarkConcreteMaterial:
		case ConcreteMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_concrete_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_concrete_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_concrete_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_concrete_cost);
			}
		case SnowMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_snow_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_snow_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_snow_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_snow_cost);
			}
		case FakeShallowWaterMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_shallowwater_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_shallowwater_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_shallowwater_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_shallowwater_cost);
			}
			break;
		case FakeMidWaterMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_midwater_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_midwater_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_midwater_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_midwater_cost);
			}
		case FakeOceanicWaterMaterial:
			switch (movetype)
			{
			case TRACKINDEX:
				return data->Get (UserConstants::track_oceanicwater_cost);
			case WHEELINDEX:
				return data->Get (UserConstants::wheel_oceanicwater_cost);
			case LEGINDEX:
				return data->Get (UserConstants::leg_oceanicwater_cost);
			case HOVERINDEX:
				return data->Get (UserConstants::hover_oceanicwater_cost);
			}
		default:
#if defined(LAB_ONLY)
			PAUSE (("ran into an unknown material type"));
#endif
			return 0;
			break;
	}
	return 0;
}


CMoveGrid::CMoveGrid (void)
{
	m_Data = NULL;
	m_MacroData = NULL;
	m_Width = 0;
	m_Height = 0;
	m_AllocWidth = 0;
	m_AllocHeight = 0;
}

CMoveGrid::CMoveGrid (Stuff::MemoryStream *stream)
{
	DWORD datasize;

	m_Data = NULL;
	m_MacroData = NULL;
	m_AllocWidth = 0;
	m_AllocHeight = 0;
	*stream >> m_Width;
	*stream >> m_Height;
	*stream >> datasize;
	if ((m_Width == 0) && (m_Height == 0) && (datasize == 0))
		STOP (("No Move grid created for this map, must exit now"));

//	Verify (m_Width == MaxX);
//	Verify (m_Height == MaxZ);
	Construct (m_Width,m_Height);
	Verify (datasize == (512*512*2));
	stream->ReadBytes (m_Data,(512*512*2));
#if 0
	int i,size,chunksize;
	size = m_Height / 10;
	chunksize = m_Width /10;
	chunksize <<= 1;
	for (i=0;i<size;i++)
	{
		stream->ReadBytes (m_Data[i],chunksize);
		datasize -= chunksize;
	}
#endif	
	{
		
		int i,j;
		int left,right,top,bottom;
		left = (int) (MinX / BLOCK_GRID_RES);
		top = (int) (MinZ / BLOCK_GRID_RES);
		right = (int) (MaxX / BLOCK_GRID_RES);
		bottom = (int) (MaxZ / BLOCK_GRID_RES);

		for (i=left;i<(left+5);i++)
			for (j=top;j<bottom;j++)
				PassableLocal (i,j,BLOCKED_FLAGS);

		for (i=(right-5);i<right;i++)
			for (j=top;j<bottom;j++)
				PassableLocal (i,j,BLOCKED_FLAGS);

		for (i=left;i<right;i++)
			for (j=top;j<(top+5);j++)
				PassableLocal (i,j,BLOCKED_FLAGS);

		for (i=left;i<right;i++)
			for (j=(bottom-5);j<bottom;j++)
				PassableLocal (i,j,BLOCKED_FLAGS);

	}
	CreateMacroData ();
}

void CMoveGrid::CreateMacroData (void)
{
	int width,height;
	int i,j;

	width = m_Width;
	height = m_Height;
	width /= MACROBLOCK_GRID_RES;
	height /= MACROBLOCK_GRID_RES;

	m_MacroAllocWidth = width;
	m_MacroAllocHeight = height;

	gos_PushCurrentHeap(g_RailHeap);
	m_MacroData = new unsigned char *[height];
	for (i=0;i<height;i++)
	{
		m_MacroData[i] = new unsigned char[width];
	}
	gos_PopCurrentHeap ();

	for (i=0;i<width;i++)
	{
		for (j=0;j<height;j++)
		{
			m_MacroData[j][i] = 0;
		}
	}
	unsigned short mask;
	for (i=0;i<m_AllocWidth;i++)
	{
		for (j=0;j<m_AllocHeight;j++)
		{
			mask = m_Data[(j*m_AllocWidth)+i];
			if ((mask & BLOCKED_FLAGS) == BLOCKED_FLAGS)
			{
				m_MacroData[j>>3][i>>3] += 1;
			}
		}
	}
}

void CMoveGrid::DeleteMacroData (void)
{
	int i,width,height;
	width = m_Width;
	height = m_Height;
	width /= MACROBLOCK_GRID_RES;
	height /= MACROBLOCK_GRID_RES;

	if (m_MacroData != NULL)
	{
		for (i=0;i<height;i++)
		{
			delete[] m_MacroData[i];
		}

		delete[] m_MacroData;

		m_MacroData = NULL;
	}
}

void CMoveGrid::SaveData (const char *filename)
{
	int i,size,chunksize;
	int temp;
	unsigned char *data;

	Stuff::FileStream stream(filename,Stuff::FileStream::WriteOnly);

	temp = m_Width;
	temp *= -1;
	stream << temp;

	temp = m_Height;
	temp *= -1;
	stream << temp;

	size = m_Height / 10;
	chunksize = m_Width /10;
	chunksize <<= 1;

	data = (unsigned char *) &(m_Data[0]);
	stream << (DWORD) (chunksize * size);
	for (i=0;i<size;i++)
	{
		stream.WriteBytes (data,chunksize);
		data += chunksize;
	}
}

bool CMoveGrid::ConstructStream (Stuff::MemoryStream *stream,const char *filename)
{
	DWORD *file;
	DWORD filesize;
	DWORD *data;
	unsigned char *dataptr;
	int twidth,theight;

	if (filename)
	{
		gos_GetFile (filename,(unsigned char **) &file,&filesize);
		data = file;
		twidth = *((int*) data);
		if (twidth > 0)
		{
			char str[255];
			sprintf (str,"need to recreate lattice for %s, very old data",filename);
			STOP ((str));
		}
		data++;
		theight = *((int*) data);
		if (theight > 0)
		{
			char str[255];
			sprintf (str,"need to recreate lattice for %s, very old data",filename);
			STOP ((str));
		}
//		*stream << (-1*twidth);				// width			negative for new style
//		*stream << (-1*theight);				// height   negative for 2 byte format
		*stream << (5120);				// width			negative for new style
		*stream << (5120);				// height   negative for 2 byte format
		data++;
		filesize -= (sizeof (DWORD) * 3);
//		*stream << filesize;
		*stream << (512*512*2);				
		data++;
		int deltax=0,deltay=0;
		switch (twidth)
		{
		case -1280:
			deltax = 192;
			break;
		case -2560:
			deltax = 128;
			break;
		case -3840:
			deltax = 64;
			break;
		case -5120:
			deltax = 0;
			break;
		default:
			return false;
//			STOP (("Unsupported map width in cmovegrid::constructstream"));
			break;
		}
		switch (theight)
		{
		case -1280:
			deltay = 192;
			break;
		case -2560:
			deltay = 128;
			break;
		case -3840:
			deltay = 64;
			break;
		case -5120:
			deltay = 0;
			break;
		default:
			return false;
//			STOP (("Unsupported map height in cmovegrid::constructstream"));
			break;
		}
		if (!filesize)
			STOP (("no data in cmovegrid::constructstream"));
		int x,y,linesize;
		int byteswritten=0;
		unsigned short blankline[512];
		linesize = twidth/-10;
		dataptr = (unsigned char *) data;
		stream->AllocateBytes (512*512*2);
		for (x=0;x<512;x++)
		{
			blankline[x] = BLOCKED_FLAGS;
		}
		for (y=0;y<deltay;y++)
		{
			stream->WriteBytes(blankline,512*2);
		}
		for (y=deltay;y<(512-deltay);y++)
		{
//			for (x=0;x<deltax;x++)
			{
				if (deltax != 0)
					stream->WriteBytes (blankline,deltax*2);
			}
//			for (x=deltax;x<(512-deltax);x++)
			{
				stream->WriteBytes (dataptr,linesize*2);
				dataptr += linesize*2;
				byteswritten += linesize*2;
			}
//			for (x=(512-deltax);x<512;x++)
			{
				if (deltax != 0)
					stream->WriteBytes (blankline,deltax*2);
			}
		}
		for (y=(512-deltay);y<512;y++)
		{
			stream->WriteBytes(blankline,512*2);
		}
//		stream->WriteBytes (dataptr =,filesize);
		gos_Free (file);
	}
	else
	{
//		PAUSE (("constructing passability map for unknown map, defaulting to impassable"));
		int x,y;
		unsigned short blankline[512];
		stream->AllocateBytes (512*512*2);
		for (x=0;x<512;x++)
		{
			blankline[x] = BLOCKED_FLAGS;
		}

		*stream << (5120);				// width			negative for new style
		*stream << (5120);				// height   negative for 2 byte format
		*stream << (512*512*2);				

		
		for (y=0;y<512;y++)
		{
			stream->WriteBytes(blankline,512*2);
		}
		return false;
	}
	return true;
}

void CMoveGrid::Clear (void)
{
	int i,j;

	for (i=0;i<m_AllocHeight;i++)
		for (j=0;j<m_AllocWidth;j++)
			m_Data [(i*m_AllocWidth)+j] = BLOCKED_FLAGS;
}
CAirMoveGrid::CAirMoveGrid (void)
{
	m_Data = NULL;
	m_Width = 0;
	m_Height = 0;
	m_AllocWidth = 0;
	m_AllocHeight = 0;
}

CAirMoveGrid::CAirMoveGrid (Stuff::MemoryStream *stream)
{
	int i,size,chunksize;
	DWORD datasize;

	m_Data = NULL;
	m_AllocWidth = 0;
	m_AllocHeight = 0;
	*stream >> m_Width;
	*stream >> m_Height;
	*stream >> datasize;
	if ((m_Width == 0) && (m_Height == 0) && (datasize == 0))
		return;

	Verify (m_Width == MaxX-MinX);
	Verify (m_Height == MaxZ-MinZ);
	Construct (m_Width,m_Height);
	size = m_Height / AIR_BLOCK_GRID_RES;
	chunksize = m_Width / AIR_BLOCK_GRID_RES;
	for (i=0;i<size;i++)
	{
		stream->ReadBytes (m_Data[i],chunksize);
		datasize -= chunksize;
	}
//	Verify (datasize == 0);
}

void CAirMoveGrid::SaveData (const char *filename)
{
	int i,size,chunksize;
	int temp;

	Stuff::FileStream stream(filename,Stuff::FileStream::WriteOnly);

	temp = m_Width;
	stream << temp;

	temp = m_Height;
	stream << temp;

	size = m_Height / AIR_BLOCK_GRID_RES;
	chunksize = m_Width /AIR_BLOCK_GRID_RES;

	stream << (DWORD) (chunksize * size);
	for (i=0;i<size;i++)
	{
		stream.WriteBytes (m_Data[i],chunksize);
	}
}

void CAirMoveGrid::ConstructStream (Stuff::MemoryStream *stream,const char *filename)
{
	DWORD *file;
	DWORD filesize;
	DWORD *data;
	int twidth,theight;

	if (filename)
	{
		gos_GetFile (filename,(unsigned char **) &file,&filesize);
		data = file;
		twidth = *((int*) data);
		data++;
		theight = *((int*) data);
		*stream << twidth;				// width	
		*stream << theight;				// height
		data++;
		filesize -= (sizeof (DWORD) * 3);
		*stream << filesize;
		data++;
		if (filesize)
			stream->WriteBytes (data,filesize);
		gos_Free (file);
	}
	else
	{
		*stream << (DWORD) 0;			// for width
		*stream << (DWORD) 0;			// for height
		*stream << (DWORD) 0;			// for datasize
	}
}

void CAirMoveGrid::Clear (void)
{
	int i,j;

	for (i=0;i<m_AllocHeight;i++)
		for (j=0;j<m_AllocWidth;j++)
			m_Data [i][j] = 0;
}

void CRailGraph::AnalyzePaths (void)
{
	int size;
	NameTable *table = NameTable::GetInstance();
	char text[255];
	int index,j;

	Check_Object (table);
	{
		size = table->nameTableArray[NameTable::PathArray].GetLength ();
		for (j=0;j<size;j++)
		{
			NameTableEntry *data;
			Entity *who;
			Path *path;

			data = &table->nameTableArray [NameTable::PathArray][j];

			who = data->dataPointer->GetCurrent ();
			if (!who)
				continue;
			Verify (who->IsDerivedFrom (Path::DefaultData));
			path = (Path *) who;
			stlport::vector<Stuff::Point3D>::iterator iter;
			
			for (index=0,iter = path->pathPoints.begin ();iter != path->pathPoints.end ();iter++,index++)
			{
				int x,y;

				x = (int) iter->x;
				y = (int) iter->z;
				x /= BLOCK_GRID_RES;
				y /= BLOCK_GRID_RES;
				if ((m_MoveGrid->PassableLocal (x,y) & BLOCKED_FLAGS) != BLOCKED_FLAGS)
					continue;
				{
					sprintf (text,"Path %s, index %d is invalid. Do you wish to remove this point?",(char *) data->objectName,index);
					if (MessageBox (NULL,text,"Path error",MB_YESNO) == IDYES)
					{
						iter = path->pathPoints.erase (iter);
						iter--;
					}
				}
			}
		}
	}
}
