//===========================================================================//
// File:	Map.cpp                                                   //
// Project: MUNGA           Brick: Interest Manager	                       //
// Contents: Interface specifications for interest manager						  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  -----------------------------------------------------------//
// 11/04/94  ECH  Initial coding.                                            //
// 11/29/94  JMA  Changed Identities to IDs                                  //
// 02/10/95  CPB  Added GaugeInterestType                                    //
// 08/25/97  ECH  Infrastructure changes.                                    //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"
#include "Tile.hpp"
#include <ElementRenderer\GridElement.hpp>
#include "Zone.hpp"
#include "Map.hpp"
#include "CollisionVolume.hpp"
#include "LightManager.hpp"
#include <ElementRenderer\StateChange.hpp>
#include <ElementRenderer\CameraElement.hpp>
#include <MLR\MLRInfiniteLightWithFalloff.hpp>
#include "Application.hpp"

#if defined(BSP_BUG)
	bool Adept::debug_bsp=true;
#endif

bool
	TerrainBSP::TestLine(
		CollisionQuery *query,
		Scalar enter,
		Scalar leave
	)
{
	Check_Object(this);
	Check_Object(query);

	//
	//-----------------------------------------
	// Set up the recursion avoidance variables
	//-----------------------------------------
	//
	Check_Object(query->m_line);
	Line3D *line = query->m_line;
	Check_Object(query->m_zone);
	Zone *zone = query->m_zone;
	Check_Object(query->m_tile);
	Tile *tile = query->m_tile;
	TerrainBSP *bsp = this;

	//
	//-------------------------------------------------------------------------
	// Here is the real start of the loop.  We first want to find which side of
	// the plane we are on and which direction we are headed
	//-------------------------------------------------------------------------
	//
Start_Over:
	Check_Object(bsp);
	Verify(enter >= 0.0f && enter <= leave);
	Verify(bsp->m_planeIndex < zone->m_planeCount);
	const Plane *plane = &zone->m_planes[bsp->m_planeIndex];
	Scalar dot = plane->normal * line->m_direction;
	Scalar separation = plane->GetDistanceTo(line->m_origin);

	//
	//----------------------------------------------------------
	// We are heading into the plane, so see when we will hit it
	//----------------------------------------------------------
	//
	#if defined(BSP_BUG)
		if (debug_bsp)
		{
			SPEW((
				BSP_BUG,
				"### %sBSP %d",
				(plane->m_normal.y > SMALL) ? "" : "pseudo ",
				bsp-tile->m_BSP
			));
			SPEW((
				BSP_BUG,
				"    enter: %f, leave: %f, dot: %f, sep: %f",
				enter,
				leave,
				dot,
				separation
			));
		}
	#endif
	if (dot < -SMALL)
	{
		Scalar distance = -separation/dot;
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((
					BSP_BUG,
					"  distance into plane = %f",
					distance
				));
		#endif

		//
		//------------------------------------------------------------------
		// See if we are still on the outside of the plane when we take into
		// account the distance already moved along the line.  If we are,
		// look to see if the line will strike the plane.  If not, and there
		// is no tree on the outer halfspace of the plane, we won't hit
		// anything so just return
		//------------------------------------------------------------------
		//
		if (distance > enter)
		{
			if (distance > leave)
			{
				if (!bsp->m_outerIndex)
				{
					#if defined(BSP_BUG)
						if (debug_bsp)
							SPEW((BSP_BUG, "    won't reach plane and outside is empty"));
					#endif
					return false;
				}

				//
				//-------------------------------------------------------------
				// There is a tree to check on the outside of our plane, so set
				// it as the current tree and start over
				//-------------------------------------------------------------
				//
				#if defined(BSP_BUG)
					if (debug_bsp)
						SPEW((BSP_BUG, "    won't reach plane, jumping to outer halfspace"));
				#endif
				Verify(bsp->m_outerIndex < tile->m_BSPCount);
				bsp = &tile->m_BSP[bsp->m_outerIndex];
				goto Start_Over;
			}

			//
			//---------------------------------------------------------------
			// We will cross into the plane space, so first check the outer
			// half via recursion, as we will need to continue into the inner
			// space.  We will shorten the line for his test to make sure
			// that any plane hits happen only within our outer halfspace.
			// If our child hits something, stop the traversal.  Note that if
			// there is no tree on the outer space, it is empty and can just
			// be ignored
			//---------------------------------------------------------------
			//
			if (bsp->m_outerIndex)
			{
				Verify(bsp->m_outerIndex < tile->m_BSPCount);
				Verify(enter <= distance);
				Verify(distance <= leave);
				Scalar new_leave = leave;
				Max_Clamp(new_leave, distance);
				if (tile->m_BSP[bsp->m_outerIndex].TestLine(query, enter, new_leave))
				{
					//
					// I'm commenting out this line because I have never beaten the case where
					// rounding error causes the line to exit one tile and enter the next tile
					// under the terrain...
					//
					// Verify(!line->m_length || query->m_normal->y > SMALL);
					return true;
				}
				#if defined(BSP_BUG)
					if (debug_bsp)
						SPEW((BSP_BUG, "### returning to BSP %d", bsp-tile->m_BSP));
				#endif
			}

			//
			//-----------------------------------------------------------------
			// We have struck the plane, so this is the normal of impact unless
			// we go through another plane.  Bump the enter to be on our plane
			// surface to make sure that nothing tries to check against the
			// outer space of the plane
			//-----------------------------------------------------------------
			//
			#if defined(BSP_BUG)
				if (debug_bsp)
					SPEW((BSP_BUG, "    crossing <%f,%f,%f>", plane->m_normal.x, plane->m_normal.y, plane->m_normal.z));
			#endif
			*query->m_normal = plane->normal;
			enter = distance;
		}

		//
		//----------------------------------------------------------------------
		// At this point we need only concern ourself with the inner space of
		// the plane.  If there is no tree there, then it is solid space.  We
		// set the line length to the amount already traveled and then stop with
		// a successful hit
		//----------------------------------------------------------------------
		//
		if (!bsp->m_innerIndex)
		{
			#if defined(BSP_BUG)
				if (debug_bsp)
					SPEW((BSP_BUG, "    inside is solid"));
			#endif
			line->m_length = enter;
			//
			// I'm commenting out this line because I have never beaten the case where
			// rounding error causes the line to exit one tile and enter the next tile
			// under the terrain...
			//
			// Verify(!line->m_length || query->m_normal->y > SMALL);
			return true;
		}

		//
		//------------------------------------------------------------
		// Set our inner space tree as the current tree and start over
		//------------------------------------------------------------
		//
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((BSP_BUG, "    jumping to inner halfspace"));
		#endif
		Verify(bsp->m_innerIndex < tile->m_BSPCount);
		bsp = &tile->m_BSP[bsp->m_innerIndex];
		goto Start_Over;
	}

	//
	//---------------------------------------------------------------
	// If we are heading out of the plane, see when we will strike it
	//---------------------------------------------------------------
	//
	else if (dot > SMALL)
	{
		Scalar distance = -separation/dot;
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((
					BSP_BUG,
					"  distance out of plane = %f",
					distance
				));
		#endif

		//
		//----------------------------------------------------------------
		// If we are still inside the plane when we take into account the
		// distance already travelled, see if the inside is solid.  If so,
		// set the line length to that already travelled and stop the
		// traversal with a successful hit
		//----------------------------------------------------------------
		//
		if (distance > enter)
		{
			if (!bsp->m_innerIndex)
			{
				#if defined(BSP_BUG)
					if (debug_bsp)
						SPEW((BSP_BUG, "    inside is solid"));
				#endif
				line->m_length = enter;
				//
				// I'm commenting out this line because I have never beaten the case where
				// rounding error causes the line to exit one tile and enter the next tile
				// under the terrain...
				//
				// Verify(!line->m_length || query->m_normal->y > SMALL);
				return true;
			}

			//
			//---------------------------------------------------------------
			// We now know there is a tree inside our plane halfspace.  If we
			// won't hit the surface of the plane, set it as the current tree
			// and start over
			//---------------------------------------------------------------
			//
			if (distance > leave)
			{
				#if defined(BSP_BUG)
					if (debug_bsp)
						SPEW((BSP_BUG, "    jumping into inner halfspace"));
				#endif
				Verify(bsp->m_innerIndex < tile->m_BSPCount);
				bsp = &tile->m_BSP[bsp->m_innerIndex];
				goto Start_Over;
			}

			//
			//---------------------------------------------------------------
			// We will cross into the plane space, so first check the inner
			// half via recursion, as we will need to continue into the outer
			// space.  We will shorten the line for his test to make sure
			// that any plane hits happen only within our inner halfspace.
			// If our child hits something, stop the traversal
			//---------------------------------------------------------------
			//
			Verify(bsp->m_innerIndex < tile->m_BSPCount);
			Verify(enter <= distance);
			Verify(distance <= leave);
			Scalar new_leave = leave;
			Max_Clamp(new_leave, distance);
			if (tile->m_BSP[bsp->m_innerIndex].TestLine(query, enter, new_leave))
			{
				//
				// I'm commenting out this line because I have never beaten the case where
				// rounding error causes the line to exit one tile and enter the next tile
				// under the terrain...
				//
				// Verify(!line->m_length || query->m_normal->y > SMALL);
				return true;
			}
			#if defined(BSP_BUG)
				if (debug_bsp)
					SPEW((BSP_BUG, "### returning to BSP %d", bsp-tile->m_BSP));
			#endif
			enter = distance;
		}

		//
		//---------------------------------------------------------------------
		// If we get here, we only need concern ourselves with the outer space.
		// If it is empty, stop the traversal with a miss
		//---------------------------------------------------------------------
		//
		if (!bsp->m_outerIndex)
		{
			#if defined(BSP_BUG)
				if (debug_bsp)
					SPEW((BSP_BUG, "    outside is empty"));
			#endif
			return false;
		}

		//
		//------------------------------------------------------------
		// Set the outer space tree as the current tree and start over
		//------------------------------------------------------------
		//
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((BSP_BUG, "    jumping to outer halfspace"));
		#endif
		Verify(bsp->m_outerIndex < tile->m_BSPCount);
		bsp = &tile->m_BSP[bsp->m_outerIndex];
		goto Start_Over;
	}

	//
	//----------------------------------------------------------------------
	// We are moving parallel to the plane, so check only the inside of the
	// plane if that's where we are
	//----------------------------------------------------------------------
	//
	else if (separation < SMALL)
	{
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((BSP_BUG, "  Inside plane"));
		#endif
		if (!bsp->m_innerIndex)
		{
			#if defined(BSP_BUG)
				if (debug_bsp)
					SPEW((BSP_BUG, "    inside is solid"));
			#endif
			line->m_length = enter;
			//
			// I'm commenting out this line because I have never beaten the case where
			// rounding error causes the line to exit one tile and enter the next tile
			// under the terrain...
			//
			// Verify(!line->m_length || query->m_normal->y > SMALL);
			return true;
		}
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((BSP_BUG, "    jumping to inner halfspace"));
		#endif
		Verify(bsp->m_innerIndex < tile->m_BSPCount);
		bsp = &tile->m_BSP[bsp->m_innerIndex];
		goto Start_Over;
	}

	//
	//------------------------
	// Check outside the plane
	//------------------------
	//
	#if defined(BSP_BUG)
		if (debug_bsp)
			SPEW((BSP_BUG, "  Outside plane"));
	#endif
	if (!bsp->m_outerIndex)
	{
		#if defined(BSP_BUG)
			if (debug_bsp)
				SPEW((BSP_BUG, "    outside is empty"));
		#endif
		return false;
	}
	#if defined(BSP_BUG)
		if (debug_bsp)
			SPEW((BSP_BUG, "    jumping to outer halfspace"));
	#endif
	Verify(bsp->m_outerIndex < tile->m_BSPCount);
	bsp = &tile->m_BSP[bsp->m_outerIndex];
	goto Start_Over;
}

//#############################################################################
//###########################    Zone    #################################
//#############################################################################

Tile::ClassData
	*Tile::DefaultData = NULL;

const Stuff::RGBAColor
	*Tile::s_GroundColor = NULL;

DECLARE_TIMER(static, Ray_Vs_BSP);
DECLARE_TIMER(static, Ray_Vs_OBB);

static bool Hide_Ground=false;
static bool __stdcall CheckHideGround() {return Hide_Ground;}
static void __stdcall EnableHideGround() {Hide_Ground = !Hide_Ground;}

static bool Hide_TileEntities=false;
static bool __stdcall CheckHideTileEntities() {return Hide_TileEntities;}
static void __stdcall EnableHideTileEntities() {Hide_TileEntities = !Hide_TileEntities;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			TileClassID,
			"Adept::Tile",
			ElementRenderer::GroupElement::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Register_Object(DefaultData);

	s_GroundColor = NULL;
	Initialize_Timer(Ray_Vs_BSP, "Ray vs BSP");
	Initialize_Timer(Ray_Vs_OBB, "Ray vs OBB");

	AddDebuggerMenuItem("Libraries\\Adept\\Hide Ground", CheckHideGround, EnableHideGround, 0 );
	AddDebuggerMenuItem("Libraries\\Adept\\Hide Tile Entities", CheckHideTileEntities, EnableHideTileEntities, 0 );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Tile::Tile(
	Zone *zone,
	BYTE row,
	BYTE column
):
	GroupElement(DefaultData),
	m_collidees(NULL),
	m_colliders(NULL),
	m_lights(NULL),
	damagableEntityList(NULL)
{
	Check_Pointer(this);

	m_draw = static_cast<DrawMethod>(&Tile::Draw);
	AdoptStateChange(new ElementRenderer::StateChange);
	m_row = row;
	m_column = column;
	m_zone = zone;
	m_ground = NULL;
	m_BSP = NULL;
	m_BSPCount = 0;
	m_lightsChanged = false;


	m_DamageBitStart = -1;
	m_damagableEntityCount = 0;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Tile::~Tile()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::Save(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

	//
	//--------------------------
	// Write stuff to the stream
	//--------------------------
	//
	*stream << static_cast<int>(ElementRenderer::GroupElementClassID) << m_state;
	if (m_stateChange)
	{
		Check_Object(m_stateChange);
		Verify(m_state & PropertyChangeFlag);
		m_stateChange->Save(stream);
	}
	if (!IsLocalToParentIdentity())
		*stream << m_localToParent;

	//
	//--------------------------
	// Write the bounding volume
	//--------------------------
	//
	if (IsBoundedByOBB())
	{
		*stream << m_localOBB.localToParent << m_localOBB.axisExtents
			<< m_localOBB.sphereRadius;
	}
	else
	{
		Point3D center(m_localOBB.localToParent);
		*stream << center;
		*stream << m_localOBB.sphereRadius;
	}

	//
	//-----------------------------------------------------------
	// Write out the child count and then the children themselves
	//-----------------------------------------------------------
	//
	ChainIteratorOf<ElementRenderer::Element*> elements(&m_group);
	ElementRenderer::Element *element;
	*stream << elements.GetSize();
	while ((element = elements.ReadAndNext()) != NULL)
	{
		Check_Object(element);
		element->Save(stream);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void*
	Tile::TestTile(
		ElementRenderer::GridElement *grid,
		WORD cell,
		ElementRenderer::CollisionQuery *query,
		Scalar enter,
		Scalar leave
	)
{
	Check_Object(grid);
	Check_Object(query);
	Verify(query->m_line->m_length >= 0.0f);
	Entity::CollisionQuery *masked_query =
		Cast_Pointer(Entity::CollisionQuery*, query);

	//
	//-------------------------------
	// Figure out what tile we are in
	//-------------------------------
	//
	Tile *tile = Cast_Object(Tile*, grid->GetIndexedElement(cell));
	Entity *result = NULL;

	//
	//-----------------------------------------------
	// Find the zone - it should be the grid's parent
	//-----------------------------------------------
	//
	if (masked_query->m_collisionMask&Entity::WalkerMask)
	{
		Zone *zone = tile->m_zone;
		Check_Object(zone);
		Normal3D normal;
		Check_Object(masked_query->m_line);
		normal.Vector3D::Negate(masked_query->m_line->m_direction);
		TerrainBSP::CollisionQuery
			bsp_query(masked_query->m_line, &normal, zone, tile);

		//
		//-------------------------
		// Try out our new BSP code
		//-------------------------
		//
		bool hit;
		Check_Object(tile->m_BSP);
		{
			RAYCAST_LOGIC("vs. BSPs");
			Start_Timer(Ray_Vs_BSP);
			hit = tile->m_BSP->TestLine(&bsp_query, enter, leave);
			Stop_Timer(Ray_Vs_BSP);
		}
		if (hit)
		{
			*masked_query->m_normal = normal;
			//
			//------------------------------------------------------------------------
			// This is turned off for now because I couldn't figure out the case where
			// a ray exits one tile and enters another tile underneath the terrain - I
			// suspect it has something to do with roundoff errors
			//------------------------------------------------------------------------
			//
			Verify(*query->m_normal * query->m_line->m_direction < -SMALL);
			Point3D impact;
			query->m_line->FindEnd(&impact);
			query->m_material = zone->GetMaterial(impact);
			result = Map::GetInstance();
		}
	}

	//
	//---------------------------
	// Check each collidee entity
	//---------------------------
	//
	ChainIteratorOf<Entity*> collidees(&tile->m_collidees);
	Entity *entity;
	while ((entity = collidees.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		Verify(!entity->IsACollider());
		Verify(entity->GetInterestLevel() != Entity::DormantInterestLevel);

		//
		//---------------------------------------------------------------------
		// If the ray is coming from us, don't check.  Also don't bother if the
		// masks don't match
		//---------------------------------------------------------------------
		//
		if (
			entity == masked_query->m_raySource
			 || !entity->CouldCollideWith(masked_query->m_collisionMask)
		)
			continue;

		//
		//-----------------------------------------------------------------------
		// If there is a hit on this element, set the return value and figure out
		// the relative position of the hit to the target entity
		//-----------------------------------------------------------------------
		//
		CollisionVolume *bounds = entity->GetHierarchicalVolume();
		if (!bounds)
			bounds = entity->GetSolidVolume();
		Check_Object(bounds);
		{
			RAYCAST_LOGIC("vs. Collidees");
			Start_Timer(Ray_Vs_OBB);
			Verify(query->m_line->m_length >= 0.0f);
			bounds = bounds->CastRay(masked_query, entity, entity->GetLocalToWorld());
			Stop_Timer(Ray_Vs_OBB);
		}
		if (bounds)
		{
			result = bounds->m_entity;
			query->m_data = NULL;
		}
	}

	//
	//---------------------------
	// Check each collider entity
	//---------------------------
	//
	ChainIteratorOf<Entity*> colliders(&tile->m_colliders);
	while ((entity = colliders.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		Verify(entity->IsACollider());
		Verify(entity->GetInterestLevel() != Entity::DormantInterestLevel);

		//
		//---------------------------------------------------------------------
		// If the ray is coming from us, don't check.  Also don't bother if the
		// masks don't match
		//---------------------------------------------------------------------
		//
		if (
			entity == masked_query->m_raySource
			 || !entity->CouldCollideWith(masked_query->m_collisionMask)
		)
			continue;

		//
		//-----------------------------------------------------------------------
		// If there is a hit on this element, set the return value and figure out
		// the relative position of the hit to the target entity
		//-----------------------------------------------------------------------
		//
		CollisionVolume *bounds = entity->GetHierarchicalVolume();
		if (!bounds)
			bounds = entity->GetSolidVolume();
		Check_Object(bounds);
		{
			RAYCAST_LOGIC("vs. Colliders");
			Start_Timer(Ray_Vs_OBB);
			bounds = bounds->CastRay(masked_query, entity, entity->GetLocalToWorld());
			Stop_Timer(Ray_Vs_OBB);
		}
		if (bounds)
		{
			result = bounds->m_entity;
			query->m_data = NULL;
		}
	}
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::SetDrawState()
{
	Check_Object(this);
	m_draw = static_cast<DrawMethod>(&Tile::Draw);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::Draw(
		ElementRenderer::CameraElement *camera,
		const ElementRenderer::StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);

	//
	//--------------------------
	// Construct our light array
	//--------------------------
	//
	if (m_lightsChanged)
		UpdateLights();

	//
	//-------------------
	// Mix the properties
	//-------------------
	//
	Check_Object(m_stateChange);
	ElementRenderer::StateChange mixed;
	mixed.Mix(*inherited_state, *m_stateChange);
	MixLights(
		mixed.m_lights,
		inherited_state->m_lights,
		m_stateChange->m_lights
	);

	//
	//--------------------------------------
	// Draw our bounds if we are supposed to
	//--------------------------------------
	//
	#if defined(LAB_ONLY)
		Callback *callback = GetCallbackSet();
		unsigned index = GetCallbackIndex();
		if (callback && callback[index])
		{
			Check_Pointer(callback[index]);
			(*callback[index])(camera, inherited_state, culling_state, this);
		}
	#endif

	//
	//------------------
	// Draw our children
	//------------------
	//
	ChainIteratorOf<ElementRenderer::Element*> children(&m_group);
	ElementRenderer::Element *child;
	while ((child = children.ReadAndNext()) != NULL)
	{
		Check_Object(child);

		//
		//---------------------
		// Deal with the ground
		//---------------------
		//
		if (child==m_ground)
		{
			#if defined(LAB_ONLY)
				if (Hide_Ground)
					continue;
			#endif
			if (s_GroundColor)
			{
				RGBAColor old(Element::FadeColor);
				Element::FadeColor = *s_GroundColor;
				camera->DrawElement(child, &mixed);
				Element::FadeColor = old;
			}
			else
				camera->DrawElement(child, &mixed);
		}

		//
		//------------------------
		// Draw our other children
		//------------------------
		//
		else
		{
			#if defined(LAB_ONLY)
				if (Hide_TileEntities)
					continue;
			#endif
			camera->DrawElement(child, &mixed);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Tile::UpdateLights()
{
	Check_Object(this);
	Verify(m_lightsChanged);

	ChainIteratorOf<FiniteLight*> light_chain(&m_lights);
	gosFX::Light *light;
	Check_Object(m_stateChange);
	int i=0;
	while (
		(light = light_chain.ReadAndNext()) != NULL
		 && i<MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive
	)
	{
		Check_Object(light);
		if(light->m_light->GetIntensity()>Stuff::SMALL)
		{
			m_stateChange->m_lights[i++] = light->m_light;
		}
	}
	if (i < MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive)
		m_stateChange->m_lights[i] = NULL;
	Verify(i <= MidLevelRenderer::Limits::Max_Number_Of_Lights_Per_Primitive);
	m_lightsChanged = false;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	Tile::AddEntityToDamagableList(Entity* entity)
{

	if (!entity->IsDestroyable())
		return;

	
	if (Application::GetInstance()->GetApplicationState() != ApplicationStateEngine::LoadingGameState)
	{
		return;
	}


	if (m_damagableEntityCount >= damagableEntityList.GetLength())
	{
		damagableEntityList.SetLength(m_damagableEntityCount + 10);
	}


	damagableEntityList[m_damagableEntityCount] = entity;
	++m_damagableEntityCount;


}
