//===========================================================================//
// File:	MechAI.cpp																	     //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 07/13/99  AHF  Created file                          //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "mw4.hpp"
#include "noncomai.hpp"
#include "Adept\Interface.hpp"
#include "Adept\Application.hpp"
#include "adept\map.hpp"
#include "mwobject.hpp"
#include "rail_move.hpp"
#include "noncom.hpp"
#include "mwplayer.hpp"
#include "mwmap.hpp"
#include <adept\collisionvolume.hpp>
#include <Adept\DamageObject.hpp>

#pragma warning (push)
#include <algorithm>
#pragma warning (pop)

using namespace MechWarrior4;
using namespace MW4AI;

//#############################################################################
//###############################    AI      ##################################
//#############################################################################

NonComAI::ClassData* NonComAI::DefaultData = NULL;
__int64	tNonComTime;

const Stuff::Scalar RUN_SPEED = 10.0f;				// meters per second to use run frame
const Stuff::Scalar WALK_SPEED = 2.5f;				// meters per second to use run frame
const Stuff::Scalar ANIMAL_SPEED = 10.0f;

const Stuff::Scalar max_heading_change_time = 1.5f;
const Stuff::Scalar min_near_player_duration = 4.0f;
const Stuff::Scalar min_flock_update_time = 1.0f;

const Receiver::MessageEntry
NonComAI::MessageEntries[]=
{
	MESSAGE_ENTRY(NonComAI, DebugText),
};


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
	new ClassData(
							 NonComAIClassID,
							 "MechWarrior4::NonComAI",
							 AI::DefaultData,
							 ELEMENTS(MessageEntries), MessageEntries,
							 (Entity::Factory)Make,
							 (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
							 ExecutionStateEngine::DefaultData,
							 (Entity::GameModel::Factory)GameModel::ConstructGameModel,
							 NULL,
							 (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
							 (Entity::GameModel::ModelWrite)GameModel::WriteToText,
							 (Entity::GameModel::ModelSave)GameModel::SaveGameModel
							 );
	Register_Object(DefaultData);
#if !defined(NO_TIMERS)
	AddStatistic( "NonComAI ", "%", gos_timedata, (void*)&tNonComTime, 0 );
#endif

}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NonComAI* NonComAI::Make(CreateMessage *message,ReplicatorID *base_id)
{
	Check_Object(message);
	AutoHeap local_heap (g_AIHeap);
	NonComAI *new_entity = new NonComAI(DefaultData, message, base_id, NULL);
	Check_Object(new_entity);
	new_entity->SyncMatrices(true);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NonComAI::NonComAI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element)
: AI(class_data, message, base_id, element)
{
	Check_Pointer(this);
	Check_Object(message);
	m_FlockList.clear ();
	scriptName[0] = 0;
	m_MultX = 0;
	m_MultZ = 0;
	m_SpeedX = 0;
	m_SpeedZ = 0;
	m_LastObject = -1;
	m_LastType = -1;

	m_FidgetDestX = 0;
	m_FidgetDestZ = 0;

	m_LastHeadingChangeTime = 0;
	m_LastTimeNearPlayer = 0;

	m_MoveType = WALK;
	m_FlockAimDelta = Point3D(0,0,0);
	m_NextFlockUpdateTime = 0;

	// Since AI turns off ai if we are not the server ( READ: A GOOD THING )
	// we need to hack non-com's back on!
	if (message->entityID != NameTable::NullObjectID && !Network::GetInstance()->AmIServer())
	{
		Check_Object(NameTable::GetInstance());
		vehicle = NameTable::GetInstance()->FindData(message->entityID);
		Check_Object (vehicle);
		Verify (vehicle->IsDerivedFrom (MWObject::DefaultData));
		ConnectEntity ((MWObject *) vehicle);
	}

	m_NonCom = Cast_Object (NonCom *,vehicle);
	Check_Object (m_NonCom);


	executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::Die (void)
{
	Check_Object (this);
	inherited::Die ();
}

void NonComAI::PostCollisionExecute(Time till)
{
	POSTCOLLISION_LOGIC("NonCom AI");
	if (memory[TYPE_INDEX] == FLOCK_TYPE)
	{
		if (Target())
		{
			Check_Object (Target());
			Check_Object (m_NonCom);

			if (Target()->IsDestroyed())
				m_NonCom->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
		}
	}
	BaseClass::PostCollisionExecute (till);

}
void NonComAI::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("NonCom AI");

	//
	//---------------------------------
	// Verify that this always executes
	//---------------------------------
	//
	Check_Object(executionState);
	Verify (getEntity ());
	gosREPORT (getEntity ()->IsDerivedFrom (NonCom::DefaultData),"You have a non com ai attached to a unit that is not a non com");
	Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState);

	if (!(!m_FirstFrame && m_SecondFrame))
	{
		if (IsAnimal() == true)
		{
			m_NonCom->SetAsAnimal();
		}

		AI::PreCollisionExecute (till);
		return;
	}

//	obj->TurnOff ();

	__int64 TempTime=GetCycles();

	if (memory[TYPE_INDEX] == FIDGET_TYPE)
	{
		DoFidget (till);
	}
	else if (memory[TYPE_INDEX] == TRACE_TYPE)
	{
		DoTrace (till);
	}
	else if (memory[TYPE_INDEX] == FLOCK_TYPE)
	{
		DoFlock (till);
	}
#if defined(LAB_ONLY)
	else
	{
		PAUSE (("unknown noncomai type in %s",instanceName));
	}
#endif
	
	UpdatePos (till);
	Check_Object (m_NonCom);
/*
	if ((m_SpeedX > 5.0f) || (m_SpeedZ > 5.0f) || (m_SpeedX < -5.0f) || (m_SpeedZ < -5.0f))
		m_NonCom->Running (true);
	else
		m_NonCom->Running (false);
*/

//	m_NonCom->Speed (m_FakeSpeedX > m_FakeSpeedZ ? m_FakeSpeedX+(m_FakeSpeedZ/2.0f) : m_FakeSpeedZ + (m_FakeSpeedX/2.0f));
	m_NonCom->TextureID ((int) memory[TEXTURE_ID]);
	
	tNonComTime+=GetCycles()-TempTime;

	AI::PreCollisionExecute(till);
}
		
void NonComAI::CreateFakeSpeed (void)
{
	Scalar len = (Scalar) sqrt ((double) (m_SpeedX*m_SpeedX) + (m_SpeedZ*m_SpeedZ));
	if (len == 0)
	{
		m_FakeSpeedX = 0;
		m_FakeSpeedZ = 0;
		m_NonCom->Speed (0);
		return;
	}

	Scalar speed = 0;

	switch (m_MoveType)
	{
		case WALK:
		{
			speed = WALK_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 20);
			break;
		}
		case RUN:
		{
			speed = RUN_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 8);
			break;
		}
		case ANIMAL:
		{
			speed = ANIMAL_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 8);
			break;
		}
	}

	m_FakeSpeedX = (m_SpeedX / len) * speed;
	m_FakeSpeedZ = (m_SpeedZ / len) * speed;
	m_NonCom->Speed(speed);
}

void NonComAI::UpdatePos (Time till)
{
	LinearMatrix4D mat;
	Point3D trans (getEntity ()->GetLocalToWorld ());
	YawPitchRoll rot(getEntity ()->GetLocalToWorld ());

	mat.BuildRotation (rot);

	Max_Clamp (m_SpeedX,memory[SPEED_INDEX]);
	Min_Clamp (m_SpeedX,-memory[SPEED_INDEX]);
	Max_Clamp (m_SpeedZ,memory[SPEED_INDEX]);
	Min_Clamp (m_SpeedZ,-memory[SPEED_INDEX]);

	CreateFakeSpeed ();

	trans.x += m_FakeSpeedX * GetTimeSlice (till);
	trans.z += m_FakeSpeedZ * GetTimeSlice (till);

	BYTE tempmaterial;

//	Scalar head;
//	if (!Small_Enough(m_FakeSpeedZ))
//		head = Arctan(m_FakeSpeedX, m_FakeSpeedZ);
//	else
//		head = 0;
//	YawPitchRoll roll (head,0,0);

	Adept::Entity* entity_hit = 0;
	trans.y = GetMapY (trans.x,trans.z,getEntity(),tempmaterial,trans.y,&entity_hit);

	if (memory[HEIGHT_INDEX] != 0)
	{
		trans.y += memory[HEIGHT_INDEX];
	}
	else
	{
		if (entity_hit != Map::GetInstance())
		{
			trans.y -= 1000.0f;
		}
	}

//	mat.BuildRotation (roll);
	mat.BuildTranslation (trans);
	getEntity ()->SetNewLocalToParent (mat);
	getEntity ()->SyncMatrices(true);
}

	Scalar RandomSign()
	{
		if (Stuff::Random::GetFraction() < 0.5f)
		{
			return (1);
		}

		return (-1);
	}

void NonComAI::DoFidget (Time till)
{
	Verify (getEntity ());
	if (m_LastType != FIDGET_TYPE)
	{
		m_MultX = 0;
		m_MultZ = 0;
		m_CenterPoint = getEntity ()->GetLocalToWorld ();
		m_SpeedX = 0;
		m_SpeedZ = 0;
		m_LastType = FIDGET_TYPE;
	}

	if (m_LastHeadingChangeTime + max_heading_change_time > gos_GetElapsedTime())
	{
		return;
	}

	Point3D loc(getEntity()->GetLocalToWorld ());

	bool avoid_player(false);
	bool recalc_dest(false);
	const Scalar radius = memory[RADIUS_INDEX];
	MWPlayer *player = 0;

	if (MWPlayer::GetInstance() != 0)
	{
		Check_Object(MWPlayer::GetInstance());
		player = Cast_Object (MWPlayer *, MWPlayer::GetInstance());

		if (m_LastTimeNearPlayer + min_near_player_duration < gos_GetElapsedTime())
		{
			Stuff::Scalar playerdist = GetLengthSquared((Point3D)player->GetLocalToWorld(),loc);

			if (playerdist < (radius * radius * 2))
			{
				avoid_player = true;
				recalc_dest = true;
				m_LastTimeNearPlayer = (Stuff::Scalar)gos_GetElapsedTime();
			}
		}
		else
		{
			avoid_player = true;
		}
	}

	if ((m_FidgetDestX == 0) &&
		(m_FidgetDestZ == 0))
	{
		recalc_dest = true;
	}
	else
	{
		Point3D dest(m_FidgetDestX,loc.y,m_FidgetDestZ);

		Scalar distance = GetLengthSquared(loc,dest);

		if (distance < 10 * 10)
		{
			recalc_dest = true;
		}
	}

	Scalar old_destX = m_FidgetDestX;
	Scalar old_destZ = m_FidgetDestZ;

	if (recalc_dest == true)
	{
		if ((avoid_player == true) &&
			(memory[HEIGHT_INDEX] <= 0))
		{
			Point3D playerloc(player->GetLocalToWorld());

			if (playerloc.x < loc.x)
			{
				m_FidgetDestX = loc.x + radius;
			}
			else
			{
				m_FidgetDestX = loc.x - radius;
			}

			if (playerloc.z < loc.z)
			{
				m_FidgetDestZ = loc.z + radius;
			}
			else
			{
				m_FidgetDestZ = loc.z - radius;
			}

			m_FidgetDestX += (RandomSign() * Stuff::Random::GetFraction() * 20);
			m_FidgetDestZ += (RandomSign() * Stuff::Random::GetFraction() * 20);
		}
		else
		{
			Scalar angle = Stuff::Two_Pi * Stuff::Random::GetFraction();

			Stuff::Scalar x_delta = radius * Stuff::Cos(angle);
			Stuff::Scalar z_delta = radius * Stuff::Sin(angle);

			Stuff::Point3D point1(m_CenterPoint);
			point1.x += x_delta;
			point1.z += z_delta;
			Stuff::Point3D point2(m_CenterPoint);
			point2.x -= x_delta;
			point2.z -= z_delta;

			if (GetLengthSquared(loc,point1) < GetLengthSquared(loc,point2))
			{
				m_FidgetDestX = point2.x;
				m_FidgetDestZ = point2.z;
			}
			else
			{
				m_FidgetDestX = point1.x;
				m_FidgetDestZ = point1.z;
			}
		}

		if ((m_FidgetDestX == old_destX) &&
			(m_FidgetDestZ == old_destZ))
		{
			return;
		}

		if (IsAnimal() == true)
		{
			m_MoveType = ANIMAL;
		}
		else
		{
			if (avoid_player == true)
			{
				m_MoveType = RUN;
			}
			else
			{
				m_MoveType = WALK;
			}
		}
	}

	const Scalar speed = memory[SPEED_INDEX];

	if (m_FidgetDestX < loc.x)
	{
		m_SpeedX = -speed;
	}
	else
	{
		m_SpeedX = speed;
	}

	if (m_FidgetDestZ < loc.z)
	{
		m_SpeedZ = -speed;
	}
	else
	{
		m_SpeedZ = speed;
	}

	m_LastHeadingChangeTime = (Scalar)gos_GetElapsedTime();
}

	void SetGenericTraceBox(ExtentBox& extent_box, const Point3D& point, Scalar distance)
	{
		extent_box.minX = point.x - distance;
		extent_box.maxX = point.x + distance;
		extent_box.minY = point.y - distance;
		extent_box.maxY = point.y + distance;
		extent_box.minZ = point.z - distance;
		extent_box.maxZ = point.z + distance;
	}

void NonComAI::DoTrace (Time till)
{
	Verify (getEntity ());
	if (Target() == 0)
	{
		return;
	}

	if (m_LastType != TRACE_TYPE)
	{
		m_MultX = 0;
		m_MultZ = 0;
		m_CenterPoint = getEntity ()->GetLocalToWorld ();
		m_SpeedX = 0;
		m_SpeedZ = 0;
		m_LastType = TRACE_TYPE;

		OBB *obb;
		CollisionVolume* col;

		col = Target ()->GetHierarchicalVolume ();
		if (!col)
			col = Target ()->GetSolidVolume ();

		if (col == 0)
		{
			SetGenericTraceBox(m_TraceBox,(Point3D)(Target()->GetLocalToWorld()),20);
		}
		else
		{
			OBB world_bounds;
			world_bounds.Multiply(col->m_localSpaceBounds, Target()->GetLocalToWorld());
			obb = &(world_bounds);
			m_TraceBox = ExtentBox (*obb);
		}
	}
	bool clockwise;
	int area = 0;
	Point3D loc (getEntity()->GetLocalToWorld ());
	clockwise = memory[CLOCKWISE_INDEX] <=0;
	int cx,cz;
	cx = (int) (((m_TraceBox.maxX - m_TraceBox.minX) / 2.0f) + m_TraceBox.minX);
	cz = (int) (((m_TraceBox.maxZ - m_TraceBox.minZ) / 2.0f) + m_TraceBox.minZ);

	if (loc.x < m_TraceBox.minX)
		area += 0x02;
	else if (loc.x > m_TraceBox.maxX)
		area += 0x04;
	if (loc.z < m_TraceBox.minZ)
		area += 0x01;
	else if (loc.z > m_TraceBox.maxZ)
		area += 0x08;
	if (area == 0)
	{
		if (loc.x < cx)
			area += 0x02;
		else if (loc.x >= cx)
			area += 0x04;
		if (loc.z < cz)
			area += 0x01;
		else if (loc.z >= cz)
			area += 0x08;

	}
	Verify (area != 0);

	m_MultX = 0;
	m_MultZ = 0;
	if (clockwise)
	{
		if (area & 0x01)
		{
			if (!(area & 0x04))
			{
				if (loc.z < m_TraceBox.minZ - Trace_Distance - Trace_Fudge)
					m_MultZ += 0.25f;
				else if (loc.z > m_TraceBox.minZ - Trace_Distance + Trace_Fudge)
					m_MultZ += -0.25f;
			}
			m_MultX += 1.0f;
		}
		if (area & 0x08)
		{
			if (!(area & 0x02))
			{
				if (loc.z > m_TraceBox.maxZ + Trace_Distance + Trace_Fudge)
					m_MultZ += -0.25f;
				else if (loc.z < m_TraceBox.maxZ + Trace_Distance - Trace_Fudge)
					m_MultZ += 0.25f;
			}
			m_MultX += -1.0f;
		}

		if (area & 0x02)
		{
			if (!(area & 0x01))
			{
				if (loc.x < m_TraceBox.minX - Trace_Distance - Trace_Fudge)
					m_MultX += 0.25f;
				else if (loc.x > m_TraceBox.minX - Trace_Distance + Trace_Fudge)
					m_MultX += -0.25f;
			}
			m_MultZ += -1.0f;
		}
		if (area & 0x04)
		{
			if (!(area & 0x08))
			{
				if (loc.x > m_TraceBox.maxX + Trace_Distance + Trace_Fudge)
					m_MultX += -0.25f;
				else if (loc.x < m_TraceBox.maxX + Trace_Distance - Trace_Fudge)
					m_MultX += 0.25f;
			}
			m_MultZ += 1.0f;
		}
	}
	else
	{
		if (area & 0x01)
		{
			if (!(area & 0x02))
			{
				if (loc.z < m_TraceBox.minZ - Trace_Distance - Trace_Fudge)
					m_MultZ += 0.25f;
				else if (loc.z > m_TraceBox.minZ - Trace_Distance + Trace_Fudge)
					m_MultZ += -0.25f;
			}
			m_MultX += -1.0f;
		}
		if (area & 0x08)
		{
			if (!(area & 0x04))
			{
				if (loc.z > m_TraceBox.maxZ + Trace_Distance + Trace_Fudge)
					m_MultZ += -0.25f;
				else if (loc.z < m_TraceBox.maxZ + Trace_Distance - Trace_Fudge)
					m_MultZ += 0.25f;
			}
			m_MultX += 1.0f;
		}

		if (area & 0x02)
		{
			if (!(area & 0x08))
			{
				if (loc.x < m_TraceBox.minX - Trace_Distance - Trace_Fudge)
					m_MultX += 0.25f;
				else if (loc.x > m_TraceBox.minX - Trace_Distance + Trace_Fudge)
					m_MultX += -0.25f;
			}
			m_MultZ += 1.0f;
		}
		if (area & 0x04)
		{
			if (!(area & 0x01))
			{
				if (loc.x > m_TraceBox.maxX + Trace_Distance + Trace_Fudge)
					m_MultX += -0.25f;
				else if (loc.x < m_TraceBox.maxX + Trace_Distance - Trace_Fudge)
					m_MultX += 0.25f;
			}
			m_MultZ += -1.0f;
		}
	}

	if (IsAnimal() == true)
	{
		m_MoveType = ANIMAL;
	}
	else
	{
		m_MoveType = WALK;
	}

	m_SpeedX = m_MultX * memory[SPEED_INDEX];
	m_SpeedZ = m_MultZ * memory[SPEED_INDEX];
}

void NonComAI::DoFlock (Time till)
{
	if (Target() == 0)
	{
		return;
	}

	if ((m_NextFlockUpdateTime != 0) &&
		((Scalar)gos_GetElapsedTime() < m_NextFlockUpdateTime))
	{
		return;
	}

	m_FlockAimDelta.x += (0.5f - Stuff::Random::GetFraction()) * (Scalar)till;
	m_FlockAimDelta.z += (0.5f - Stuff::Random::GetFraction()) * (Scalar)till;

	const Scalar radius = memory[RADIUS_INDEX];
	Max_Clamp(m_FlockAimDelta.x,radius);
	Min_Clamp(m_FlockAimDelta.x,-radius);
	Max_Clamp(m_FlockAimDelta.z,radius);
	Min_Clamp(m_FlockAimDelta.z,-radius);

	if (IsAnimal() == true)
	{
		m_MoveType = ANIMAL;
	}
	else
	{
		m_MoveType = WALK;
	}

	Point3D loc(getEntity()->GetLocalToWorld ());
	Point3D head_point(Target()->GetLocalToWorld());
	head_point += m_FlockAimDelta;

	const Scalar speed = memory[SPEED_INDEX] * 0.9f;

	if (head_point.x < loc.x)
	{
		m_SpeedX = -speed;
	}
	else
	{
		m_SpeedX = speed;
	}

	if (head_point.z < loc.z)
	{
		m_SpeedZ = -speed;
	}
	else
	{
		m_SpeedZ = speed;
	}

	m_NextFlockUpdateTime = (Scalar)gos_GetElapsedTime() + min_flock_update_time + Stuff::Random::GetFraction();
}

void NonComAI::AddFlockUnit (Entity *newunit)
{
	stlport::vector<Entity *>::iterator iter;
	
	Check_Object (newunit);
	for (iter = m_FlockList.begin ();iter != m_FlockList.end ();iter++)
	{
		if ((*iter) == newunit)
			 return;
	}
	m_FlockList.push_back (newunit);

	MWMap *map;
	Check_Object (Map::GetInstance());
	map = Cast_Object (MWMap *,Map::GetInstance());
	Check_Object (map);
	AI *ai;
	ChainIteratorOf<AI*> ais2(&map->m_executingAIs);
	while ((ai = ais2.ReadAndNext()) != NULL)
	{
		Check_Object(ai);
		if (ai->IsDerivedFrom (NonComAI::DefaultData))
		{
			NonComAI *noncom;
			noncom = Cast_Object (NonComAI *,ai);
			if (noncom == this)
				continue;
			if ((noncom->memory[TYPE_INDEX] == FLOCK_TYPE) &&
				(noncom->Target() == Target()))
			{
				{
					noncom->AddFlockUnit (m_NonCom);
				}
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::DebugTextMessageHandler(Adept::ReceiverDataMessageOf<int> *message)
{
	Check_Object(this);
	Check_Object(message);
	Verify(message->messageID == DebugTextMessageID);
}

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

void NonComAI::Info (void (*printCallback)(char* s))
{
	Check_Object (this);
	inherited::Info (printCallback);
}

bool NonComAI::IsAnimal()
{
	return (memory[BITMAP_LAYOUT_TYPE] == 1);
}
