Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
415 lines
11 KiB
C++
415 lines
11 KiB
C++
//===========================================================================//
|
|
// 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;
|
|
}
|