//===========================================================================//
// File:	Building.cpp										
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 05/06/99  DPB  Inital base class
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "Building.hpp"
#include "CombatAI.hpp"
#include "obstacle.hpp"
#include "Weapon.hpp"
#include <Adept\Site.hpp>
#include "MWMission.hpp"

#include "MWDamageObject.hpp"

//#############################################################################
//###############################    Building    ##############################
//#############################################################################

extern int g_nMR;

Building::ClassData*
	Building::DefaultData = NULL;


DWORD MechWarrior4::Executed_Building_Count = 0;
bool MechWarrior4::Building::m_BuildingsAddedToCellMap = false;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			BuildingClassID,
			"MechWarrior4::Building",
			MWObject::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			(Entity::GameModel::Factory)GameModel::ConstructOBBStream,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
			AnimationStateEngine::Make
		);
	Register_Object(DefaultData);

}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Building*
	Building::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	gos_PushCurrentHeap(Heap);
	Building *new_entity =
		new Building(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Building::Building(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	MWObject(class_data, message, base_id, element)
	,
	//the abl call markBuildingAsScorable is made in siege assault 
	//strong hold type maps.  
	//If set, when the building is destroyed points are awarded.
	//Turrets will always be scored.
	m_scoreThisBuilding (false)

{
	Check_Pointer(this);
	Check_Object(message);

	CommonCreation(message);	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::CommonCreation(CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	m_AddedToSensorCellMap = false;
}

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

void
	Building::LoadAnimationScripts()
{
	MWObject::LoadAnimationScripts();

	if (!m_AI)
	{
		const GameModel *model = GetGameModel();
		if(model->animScriptName[0] != NULL)
		{
			executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
			animStateEngine->RequestState(AnimationStateEngine::Test1State);
		}
	//	if(!executingSubsystems.IsEmpty())
	//		executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::Respawn(Entity::CreateMessage *message)
{
	Check_Object(this);
	Check_Object(message);

	MWObject::Respawn(message);
	CreateMessage *building_message = Cast_Pointer(CreateMessage *, message);
	CommonCreation(building_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Building::~Building()
{
	DESTRUCTOR("Building");
}

void Building::TurnOn (void)
{
	lastParameterization = gos_GetElapsedTime ();
	const GameModel *model = GetGameModel();
	if(model->animScriptName[0] != NULL)
	{
		executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
		animStateEngine->RequestState(AnimationStateEngine::Test1State);
	}
	if(!executingSubsystems.IsEmpty())
		executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
}

void Building::TurnOff (void)
{
	executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::Reuse(
		const CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(this);
	STOP(("Not implemented"));
	MWObject::Reuse(message, base_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	Building::GetExecutionSlot()
{
	Check_Object(this);
	return BuildingExecutionSlot;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::PreCollisionExecute(Stuff::Time till)
{
	Check_Object(this);
	Check_Object(animStateEngine);
	PRECOLLISION_LOGIC("Building");

	Scalar time_slice = GetTimeSlice(till);
	Verify(time_slice > 0.0f);

	Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState);
	Check_Object(animStateEngine);

	if(animStateEngine->GetState() != AnimationStateEngine::UninitializedState)
	{
		animStateEngine->RunStates(time_slice);
	}

	Set_Statistic(Executed_Building_Count, Executed_Building_Count+1);

	MWObject::PreCollisionExecute(till);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(LAB_ONLY)
	void Building::SyncMatrices(bool update_matrix)
	{
		SYNC_LOGIC("Building");
		BaseClass::SyncMatrices(update_matrix);
	}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::ReactToDestruction(int damage_mode, int damage_type)
{
	Check_Object(this);


	if(!IsDestroyed())
	{
		//added for scroing objective building in strong holds and siege assault
		if (m_scoreThisBuilding)
		{	
			Check_Object (Mission::GetInstance());
			Mission::GetInstance()->ScoringReactToBuildingDeath (m_whoShotMeLast, GetReplicatorID());
		}

		if(m_AI)
		{
			Check_Object(m_AI);
			m_AI->Die();
		}
		
		SetDestroyedFlag(damage_mode);

		if (damage_type != SplashDamageType)
			DealSplashDamage();

		MW4AI::g_Rect4DHash->RemovePermRect (this);
		const GameModel *model = GetGameModel();
		Check_Object(model);
				
//		CreateEffect(model->destroyedEffectResource, (Point3D)GetLocalToWorld());
		CreateEffect(model->destroyedEffectResource, this);
		Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource);
		if((death_entity) && (!m_deathEntity.GetCurrent()))
		{
			m_deathEntity.Remove();
			m_deathEntity.Add(death_entity);
		}

		RemoveFromExecution();

		
	}

	Entity::ReactToDestruction(damage_mode, damage_type);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::ReactToHit(const Entity__TakeDamageMessage *message,Adept::DamageObject *part_hit)
{
	Check_Object(this);
	Check_Object(message);

	MWObject::ReactToHit(message,part_hit);
	
    if (g_nMR == 2)
        return;
	// TODO: this should be more general and should take near
	// misses into account as well as direct hits -- PAULT
	if (GetAI() != 0)
	{
		NotifyAIShot(*(GetAI()),message->inflictingEntityID);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::Scalar
	Building::FriendlyFireDamageMultiplier() const
{
	if (MW4AI::UserConstants::Instance() != 0)
	{
		return (MW4AI::UserConstants::Instance()->Get(MW4AI::UserConstants::building_ff_multiplier));
	}

	return (1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Building::GetLineOfSight(Stuff::LinearMatrix4D& line_of_sight)
{
	Stuff::ChainIteratorOf<MechWarrior4::Weapon*> i(&weaponChain);
	MechWarrior4::Weapon* weapon = i.ReadAndNext();

	if ((weapon != 0) &&
		(weapon->sitePointer != 0) &&
		(weapon->sitePointer->GetParent() != 0))
	{
		line_of_sight = weapon->sitePointer->GetParent()->GetLocalToWorld();
		return;
	}

	MWObject::GetLineOfSight(line_of_sight);
}

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

	void AddOrRemoveFromMap(SensorCellMap& sensor_cell_map, int name_table_array, bool add)
	{
		NameTable *table = NameTable::GetInstance();
		int size(table->nameTableArray[name_table_array].GetLength());
		{for (int i = 0;
			  i < size;
			  ++i)
		{
			NameTableEntry *data = &table->nameTableArray [name_table_array][i];
			Adept::Entity* entity = data->dataPointer->GetCurrent ();

			if ((entity != 0) &&
				(entity->IsDerivedFrom(Building::DefaultData) == true))
			{
				Building* building = Cast_Object(Building*,entity);

				if (building->m_AddedToSensorCellMap != add)
				{
					if (add == true)
					{
						sensor_cell_map.Add(building,building->GetLocalToWorld());
					}
					else
					{
						sensor_cell_map.Remove(building,building->GetLocalToWorld());
					}
				}

				building->m_AddedToSensorCellMap = add;
			}
		}}
	}

void
	Building::AddAllBuildingsToSensorCellMap()
{
	if ((NameTable::GetInstance() == 0) ||
		(m_BuildingsAddedToCellMap == true) ||
		(MWMission::GetInstance() == 0))
	{
		return;
	}

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	AddOrRemoveFromMap(mwmission->m_BuildingCellMap,NameTable::BuildingArray,true);
	AddOrRemoveFromMap(mwmission->m_VehicleAndTurretCellMap,NameTable::TurretArray,true);

	m_BuildingsAddedToCellMap = true;
}

void
	Building::RemoveAllBuildingsFromSensorCellMap()
{
	if ((NameTable::GetInstance() == 0) ||
		(m_BuildingsAddedToCellMap == false) ||
		(MWMission::GetInstance() == 0))
	{
		return;
	}

	MWMission* mwmission = Cast_Object(MWMission*,MWMission::GetInstance());
	AddOrRemoveFromMap(mwmission->m_BuildingCellMap,NameTable::BuildingArray,false);
	AddOrRemoveFromMap(mwmission->m_VehicleAndTurretCellMap,NameTable::TurretArray,false);

	m_BuildingsAddedToCellMap = false;
}
