#include "MW4Headers.hpp"

#include "Flag.hpp"
#include "ai.hpp"
#include "Mech.hpp"
#include "MWApplication.hpp"
#include "bucket.hpp"
#include "MWMission.hpp"
#include "aiutils.hpp"
#include "rail_move.hpp"
#include "torso.hpp"
#include <Adept\DropZone.hpp>
#include <Adept\EntityClassData.hpp>
#include <Adept\Site.hpp>
#include <Adept\CollisionGrid.hpp>
#include <Adept\Map.hpp>
#include "team.hpp"
#include <ElementRenderer\Element.hpp>
#include <Adept\Renderer.hpp>
#include <MissionLang\Resource.h>
#include "nglog_mw4.hpp"

// jcem - start
#include "windows.h"
#include "ctcls.h"
#include "ctcl.h"
#include "recscore.h" // jcem
// jcem - end

//#############################################################################
//###############################    Flag    ##############################
//#############################################################################

Flag::ClassData* Flag::DefaultData = NULL;

const Stuff::Scalar FLAG_DISTANCE = 25.0f;
const Stuff::Scalar FLAG_DROP_DISTANCE = 80.0f;
const Stuff::Scalar FLAG_MAX_IDLE_TIME = 30.0f;
const Stuff::Scalar flag_hold_increment_time = 1.0f;
const Stuff::Scalar flag_drop_behind_distance = 10.0f;

//________________________________________________________________________________________________________________

void Flag::InitializeClass()
{
	Verify(!DefaultData);

	DefaultData =
		new ClassData(
			 FlagClassID,
			 "MechWarrior4::Flag",
			BaseClass::DefaultData,
			0, NULL,
							 (BaseClass::Factory)Make,
							 (BaseClass::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
							 ExecutionStateEngine::DefaultData,
							 (BaseClass::GameModel::Factory)GameModel::ConstructGameModel,
							 (BaseClass::GameModel::Factory)GameModel::ConstructOBBStream,
							 (BaseClass::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
							 (BaseClass::GameModel::ModelWrite)GameModel::WriteToText,
							 (BaseClass::GameModel::ModelSave)GameModel::SaveGameModel,
							 AnimationStateEngine::Make
		);
	Register_Object(DefaultData);
}

//________________________________________________________________________________________________________________

void Flag::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//________________________________________________________________________________________________________________

Flag* Flag::Make(CreateMessage *message,ReplicatorID *base_id)
{
	Check_Object(message);
	Flag *new_entity =
	new Flag(DefaultData, message, base_id, NULL);
	Check_Object(new_entity);
	return new_entity;
}

//________________________________________________________________________________________________________________

Flag::Flag(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element)
	: NavPoint(class_data, message, base_id, element)
	, m_AttachedTo (NULL)
	, m_Team(MWApplication::No_Team)
	, m_Origin(LinearMatrix4D::Identity)
	, m_DeactiveTime(0)
	, m_WaitAfterCaptureTime(0)
	, m_AttachRadius(FLAG_DISTANCE)
	, m_FirstFrame(true)
	, m_HoldTimeLastUpdate(0)
	, m_Visible(true)
	, m_LastFlagEvent(FLAG_NONE)
{
	Check_Pointer(this);
	Check_Object(message);

	executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);

	int team(MWApplication::NameToTeamNumber(instanceName));

	if (team != MWApplication::No_Team)
	{
		SetTeam(team);
	}

	m_UseDropNames = UseDropNames();
	m_LastAnimateTime = -10.0f;
	m_DisplayIncValue = -1; // starts at -1 forcing a -1 value to become 0 for visualrepresentation
	SetToDisplay();
}

//________________________________________________________________________________________________________________

Flag::~Flag()
{
}

//________________________________________________________________________________________________________________

void Flag::RevealFlag()
{
	Check_Object(this);

	if (m_Visible == false)
	{
		SetVisibleFlag();

		SetNewLocalToParent(m_Origin);
		SyncMatrices(true);
		Map::GetInstance()->UpdateZone(this);
		m_Visible = true;
//		executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);

		MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
		if (mwmission->GetFlagsAsNavPoints() == true)
		{
			AddToNavList();
		}
		else
		{
			RemoveFromNavList();
		}
	}
}

void Flag::HideFlag()
{
	m_HoldTimeLastUpdate = 0;

	if (m_Visible == true)
	{
		ClearVisibleFlag();

		Stuff::LinearMatrix4D m(GetLocalToWorld());
		m.BuildTranslation(Stuff::Point3D::HellPoint);
		SetNewLocalToParent(m);
		SyncMatrices(true);

		Map::GetInstance()->UpdateZone(this);
		m_Visible = false;
//		executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
		RemoveFromNavList();
		IgnorePostCollision();
	}
}

//________________________________________________________________________________________________________________

void Flag::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

void Flag::Attach (Adept::Entity *who)
{
	if (Attached() == who)
		return;

	if (who == 0)
	{
		m_HoldTimeLastUpdate = 0;
	}

	m_AttachedTo.Remove ();
	if ((who) &&
		(m_Visible == true))
	{
		Check_Object (who);
		m_StartAttachTime = gos_GetElapsedTime ();
		m_AttachedTo.Add (who);
		
		MWObject *obj;
		obj = Cast_Object (MWObject *, who);
		m_AttachSite = obj->FindSite("site_flag");
		if (!m_AttachSite)
			m_AttachSite = obj->FindSite ("site_eject");
		if (!m_AttachSite)
		{
			Attach (NULL);
		}
	}
}

//________________________________________________________________________________________________________________

Stuff::Time Flag::TimeAttached()
{
	if (Attached())
	{
		return gos_GetElapsedTime () - m_StartAttachTime;
	}

	return 0;
}

//________________________________________________________________________________________________________________

Adept::Entity* Flag::Attached()
{
	return m_AttachedTo.GetCurrent();
}

//________________________________________________________________________________________________________________

void Flag::PreCollisionExecute(Stuff::Time till)
{
	PRECOLLISION_LOGIC("Flag");

	if (m_FirstFrame == true)
	{
		BaseClass::PreCollisionExecute (till);
		m_Origin = GetLocalToWorld();
		m_FirstFrame = false;
		return;
	}

	if (Enabled() == false)
	{
		Attach(NULL);
		HideFlag();
		return;
	}

	if (m_WaitAfterCaptureTime != 0)
	{
		UpdateWaitAfterCapture();
	}
	else
	{
		RevealFlag();
		UsePostCollision ();

		if (Network::GetInstance()->AmIServer())
		{
			if (Attached() == 0)
			{
				UpdateUnattached();
			}
			else
			{
				UpdateAttached();
			}
		}
	}

	if (m_Visible == true)
	{
		if (gos_GetElapsedTime () - m_LastAnimateTime > (1.0f/10.0f)) // six animations per second
		{
			m_LastAnimateTime = (float)gos_GetElapsedTime ();

			visualRepresentation += m_DisplayIncValue;
			if (visualRepresentation <= FirstFlagAnimationState)
			{
				visualRepresentation = FirstFlagAnimationState;
				m_DisplayIncValue = 1;
			}
			else if (visualRepresentation >= SixthFlagAnimationState)
			{
				visualRepresentation = SixthFlagAnimationState;
				m_DisplayIncValue = -1;
			}
			ExecuteComponentWebs();
		}
		BaseClass::PreCollisionExecute (till);
	}
}

//________________________________________________________________________________________________________________

void Flag::UpdateUnattached()
{
	if ((m_DeactiveTime != 0) &&
		(GetFlagDropReturnTime() != -1) &&
		(gos_GetElapsedTime() > m_DeactiveTime))
	{
		// ngLog addition
#if !defined(NO_LOG)
		if(m_Team != MWApplication::No_Team) {
			MW4log_generic(MW4P_DSTATS, va("Flag_Return\t-1\t%d", m_Team));
		}
#endif // !defined(NO_LOG)
		Execute_ReturnToOrigin();
		return;
	}

	LookForSomeoneToAttachTo();
}


//________________________________________________________________________________________________________________

void Flag::LookForSomeoneToAttachTo()
{
	NameTable *table = NameTable::GetInstance();

// For Bots?
// MSL 5.05 Team Return Flag
	int size(table->nameTableArray[NameTable::VehicleArray].GetLength());

	{
		for (int i = 0; i < size; ++i)
		{
			NameTableEntry *data;

			data = &table->nameTableArray [NameTable::VehicleArray][i];
			Adept::Entity* ent = data->dataPointer->GetCurrent ();

			if ((ent != 0) ||
				(EntityCanCarryMe(*ent) == true))
			{
				if (IsWithin(ent,m_AttachRadius,true) == true)
				{
					if (EntityCanCarryMe(*ent) == true)
					{
						Execute_AttachTo(ent);
						return;
					}
				}
			}
		}
	}

	size = table->nameTableArray[NameTable::VehicleArray].GetLength();

	{
		for (int i = 0; i < size; ++i)
		{
			NameTableEntry *data;

			data = &table->nameTableArray [NameTable::VehicleArray][i];
			Adept::Entity* ent = data->dataPointer->GetCurrent ();

			if ((ent != 0) ||
				(EntityCanReturnMe(*ent) == true))
			{
				if (IsWithin(ent,m_AttachRadius,true) == true)
				{
					if (EntityCanReturnMe(*ent) == true)
					{
						SetNewLocalToParent(m_Origin);
						m_DeactiveTime = 0;
						return;
					}
				}
			}
		}
	}
}


/*
// Original Flag Code
//________________________________________________________________________________________________________________

void Flag::LookForSomeoneToAttachTo()
{
	NameTable *table = NameTable::GetInstance();

	int size(table->nameTableArray[NameTable::VehicleArray].GetLength());

	{for (int i = 0;
		  i < size;
		  ++i)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::VehicleArray][i];
		Adept::Entity* ent = data->dataPointer->GetCurrent ();

		if ((ent == 0) ||
			(EntityCanCarryMe(*ent) == false))
		{
			continue;
		}

		if (IsWithin(ent,m_AttachRadius,true) == true)
		{
			Execute_AttachTo(ent);
			return;
		}
	}}

	size = table->nameTableArray[NameTable::PlayerArray].GetLength();
	{for (int i = 0;
		  i < size;
		  ++i)
	{
		NameTableEntry *data;

		data = &table->nameTableArray [NameTable::PlayerArray][i];
		Adept::Entity* ent = data->dataPointer->GetCurrent ();

		if ((ent != 0) &&
			(ent->IsDerivedFrom(Adept::Player::DefaultData) == true))
		{
			Adept::Player* player = Cast_Object(Adept::Player*,ent);

			if (player->vehicle != 0)
			{
				if (EntityCanCarryMe(*(player->vehicle)) == false)
				{
					continue;
				}

				if (IsWithin(player->vehicle,m_AttachRadius,true) == true)
				{
					Execute_AttachTo(player->vehicle);
					return;
				}
			}
		}
	}}

}
*/
//________________________________________________________________________________________________________________

void Flag::UpdateAttached()
{
	Verify(Attached() != 0);

	if (CarrierCanStillCarryMe() == false)
	{
		Execute_Drop();
	}
	else
	{
		if (CarrierCanCaptureMe() == true)
		{
			Execute_Capture();
		}
		else
		{
			if (m_HoldTimeLastUpdate == 0)
			{
				m_HoldTimeLastUpdate = (Stuff::Scalar)gos_GetElapsedTime();
			}

			if (m_HoldTimeLastUpdate + flag_hold_increment_time < (Stuff::Scalar)gos_GetElapsedTime())
			{
				m_HoldTimeLastUpdate += flag_hold_increment_time;
				IncrementScore(CBucketManager::FLAG_HOLD_TIME,Attached()->GetReplicatorID());
			}
		}
	}
}

//________________________________________________________________________________________________________________

void Flag::UpdateWaitAfterCapture()
{
	Verify(m_WaitAfterCaptureTime != 0);

	if (m_WaitAfterCaptureTime < 0)
	{
		HideFlag();
		return;
	}

	if (gos_GetElapsedTime() > m_WaitAfterCaptureTime)
	{
		m_WaitAfterCaptureTime = 0;
		RevealFlag();
	}
}

//________________________________________________________________________________________________________________

void Flag::PostCollisionExecute(Stuff::Time till)
{
	POSTCOLLISION_LOGIC("Flag");

	if (Attached() == 0)
	{
		UpdateFlagObjectOnGround();
	}
	else
	{
		if (Attached()->IsDestroyed ())
		{
			Execute_Drop();
		}
		else
		{
			UpdateFlagObjectAttached();
		}
	}

	SyncMatrices(true);
	BaseClass::PostCollisionExecute (till);
}

//________________________________________________________________________________________________________________

void Flag::UpdateFlagObjectOnGround()
{
	Verify(Attached() == 0);

	LinearMatrix4D mat(GetLocalToWorld ());
	Stuff::Line3D line;
	line.m_length = 2000.0f;
	line.m_direction = Vector3D::Down;
	line.m_origin = mat;
	line.m_origin.y += 1000.0f;
	Stuff::Normal3D normal;
	CollisionQuery query(&line, &normal, CanBeWalkedOnFlag, this);
	Check_Object(CollisionGrid::Instance);

	CollisionGrid::Instance->ProjectLine(&query);		// there had to be a unit here so it must hit something
	Point3D tr;
	line.FindEnd(&tr);
	Point3D loc (GetLocalToWorld ());
	loc.y = tr.y;
	YawPitchRoll new_rotation(YawPitchRoll::Identity);
	new_rotation = (YawPitchRoll) GetLocalToWorld ();

	mat.BuildTranslation(loc);
	mat.AlignLocalAxisToWorldVector (Vector3D::Up,Y_Axis,X_Axis,Z_Axis);

	SetNewLocalToParent(mat);
}

//________________________________________________________________________________________________________________

void Flag::UpdateFlagObjectAttached()
{
	Verify(Attached() != 0);
	Check_Object(m_AttachSite);

	LinearMatrix4D mat;

	if (Attached()->IsDerivedFrom(MWObject::DefaultData) == true)
	{
		MWObject* mwobject = Cast_Object(MWObject*,Attached());

		if (mwobject->GetTorso() != 0)
		{
			mat = mwobject->GetTorso()->GetTwistJointMatrix();
		}
	}
	else
	{
		mat = Attached()->GetLocalToWorld();
	}

	UnitVector3D dir;
	mat.GetLocalForwardInWorld(&dir);

	EulerAngles ea(mat);
	mat.BuildRotation(ea);

	if (m_AttachSite != 0)
	{
		mat.BuildTranslation((Stuff::Point3D)m_AttachSite->GetLocalToWorld());
	}
	else
	{
														// put the flag in front and to the right of us
		Point3D forward(dir);
		forward *= 6.0f;
		UnitVector3D unit_right;
		Attached()->GetLocalToWorld().GetLocalRightInWorld(&unit_right);
		Point3D right(unit_right);
		right *= (4.0f + (Stuff::Scalar)GetMyIndex());
		forward += right;
		forward += (Point3D)mat;

		mat.BuildTranslation(forward);
	}

	SetNewLocalToParent(mat);
}

//________________________________________________________________________________________________________________

int Flag::GetTeam() const
{
	return (m_Team);
}

//________________________________________________________________________________________________________________

void Flag::SetTeam(int team)
{
	Verify(team >= 0);
	Verify(team <= 7);
	m_Team = team;
}

//________________________________________________________________________________________________________________

	inline std::string TeamNumToStr(int team_num)
	{
		char buf[100];
// MSL 5.02 Flag Team Name
		if (team_num == 0)
			sprintf(buf,"Blue");
		if (team_num == 1)
			sprintf(buf,"Red");
		if (team_num == 2)
			sprintf(buf,"Yellow");
		if (team_num == 3)
			sprintf(buf,"Green");
//		sprintf(buf,"%d",team_num + 1);
		return (buf);
	}

//________________________________________________________________________________________________________________

void Flag::Notify(const std::string& s)
{
	MWApplication::GetInstance()->SendChat(
			Connection::Local->GetID(),
			MWApplication::SystemBroadcast,
			0,
			(char*)s.c_str());
}

	void AddNameAndOrTeam(std::string& s, int team_number, const char* string_name)
	{
		if (string_name != 0)
		{
			s += string_name;
			if (team_number != MWApplication::No_Team)
			{
				s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM1);//" (Team "
				s += TeamNumToStr(team_number);
				s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM2);//")"

			}
		}
		else
		{
			if (team_number != MWApplication::No_Team)
			{
				s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM3);//"Team "
				s += TeamNumToStr(team_number);
			}
		}
	}

//________________________________________________________________________________________________________________

void Flag::NotifyFlagTaken(int flag_team, int taker_team, const char* taker_name)
{
	// TODO: notify the team that their flag was picked up
	std::string s;

	if (flag_team == MWApplication::No_Team)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_FLAG_TAKEN_BY);//"Flag Taken By ";
	}
	else
	{
// MSL 5.02 Flag Notification Change
//		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM3);//"Team ";
		s += TeamNumToStr(flag_team);
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_S_FLAG_TAKEN_BY);//" Flag Taken By ";
	}

	AddNameAndOrTeam(s,taker_team,taker_name);

	Notify(s);
}

//________________________________________________________________________________________________________________

void Flag::NotifyFlagDropped(int flag_team, int dropper_team, const char* dropper_name)
{
	// TODO: notify the team that their flag was dropped by its carrier
	std::string s;

	if (flag_team == MWApplication::No_Team)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_FLAG_DROPPED_BY);//"Flag Dropped By ";
	}
	else
	{
// MSL 5.02 Flag Notification Change
//		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM3);//"Team ";
		s += TeamNumToStr(flag_team);
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_S_FLAG_DROPPED_BY);//" Flag Dropped By ";
	}

	AddNameAndOrTeam(s,dropper_team,dropper_name);

	Notify(s);
}

//________________________________________________________________________________________________________________

void Flag::NotifyFlagCaptured(int flag_team, int capturer_team, const char* capturer_name)
{
	// TODO: notify the team that their flag was taken to another team's drop zone
	std::string s;

	if (flag_team == MWApplication::No_Team)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_FLAG_CAPTURED_BY);//"Flag Captured By ";
	}
	else
	{
// MSL 5.02 Flag Notification Change
//		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM3);//"Team ");
		s += TeamNumToStr(flag_team);
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_S_FLAG_CAPTURED_BY);//" Flag Captured By ";
	}

	AddNameAndOrTeam(s,capturer_team,capturer_name);

	Notify(s);
}

//________________________________________________________________________________________________________________

void Flag::NotifyFlagReturned(int flag_team)
{
	// TODO: notify the team that their flag was returned to their base due to time-out
	std::string s;
// MSL 5.02 Flag Notification Change
//	std::string s(Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM3));//"Team ");
	s += TeamNumToStr(flag_team);
	s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_S_FLAG_RETURNED_TO_BASE);//" Flag Returned To Base";

	Notify(s);
}

//________________________________________________________________________________________________________________

bool Flag::UseDropNames()
{
	{for (int i = 0;
		  i < NameTable::GetInstance()->GetLength(NameTable::DropZoneArray);
		  ++i)
	{
		NameTableEntry* table_entry = NameTable::GetInstance()->FindEntry(NameTable::DropZoneArray,i);

		if (table_entry != 0)
		{
			int team_number(FlagDropNameToTeamNumber(table_entry->objectName));

			if (team_number != MWApplication::No_Team)
			{
				return (true);
			}
		}
	}}

	return (false);
}

//________________________________________________________________________________________________________________

bool Flag::CanDropTeamFlagHere(int team)
{
	{
		for (int i = 0;
			i < NameTable::GetInstance()->GetLength(NameTable::DropZoneArray);
			++i)
		{
			NameTableEntry* table_entry = NameTable::GetInstance()->FindEntry(NameTable::DropZoneArray,i);

			if ((table_entry != 0) &&
				(table_entry->dataPointer != 0) &&
				(table_entry->dataPointer->GetCurrent() != 0)&&
				(table_entry->dataPointer->GetCurrent()->IsDerivedFrom(DropZone::DefaultData) == true))
			{
				DropZone* drop_zone = Cast_Object(DropZone*,table_entry->dataPointer->GetCurrent());

				int team_number;
			
				if (m_UseDropNames == true)
				{
					team_number = FlagDropNameToTeamNumber(drop_zone->instanceName);
				}
				else
				{
					team_number = MWApplication::NameToTeamNumber(drop_zone->instanceName);
				}
// MSL 5.06 Edit for other flag being carried here!!
				if (team_number == team)
				{
					return (drop_zone->IsWithin(this,FLAG_DROP_DISTANCE,true));
				}
			}
		}
	}

	return (false);
}

//________________________________________________________________________________________________________________

void Flag::GetFlagsCarriedBy(Adept::Entity& entity, std::vector<Flag*>& flags)
{
	int num_flags = NameTable::GetInstance()->GetLength(NameTable::FlagArray);

	{for (int i = 0;
		  i < num_flags;
		  ++i)
	{
		NameTableEntry* entry = NameTable::GetInstance()->FindEntry(NameTable::FlagArray,i);

		if ((entry != 0) &&
			(entry->dataPointer != 0) &&
			(entry->dataPointer->GetCurrent() != 0) &&
			(entry->dataPointer->GetCurrent()->IsDerivedFrom(Flag::DefaultData) == true))
		{
			Flag* flag = Cast_Object(Flag*,entry->dataPointer->GetCurrent());

			if (flag->Attached() == &entity)
			{
				flags.push_back(flag);
			}
		}
	}}
}

//________________________________________________________________________________________________________________

int Flag::NumFlagsCarriedBy(Adept::Entity& entity)
{
	std::vector<Flag*> flags;
	GetFlagsCarriedBy(entity, flags);

	return (flags.size());
}

//________________________________________________________________________________________________________________

bool Flag::EntityCanCarryMe(Adept::Entity& entity)
{
	if (Attached() != 0)
	{
		return (false);
	}

	if ((entity.IsDestroyed() == true) ||
		(entity.IsDerivedFrom(Mech::DefaultData) == false))
	{
		return (false);
	}

	Mech* mech = Cast_Object(Mech*,&entity);

	if (GetTeam() != MWApplication::No_Team)
	{
		if (mech->GetTeam() == GetTeam())
		{
			return (false);
		}
	}

	if (mech->IsShutdown() == true)
	{
		return (false);
	}

	int max_flags = mech->GetMaxFlagsCarried();

	if (max_flags < 1)
	{
		return (false);
	}

	if (NumFlagsCarriedBy(entity) >= max_flags)
	{
		return (false);
	}

	const Stuff::Point3D my_pos(GetLocalToWorld());
	if (GetSquaredDistToMatrixForward(my_pos,mech->GetLocalToWorld()) >
		GetSquaredDistToMatrixBackward(my_pos,mech->GetLocalToWorld()))
	{
		return (false);
	}

	return (true);
}

//________________________________________________________________________________________________________________

bool Flag::EntityCanReturnMe(Adept::Entity& entity)
{
	if (Attached() != 0)
	{
		return (false);
	}

	if ((entity.IsDestroyed() == true) ||
		(entity.IsDerivedFrom(Mech::DefaultData) == false))
	{
		return (false);
	}

	Mech* mech = Cast_Object(Mech*,&entity);

	if (GetTeam() != MWApplication::No_Team)
	{
		if (mech->GetTeam() == GetTeam())
		{
			return (true);
		}
	}

	if (mech->IsShutdown() == true)
	{
		return (false);
	}

	int max_flags = mech->GetMaxFlagsCarried();

	if (max_flags < 1)
	{
		return (false);
	}

	if (NumFlagsCarriedBy(entity) >= max_flags)
	{
		return (false);
	}

	const Stuff::Point3D my_pos(GetLocalToWorld());
	if (GetSquaredDistToMatrixForward(my_pos,mech->GetLocalToWorld()) >
		GetSquaredDistToMatrixBackward(my_pos,mech->GetLocalToWorld()))
	{
		return (false);
	}

	return (false);
}

//________________________________________________________________________________________________________________

void Flag::Execute_ReturnToOrigin()
{
	SetNewLocalToParent(m_Origin);

	Broadcast_FlagReturned();

	if (m_Team != MWApplication::No_Team)
	{
		NotifyFlagReturned(m_Team);
	}

	m_DeactiveTime = 0;
}

//________________________________________________________________________________________________________________

void Flag::Execute_AttachTo(Adept::Entity* entity)
{
	Verify(Attached() == 0);
	Verify(entity != 0);

	IncrementScore(CBucketManager::FLAGS_TAKEN,entity->GetReplicatorID());
#if !defined(NO_LOG)
	MW4log_generic(MW4P_DSTATS, va("Flag_Taken\t%d", entity->GetReplicatorID().connectionID));
#endif // !defined(NO_LOG)

	m_HoldTimeLastUpdate = (Stuff::Scalar)gos_GetElapsedTime();

	Attach(entity);

	if (Attached() != 0)
	{
		Vehicle* vehicle = Cast_Object(Vehicle*,entity);
		NotifyFlagTaken(m_Team,vehicle->GetTeam(),vehicle->instanceName);

		Broadcast_FlagTaken(*vehicle);
	}
}

//________________________________________________________________________________________________________________

void Flag::Execute_Drop()
{
	Verify(Attached() != 0);

	m_HoldTimeLastUpdate = 0;

	Stuff::LinearMatrix4D matrix(Attached()->GetLocalToWorld());

	Stuff::Scalar desired_height = ((Stuff::Point3D)matrix).y;

	Stuff::UnitVector3D unit_backwards;
	matrix.GetLocalBackwardInWorld(&unit_backwards);

	Stuff::Point3D backwards(unit_backwards);
	backwards *= flag_drop_behind_distance;

	backwards += (Stuff::Point3D)matrix;

	FindGoodDropSpot(backwards,desired_height);

	matrix.BuildTranslation(backwards);
	SetNewLocalToParent(matrix);
	SyncMatrices(true);
	Map::GetInstance()->UpdateZone(this);

	IncrementScore(CBucketManager::FLAGS_DROPPED,Attached()->GetReplicatorID());
#if !defined(NO_LOG)
    MW4log_generic(MW4P_DSTATS, va("Flag_Drop\t%d", Attached()->GetReplicatorID().connectionID));
#endif // !defined(NO_LOG)

	Vehicle* vehicle = Cast_Object(Vehicle*,Attached());
	Check_Object(vehicle);
	NotifyFlagDropped(m_Team,vehicle->GetTeam(),vehicle->instanceName);

	Attach (NULL);

	switch (GetFlagDropReturnTime())
	{
		case 0:
		{
			// ngLog addition
#if !defined(NO_LOG)
			if(m_Team != MWApplication::No_Team) {
				MW4log_generic(MW4P_DSTATS, va("Flag_Return\t-1\t%d", m_Team));
			}
#endif // !defined(NO_LOG)
			Execute_ReturnToOrigin();
			break;
		}

		case -1:
		{
			return;
		}

		default:
		{
			m_DeactiveTime = gos_GetElapsedTime() + GetFlagDropReturnTime();
		}
	}
}

//________________________________________________________________________________________________________________

bool Flag::CarrierCanStillCarryMe()
{
	Verify(Attached() != 0);

	if ((Attached()->IsDestroyed() == true) ||
		(Attached()->IsDerivedFrom(Mech::DefaultData) == false))
	{
		return (false);
	}

	if (GetTeam() != MWApplication::No_Team)
	{
		Mech* mech = Cast_Object(Mech*,Attached());
		if (mech->GetTeam() == GetTeam())
		{
			return (false);
		}
	}

	Mech* mech = Cast_Object(Mech*,Attached());

	if (mech->IsShutdown() == true)
	{
		return (false);
	}

	return (true);
}

//________________________________________________________________________________________________________________

bool Flag::CarrierCanCaptureMe()
{
	Verify(Attached() != 0);
	Verify(Attached()->IsDerivedFrom(Mech::DefaultData) == true);

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	if (mwmission->GetFlagCaptureEnabled() == false)
	{
		return (false);
	}

	Mech* mech = Cast_Object(Mech*,Attached());

	if (GetTeam() == MWApplication::No_Team)
	{
		return (CanDropTeamFlagHere(mech->GetTeam()));
	}
	else
	{
		if (mech->GetTeam() != GetTeam())
		{
			return (CanDropTeamFlagHere(mech->GetTeam()));
		}
	}

	return (false);
}

//________________________________________________________________________________________________________________

void Flag::Execute_Capture()
{
	Verify(Attached() != 0);

	IncrementScore(CBucketManager::FLAGS_CAPTURED,Attached()->GetReplicatorID());
    g_RSF.RecScore(CBucketManager::FLAGS_CAPTURED,Attached()->GetReplicatorID()); // jcem
#if !defined(NO_LOG)
	MW4log_generic(MW4P_DSTATS, va("Flag_Capture\t%d", Attached()->GetReplicatorID().connectionID));
#endif // !defined(NO_LOG)

	if (Attached()->IsDerivedFrom(Mech::DefaultData) == true)
	{
		Mech* mech = Cast_Object(Mech*,Attached());
		NotifyFlagCaptured(m_Team,mech->GetTeam(),mech->instanceName);
	}

	m_HoldTimeLastUpdate = 0;

	Broadcast_FlagCaptured();

	Attach(NULL);

	switch (GetFlagCaptureReturnTime())
	{
		case 0:
		{
			SetNewLocalToParent(m_Origin);
			break;
		}

		case -1:
		{
			HideFlag();
			m_WaitAfterCaptureTime = -1;
			break;
		}

		default:
		{
			HideFlag();
			m_WaitAfterCaptureTime = gos_GetElapsedTime() + GetFlagCaptureReturnTime();
			break;
		}
	}
}

//________________________________________________________________________________________________________________

int Flag::FlagDropNameToTeamNumber(char* name) const
{
	if (name == 0)
	{
		return (MWApplication::No_Team);
	}

	char buf[200];
	strcpy(buf,name);
	std::string s(_strlwr(buf));
	std::string::size_type pos(s.find("teamcapture"));

	if (pos != std::string::npos)
	{
		pos += 11;
		if (pos < s.size())
		{
			s.erase(0,pos);

			if (s.size() > 1)
			{
				s.erase(1,s.size() - 1);
			}
			char* stopstring;
			long team_number(strtol(s.c_str(),&stopstring,10));

			if (team_number == 0)
			{
				team_number = MWApplication::No_Team;
			}
			else
			{
				--team_number;
			}

			if ((team_number >= 0) &&
				(team_number <= 7))
			{
				return (team_number);
			}
		}
	}

	return (MWApplication::No_Team);
}

//________________________________________________________________________________________________________________

int Flag::GetFlagDropReturnTime()
{
	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	return (mwmission->GetFlagDropReturnTime());
}

//________________________________________________________________________________________________________________

int Flag::GetFlagCaptureReturnTime()
{
	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	return (mwmission->GetFlagCaptureReturnTime());
}

//________________________________________________________________________________________________________________

int Flag::GetMyIndex()
{
	Verify(Attached() != 0);

	std::vector<Flag*> flags;
	GetFlagsCarriedBy(*(Attached()),flags);

	{for (int i = 0;
		  i < flags.size();
		  ++i)
	{
		if (flags[i] == this)
		{
			return (i);
		}
	}}

	Verify(!"Should never get here.");
	return (0);
}

//________________________________________________________________________________________________________________

void Flag::IncrementScore(int bucket_type, const Adept::ReplicatorID& id)
{
	MWMission *mission = Cast_Object(MWMission*,MWMission::GetInstance());
	mission->m_BucketManager->NotifyAction((CBucketManager::Bucket_Type)bucket_type,id,1);
}

//________________________________________________________________________________________________________________

bool Flag::Enabled()
{
	if (m_Team != MWApplication::No_Team)
	{
		if ((MWApplication::GetInstance() == 0) ||
			(MWApplication::GetInstance()->TeamHasAnyPlayers(m_Team) == false))
		{
			return (false);
		}
	}

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	switch (mwmission->GetFlagsEnabled())
	{
		case MWMission::FLAGS_HIDE:
		{
			return (false);
		}

		case MWMission::FLAGS_TEAM_ONLY:
		{
			return ((m_Team >= 0) && (m_Team <= 7));
		}

		case MWMission::FLAGS_UNIVERSAL_ONLY:
		{
			return (m_Team == MWApplication::No_Team);
		}

		case MWMission::FLAGS_SHOW_ALL:
		{
			return (true);
		}
	}

	Verify(!"Enable state not found");
	return (false);
}

//________________________________________________________________________________________________________________

void Flag::Respawn(Adept::Entity::CreateMessage *message)
{
	Attach(NULL);
	HideFlag();
	SetNewLocalToParent(m_Origin);

	m_DeactiveTime = 0;
	m_WaitAfterCaptureTime = 0;
	m_HoldTimeLastUpdate = 0;

	if (message != 0)
	{
		Entity::Respawn(message);
	}
}

//________________________________________________________________________________________________________________

void Flag::RemoveAllFlagsFrom(Adept::Entity& entity)
{
	NameTable *table = NameTable::GetInstance();

	if (table == 0)
	{
		return;
	}

	int size(table->nameTableArray[NameTable::FlagArray].GetLength());

	{for (int i = 0;
		  i < size;
		  ++i)
	{
		NameTableEntry *data = &table->nameTableArray [NameTable::FlagArray][i];
		Adept::Entity* ent = data->dataPointer->GetCurrent();

		if ((ent != 0) &&
			(ent->IsDerivedFrom(Flag::DefaultData) == true))
		{
			Flag* flag = Cast_Object(Flag*,ent);

			if (flag->Attached() == &entity)
			{
				flag->Execute_Drop();
			}
		}
	}}
}

//________________________________________________________________________________________________________________

void Flag::ResetAllFlags()
{
	NameTable *table = NameTable::GetInstance();

	if (table == 0)
	{
		return;
	}

	int size(table->nameTableArray[NameTable::FlagArray].GetLength());

	{for (int i = 0;
		  i < size;
		  ++i)
	{
		NameTableEntry *data = &table->nameTableArray [NameTable::FlagArray][i];
		Adept::Entity* ent = data->dataPointer->GetCurrent();

		if ((ent != 0) &&
			(ent->IsDerivedFrom(Flag::DefaultData) == true))
		{
			Flag* flag = Cast_Object(Flag*,ent);

			if (flag->Attached() != 0)
			{
				flag->Attach(0);
			}
		}
	}}
}

//________________________________________________________________________________________________________________

bool Flag::AnyFlagsAttachedTo(Adept::Entity& entity)
{
	NameTable *table = NameTable::GetInstance();

	if (table == 0)
	{
		return (false);
	}

	int size(table->nameTableArray[NameTable::FlagArray].GetLength());

	{for (int i = 0;
		  i < size;
		  ++i)
	{
		NameTableEntry *data = &table->nameTableArray [NameTable::FlagArray][i];
		Adept::Entity* ent = data->dataPointer->GetCurrent();

		if ((ent != 0) &&
			(ent->IsDerivedFrom(Flag::DefaultData) == true))
		{
			Flag* flag = Cast_Object(Flag*,ent);

			if (flag->Attached() == &entity)
			{
				return (true);
			}
		}
	}}

	return (false);
}

//________________________________________________________________________________________________________________

	bool CouldDropAtPoint(const Stuff::Point3D& point, Stuff::Scalar desired_height, Adept::Entity* who)
	{
		Stuff::Scalar minZ, maxZ, minX, maxX;
		Map::GetInstance()->GetMapExtents(&minZ,&maxZ,&minX,&maxX);

		if ((point.x <= minX) ||
			(point.x >= maxX) ||
			(point.z <= minZ) ||
			(point.z >= maxZ))
		{
			return (false);
		}

		if (MWMission::GetInstance()->GetMissionArea(point) != MWMission::InMissionArea)
		{
			return (false);
		}

		#pragma warning (disable : 4800)
		if (MW4AI::CheckCanPass(MW4AI::g_MissionGraph->Passable(point.x,point.z),MW4AI::LEGPASS_FLAG) == true)
		{
			return (false);
		}
		#pragma warning (default : 4800)

		Verify(Adept::Map::GetInstance() != 0);

		Stuff::Line3D line;
		line.m_length = 200.0f;
		line.m_direction = Vector3D::Down;
		line.m_origin = point;
		line.m_origin.y += 50.0f;
		Stuff::Normal3D normal;
		Entity::CollisionQuery query(&line,&normal,Entity::CanBeWalkedOnFlag,who);
		Check_Object(CollisionGrid::Instance);
		if (CollisionGrid::Instance->ProjectLine(&query) != Adept::Map::GetInstance())
		{
			return (false);
		}

		Stuff::Point3D hit_spot;
		line.FindEnd(&hit_spot);

		if ((hit_spot.y < desired_height - 10) ||
			(hit_spot.y > desired_height + 10))
		{
			return (false);
		}

		return (true);
	}

void Flag::FindGoodDropSpot(Stuff::Point3D& point, Stuff::Scalar desired_height)
{
	if (CouldDropAtPoint(point,desired_height,Attached()) == true)
	{
		return;
	}

	Stuff::Scalar move_distance = 8.0f;

	{for (int i_x = 0;
		  i_x < 10;
		  ++i_x)
	{
		Stuff::Scalar x_delta = move_distance * (Stuff::Scalar)i_x;

		if (i_x & 0x01)
		{
			x_delta *= -1;
		}

		{for (int i_z = 0;
			  i_z < 10;
			  ++i_z)
		{
			Stuff::Scalar z_delta = move_distance * (Stuff::Scalar)i_z;

			if (i_z & 0x01)
			{
				z_delta *= -1;
			}

			Stuff::Point3D p(point.x + x_delta,point.y,point.z + z_delta);

			if (CouldDropAtPoint(p,desired_height,Attached()) == true)
			{
				point = p;
				return;
			}
		}}
	}}
}

int
	Flag::GetExecutionSlot()
{
	return (Entity::GetExecutionSlot());
}

void
	Flag::AddStatsToString(std::string& s)
{
	s += instanceName;

	if (GetTeam() != MWApplication::No_Team)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_TEAM4);//"Team: ";
		s += IntToString(m_Team);
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_SPACE_NEWLINE);//" \n";
	}

	if (Attached() != 0)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_ATTACHED);//"Attached: \n";
//		s += IntToString(Attached()->objectID);
//		s += "\n";
	}
	else
	{	
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_NOT_ATTACHED);//"Not attached\n";
	}

	if (m_Visible == true)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_VISIBLE);//"Visible\n";
	}
	else
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_NOT_VISIBLE);//"Not Visible\n";
	}

	if (Enabled() == true)
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_ENABLED);//"Enabled\n";
	}
	else
	{
		s += Application::GetInstance()->GetLocString(IDS_FLAGS_CPP_NOT_ENABLED);//"Not Enabled\n";
	}
}


void
	Flag::BecomeInteresting(bool render_me)
{
	Check_Object(this);
	LOAD_LOGIC("Become Interesting::Flag");

	//
	//------------------
	// Validate children
	//------------------
	//
	ChainIteratorOf<Entity*> children(&childEntityChain);
	Entity *entity;
	while ((entity = children.ReadAndNext()) != NULL)
	{
		Check_Object(entity);
		entity->BecomeInteresting(render_me);
	}

  //
	//-----------------------------
	// Now make ourself interesting
	//-----------------------------
	//
	/*
	extern int g_TeamDecal;
	if (GetTeam() != MWApplication::No_Team)
	{
		Team * team = MWApplication::GetInstance()->teams[GetTeam()]->GetCurrent();
		if (team)
			g_TeamDecal = team->GetTeamDecal();
	}

	*/
	BaseClass::BecomeInteresting(render_me);
}

// MSL 5.03 Fixed Flag_Taken (disabled)
void Flag::Broadcast_FlagTaken(Adept::Entity& entity)
{
	VehicleInterface* vi = VehicleInterface::GetInstance();

	if (vi != 0)
	{
		if (&entity == vi->vehicle)
		{
//			m_LastFlagEvent = FLAG_TAKEN_BY_ME;
// MSL		Currently only the server is playing the Taken_by_me sound.  Disabling.
			m_LastFlagEvent = FLAG_TAKEN;
		}
		else
		{
			m_LastFlagEvent = FLAG_TAKEN;
		}
	}
	else
	{
		m_LastFlagEvent = FLAG_TAKEN;
	}
	ReactToFlagEvent(m_LastFlagEvent);
}

void Flag::PlaySound_FlagTaken()
{
	if (VehicleInterface::GetInstance() != 0)
	{
		VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::FLAGTAKEN);
	}
}

// MSL 5.03 Fixed Flag_Taken
void Flag::PlaySound_FlagTakenByMe()
{
	if (VehicleInterface::GetInstance() != 0)
	{
		VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::YOUHAVEFLAG);
	}
}

void Flag::Broadcast_FlagReturned()
{
	m_LastFlagEvent = FLAG_RETURNED;
	ReactToFlagEvent(m_LastFlagEvent);
}

void Flag::PlaySound_FlagReturned()
{
	if (VehicleInterface::GetInstance() != 0)
	{
		VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::FLAGRETURNED);
	}
}

void Flag::Broadcast_FlagCaptured()
{
	m_LastFlagEvent = FLAG_CAPTURED;
	ReactToFlagEvent(m_LastFlagEvent);
}


void Flag::PlaySound_FlagCaptured()
{
	if (VehicleInterface::GetInstance() != 0)
	{
		MWMission *mission = Cast_Object (MWMission *,MWMission::GetInstance());
		Check_Object (mission);
		int theType = mission->GetMissionType();
		if (theType == MWMission::MISSIONTYPE_COLISEUM)
		{
			VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::FLAGCAPTUREDHORN);
		}
		else
		{
			VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::FLAGCAPTUREDBUZZER);
		}
	}
}

void Flag::ReactToFlagEvent(FlagEvent event)
{
	switch (event)
	{
		case FLAG_TAKEN:
		{
			Flag::PlaySound_FlagTaken();
			break;
		}

// MSL 5.03 Fixed Flag_Taken
		case FLAG_TAKEN_BY_ME:
		{
			Flag::PlaySound_FlagTakenByMe();
			break;
		}

		case FLAG_CAPTURED:
		{
			Flag::PlaySound_FlagCaptured();
			break;
		}

		case FLAG_RETURNED:
		{
			Flag::PlaySound_FlagReturned();
			break;
		}
	}
}

Flag::FlagEvent Flag::GetLastFlagEvent() const
{
	return (m_LastFlagEvent);
}

void Flag::SetLastFlagEvent(FlagEvent event)
{
	if ((event == m_LastFlagEvent) ||
		(Network::GetInstance()->AmIServer()))
	{
		return;
	}

	ReactToFlagEvent(event);

	m_LastFlagEvent = event;
}
