Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1584 lines
38 KiB
C++
1584 lines
38 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "entity.h"
|
|
#include "interest.h"
|
|
#include "exptbl.h"
|
|
#include "hostmgr.h"
|
|
#include "player.h"
|
|
#include "audio.h"
|
|
#include "fileutil.h"
|
|
#include "registry.h"
|
|
#include "event.h"
|
|
#include "app.h"
|
|
#include "nttmgr.h"
|
|
#include "subsystm.h"
|
|
#include "namelist.h"
|
|
#include "notation.h"
|
|
|
|
#if defined(TRACE_EXECUTE_ENTITY)
|
|
static BitTrace Execute_Entity("Execute Entity");
|
|
#define SET_EXECUTE_ENTITY() Execute_Entity.Set()
|
|
#define CLEAR_EXECUTE_ENTITY() Execute_Entity.Clear()
|
|
#else
|
|
#define SET_EXECUTE_ENTITY()
|
|
#define CLEAR_EXECUTE_ENTITY()
|
|
#endif
|
|
|
|
#if defined(TRACE_PERFORM_ENTITY)
|
|
static BitTrace Perform_Entity("Perform Entity");
|
|
#define SET_PERFORM_ENTITY() Perform_Entity.Set()
|
|
#define CLEAR_PERFORM_ENTITY() Perform_Entity.Clear()
|
|
#else
|
|
#define SET_PERFORM_ENTITY()
|
|
#define CLEAR_PERFORM_ENTITY()
|
|
#endif
|
|
|
|
#if defined(TRACE_PERFORM_SUBSYSTEMS)
|
|
static BitTrace Perform_Subsystems("Perform Subsystems");
|
|
#define SET_PERFORM_SUBSYSTEMS() Perform_Subsystems.Set()
|
|
#define CLEAR_PERFORM_SUBSYSTEMS() Perform_Subsystems.Clear()
|
|
#else
|
|
#define SET_PERFORM_SUBSYSTEMS()
|
|
#define CLEAR_PERFORM_SUBSYSTEMS()
|
|
#endif
|
|
|
|
//#############################################################################
|
|
//############################## Entity #################################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Virtual Data support
|
|
//
|
|
Derivation* Entity::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(Simulation::GetClassDerivations(), "Entity");
|
|
return &classDerivations;
|
|
}
|
|
|
|
Entity::SharedData
|
|
Entity::DefaultData(
|
|
Entity::GetClassDerivations(),
|
|
Entity::GetMessageHandlers(),
|
|
Entity::GetAttributeIndex(),
|
|
Entity::StateCount,
|
|
Entity::Make
|
|
);
|
|
|
|
//#############################################################################
|
|
// Message Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
Entity::MessageHandlerEntries[]=
|
|
{
|
|
{
|
|
Entity::MakeMessageID,
|
|
"Make",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::MakeReadyMessageID,
|
|
"MakeReady",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::RemakeEntityMessageID,
|
|
"RemakeEntity",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::RemakeReadyMessageID,
|
|
"RemakeReady",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::RemakeCompleteMessageID,
|
|
"RemakeComplete",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::RemakeIncompleteMessageID,
|
|
"RemakeIncomplete",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
MESSAGE_ENTRY(Entity, DestroyEntity),
|
|
{
|
|
Entity::TransferEntityMessageID,
|
|
"TransferEntity",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::TransferCompleteMessageID,
|
|
"TransferComplete",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::TransferIncompleteMessageID,
|
|
"TransferIncomplete",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
MESSAGE_ENTRY(Entity, BecomeInteresting),
|
|
MESSAGE_ENTRY(Entity, BecomeUninteresting),
|
|
MESSAGE_ENTRY(Entity, Update),
|
|
{
|
|
Entity::SubscribeReplicantMessageID,
|
|
"SubscribeReplicant",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
{
|
|
Entity::UnsubscribeReplicantMessageID,
|
|
"UnsubscribeReplicant",
|
|
(Entity::Handler)&Entity::DefaultMessageHandler
|
|
},
|
|
MESSAGE_ENTRY(Entity, TakeDamage),
|
|
MESSAGE_ENTRY(Entity, PlayerLink),
|
|
MESSAGE_ENTRY(Entity, TakeDamageStream)
|
|
};
|
|
|
|
Entity::MessageHandlerSet& Entity::GetMessageHandlers()
|
|
{
|
|
static Entity::MessageHandlerSet messageHandlers(ELEMENTS(Entity::MessageHandlerEntries), Entity::MessageHandlerEntries, Receiver::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::Receive(Event* event)
|
|
{
|
|
Check(this);
|
|
Check(event);
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If the entity is being transferred, post the message to the new owner of
|
|
// the entity
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// If the object is not valid, filter the message to determine which ones to
|
|
// defer and which ones to immediately process
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
if (!IsValid())
|
|
{
|
|
//switch case commented out by Ryan Bunker on 1/6/07
|
|
//switch (event->messageToSend->messageID)
|
|
{
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// The messages which should be allowed through should just break out of
|
|
// this switch
|
|
//----------------------------------------------------------------------
|
|
//
|
|
|
|
//default:
|
|
//
|
|
//-------------------
|
|
// Defer the messages
|
|
//-------------------
|
|
//
|
|
event->Defer();
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// Go ahead and dispatch the message to the entity
|
|
//------------------------------------------------
|
|
//
|
|
Receiver::Receive(event);
|
|
|
|
#if defined(USE_FPU_ANALYSIS)
|
|
if (!Fpu_Ok())
|
|
{
|
|
Registry *registry = application->GetRegistry();
|
|
Receiver::SharedData *shared_data =
|
|
registry->GetStaticData(GetClassID());
|
|
Derivation *derivation = shared_data->derivedClasses;
|
|
Receiver::MessageHandlerSet *handlers =
|
|
shared_data->activeMessageHandlers;
|
|
DEBUG_STREAM << derivation->className << ',' << GetInstance() << endl << std::flush;
|
|
DEBUG_STREAM << handlers->GetName(event->messageToSend->messageID)
|
|
<< endl;
|
|
Fail("Event corrupted FPU!");
|
|
}
|
|
#else
|
|
Check_Fpu();
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::Dispatch(Receiver::Message* incoming)
|
|
{
|
|
Message *message = (Message*) incoming;
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
// Call Receiver if it is not entity aware!!!!!
|
|
//
|
|
if (message->messageID < Receiver::NextMessageID)
|
|
{
|
|
Receiver::Dispatch(message);
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Initialize the entity information fields
|
|
//-----------------------------------------
|
|
//
|
|
message->entityID = entityID;
|
|
message->interestZoneID = interestZoneID;
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// If this is a replicant instance, reroute the message to the master
|
|
//-------------------------------------------------------------------
|
|
//
|
|
if (GetInstance() == ReplicantInstance)
|
|
{
|
|
application->SendMessage(
|
|
ownerID,
|
|
EntityManager::EntityManagerClientID,
|
|
message
|
|
);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// If this entity is invalid, post the message as an event
|
|
//--------------------------------------------------------
|
|
//
|
|
else if (!IsValid())
|
|
{
|
|
// ECH 1/28/96 - application->Post(DefaultEventPriority, this, message);
|
|
application->Post(EntityInvalidEventPriority, this, message);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Otherwise, go ahead and receive the message
|
|
//--------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Receiver::Receive(message);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::DispatchToReplicants(Message *what)
|
|
{
|
|
Check(this);
|
|
Check(what);
|
|
|
|
what->entityID = entityID;
|
|
what->interestZoneID = interestZoneID;
|
|
Check(application);
|
|
InterestManager *int_mgr = application->GetInterestManager();
|
|
Check(int_mgr);
|
|
int_mgr->EntityBroadcastToReplicants(this, what);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
const Entity::IndexEntry
|
|
Entity::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(Entity, LocalToWorld, localToWorld),
|
|
ATTRIBUTE_ENTRY(Entity, LocalOrigin, localOrigin),
|
|
ATTRIBUTE_ENTRY(Entity, DamageZoneCount, damageZoneCount),
|
|
ATTRIBUTE_ENTRY(Entity, DamageZones, damageZones)
|
|
};
|
|
|
|
Entity::AttributeIndexSet& Entity::GetAttributeIndex()
|
|
{
|
|
static Entity::AttributeIndexSet attributeIndex(ELEMENTS(Entity::AttributePointers),
|
|
Entity::AttributePointers,
|
|
Simulation::GetAttributeIndex()
|
|
);
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::WriteUpdateRecord(
|
|
Simulation::UpdateRecord *record,
|
|
int update_model
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(record);
|
|
|
|
switch (update_model)
|
|
{
|
|
case DefaultUpdateModelBit:
|
|
{
|
|
Simulation::WriteUpdateRecord(record, update_model);
|
|
|
|
//
|
|
//----------------------------
|
|
// Write out the record update
|
|
//----------------------------
|
|
//
|
|
UpdateRecord *update = (UpdateRecord*)record;
|
|
update->localOrigin = localOrigin;
|
|
updateOrigin = localOrigin;
|
|
update->recordLength = sizeof(*update);
|
|
}
|
|
break;
|
|
case DamageZoneUpdateModelBit:
|
|
record->timeStamp = lastPerformance;
|
|
record->recordID = (Word)update_model;
|
|
WriteDamageUpdateRecord(record);
|
|
break;
|
|
|
|
default:
|
|
Simulation::WriteUpdateRecord(record, update_model);
|
|
break;
|
|
}
|
|
record->subsystemID = (Word)(EntitySubsystemID + 1);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::UpdateMessageHandler(UpdateMessage *message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//------------------------------------------------------
|
|
// Position to the beginning of the update record blocks
|
|
//------------------------------------------------------
|
|
//
|
|
MemoryStream stream(message, message->messageLength, sizeof(*message));
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Step through each block until there are no more remaining, and send the
|
|
// update out the the simulation indicated by the subsystemID
|
|
//------------------------------------------------------------------------
|
|
//
|
|
while (stream.GetBytesRemaining())
|
|
{
|
|
Simulation::UpdateRecord *update =
|
|
(Simulation::UpdateRecord*)stream.GetPointer();
|
|
Check_Pointer(update);
|
|
Simulation *simulation = GetSimulation((int)update->subsystemID-1);
|
|
Check(simulation);
|
|
simulation->ReadUpdateRecord(update);
|
|
stream.AdvancePointer(update->recordLength);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::ReadUpdateRecord(Simulation::UpdateRecord *record)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(record);
|
|
|
|
switch(record->recordID)
|
|
{
|
|
case DefaultUpdateModelBit:
|
|
{
|
|
//
|
|
//-----------------------
|
|
// Read the record update
|
|
//-----------------------
|
|
//
|
|
UpdateRecord *update = (UpdateRecord*)record;
|
|
updateOrigin = update->localOrigin;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// If we aren't able to run yet, we must update local origin as well
|
|
//------------------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
int app_state = application->GetApplicationState();
|
|
if (
|
|
app_state < Application::RunningMission
|
|
|| app_state == Application::CreatingMission
|
|
)
|
|
{
|
|
localOrigin = updateOrigin;
|
|
localToWorld = localOrigin;
|
|
}
|
|
Simulation::ReadUpdateRecord(record);
|
|
}
|
|
break;
|
|
case DamageZoneUpdateModelBit:
|
|
ReadDamageUpdateRecord(record);
|
|
break;
|
|
default:
|
|
Simulation::ReadUpdateRecord(record);
|
|
}
|
|
|
|
|
|
|
|
Check_Fpu();
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::ReadDamageUpdateRecord(Simulation::UpdateRecord *update_record)
|
|
{
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Step through each block until there are no more remaining, and send the
|
|
// update out the the damageZone indicated by the damageZoneIndex
|
|
//------------------------------------------------------------------------
|
|
//
|
|
MemoryStream stream(
|
|
update_record,
|
|
update_record->recordLength,
|
|
sizeof(*update_record)
|
|
);
|
|
|
|
while (stream.GetBytesRemaining())
|
|
{
|
|
DamageZone::UpdateRecord *damage_zone_record =
|
|
(DamageZone::UpdateRecord*)stream.GetPointer();
|
|
int damage_zone_index = damage_zone_record->damageZoneIndex;
|
|
damageZones[damage_zone_index]->ReadUpdateRecord(damage_zone_record);
|
|
stream.AdvancePointer(damage_zone_record->recordLength);
|
|
}
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
#define MAX_UPDATE_LENGTH 1400
|
|
|
|
static long Update_Buffer[MAX_UPDATE_LENGTH >> 2];
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::WriteDamageUpdateRecord(Simulation::UpdateRecord *update_record)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(update_record);
|
|
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Make a memory stream this size of a network packet
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
MemoryStream stream(
|
|
update_record,
|
|
sizeof(Update_Buffer) - ((char*)update_record - (char*)Update_Buffer),
|
|
sizeof(*update_record)
|
|
);
|
|
DamageZone::UpdateRecord *record_start =
|
|
(DamageZone::UpdateRecord*)stream.GetPointer();
|
|
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Update Every DamageZone For NOW
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
for(int ii=0;ii<damageZoneCount;++ii)
|
|
{
|
|
DamageZone::UpdateRecord *damage_zone_update =
|
|
(DamageZone::UpdateRecord*)stream.GetPointer();
|
|
damageZones[ii]->WriteUpdateRecord(damage_zone_update);
|
|
stream.AdvancePointer(damage_zone_update->recordLength);
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Check to see if an update message is necessary
|
|
//-----------------------------------------------
|
|
//
|
|
if (stream.GetPointer() != record_start)
|
|
{
|
|
Simulation::UpdateRecord *damage_update =
|
|
(Simulation::UpdateRecord*)update_record;
|
|
damage_update->recordLength =
|
|
(char*)stream.GetPointer() - (char*)update_record;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Entity::UpdateMessage*
|
|
Entity::Execute(const Time& till)
|
|
{
|
|
SET_EXECUTE_ENTITY();
|
|
Check(this);
|
|
Check(&till);
|
|
|
|
MemoryStream
|
|
stream(Update_Buffer, sizeof(Update_Buffer), sizeof(UpdateMessage));
|
|
char *record_start = (char*)stream.GetPointer();
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Check to see if the application will let us run. If We are starting a
|
|
// mission, only the player class objects can execute
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
// BT bring-up trace (env BT_NET_TRACE): which Execute branch high-ID entities take.
|
|
if (getenv("BT_NET_TRACE") && (int)GetEntityID() >= 20)
|
|
{
|
|
static int s_execTrace = 0;
|
|
if ((s_execTrace++ % 240) == 0)
|
|
{
|
|
DEBUG_STREAM << "[ent-exec] " << GetEntityID()
|
|
<< " state=" << (int)application->GetApplicationState()
|
|
<< " preRun=" << (int)IsPreRunnable()
|
|
<< " inst=" << (int)GetInstance() << "\n" << std::flush;
|
|
}
|
|
}
|
|
if (
|
|
application->GetApplicationState() == Application::RunningMission
|
|
|| application->GetApplicationState() == Application::EndingMission
|
|
|| IsPreRunnable()
|
|
)
|
|
{
|
|
PerformAndWatch(till, &stream);
|
|
}
|
|
else
|
|
{
|
|
WriteSimulationUpdate(&stream);
|
|
}
|
|
|
|
#if defined(USE_FPU_ANALYSIS)
|
|
if (!Fpu_Ok())
|
|
{
|
|
Registry *registry = application->GetRegistry();
|
|
Receiver::SharedData *shared_data =
|
|
registry->GetStaticData(GetClassID());
|
|
Derivation *derivation = shared_data->derivedClasses;
|
|
DEBUG_STREAM << derivation->className << ',' << GetInstance() << endl << std::flush;
|
|
Fail("Simulation corrupted FPU!");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Check to see if an update message is necessary
|
|
//-----------------------------------------------
|
|
//
|
|
if (stream.GetPointer() != record_start)
|
|
{
|
|
UpdateMessage *update = (UpdateMessage*)Update_Buffer;
|
|
update->messageID = UpdateMessageID;
|
|
update->messageLength = (char*)stream.GetPointer() - (char*)Update_Buffer;
|
|
update->messageFlags = 0;
|
|
CLEAR_EXECUTE_ENTITY();
|
|
return update;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Otherwise, return NULL for no update
|
|
//-------------------------------------
|
|
//
|
|
Check_Fpu();
|
|
CLEAR_EXECUTE_ENTITY();
|
|
return NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Simulation*
|
|
Entity::GetSimulation(int index)
|
|
{
|
|
Check(this);
|
|
|
|
if (index == EntitySubsystemID)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Check_Fpu();
|
|
if ((unsigned)index < subsystemCount)
|
|
{
|
|
return subsystemArray[index];
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Subsystem*
|
|
Entity::FindSubsystem(const char* name)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(name);
|
|
|
|
for (int i=0; i<subsystemCount; ++i)
|
|
{
|
|
if (subsystemArray[i])
|
|
{
|
|
Check(subsystemArray[i]);
|
|
if (!stricmp(subsystemArray[i]->GetName(), name))
|
|
{
|
|
return subsystemArray[i];
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
return NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
#define MAX_SUB_FILE_NAME (256)
|
|
|
|
int
|
|
Entity::FindSubsytemID
|
|
(
|
|
#if DEBUG_LEVEL>0
|
|
const char *model_name,
|
|
const char *subsystem_name
|
|
#else
|
|
const char *,
|
|
const char *subsystem_name
|
|
#endif
|
|
)
|
|
{
|
|
Check_Pointer(model_name);
|
|
Check_Pointer(subsystem_name);
|
|
|
|
int subsytem_id = EntitySubsystemID;
|
|
int sub_count = 0;
|
|
|
|
CString sub_file_string;
|
|
CString sub_directory_string("models\\");
|
|
CString model_name_string("model_name");
|
|
CString sub_post_string(".sub");
|
|
|
|
sub_file_string =
|
|
sub_directory_string + model_name_string + sub_post_string;
|
|
|
|
NotationFile sub_file(sub_file_string);
|
|
Check(&sub_file);
|
|
|
|
NameList *page_list = sub_file.MakePageList();
|
|
Register_Object(page_list);
|
|
NameList::Entry *entry=page_list->GetFirstEntry();
|
|
while (entry != NULL)
|
|
{
|
|
Check(entry);
|
|
Check_Pointer(entry->GetName());
|
|
if (!stricmp(subsystem_name, entry->GetName()))
|
|
{
|
|
subsytem_id = sub_count;
|
|
break;
|
|
}
|
|
|
|
++sub_count;
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
Unregister_Object(page_list);
|
|
delete page_list;
|
|
|
|
return subsytem_id;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::PerformAndWatch(
|
|
const Time &till,
|
|
MemoryStream *update_stream
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(&till);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Execute all the subsystems, then ourself
|
|
//-----------------------------------------
|
|
//
|
|
SET_PERFORM_SUBSYSTEMS();
|
|
if (subsystemArray)
|
|
{
|
|
Check_Pointer(subsystemArray);
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// If this is not a replicant, execute all subsystems
|
|
//---------------------------------------------------
|
|
//
|
|
if (GetInstance() != ReplicantInstance)
|
|
{
|
|
for (int i=0; i<subsystemCount; ++i)
|
|
{
|
|
if (subsystemArray[i])
|
|
{
|
|
Subsystem *subsystem = subsystemArray[i];
|
|
Check(subsystem);
|
|
if (subsystem->IsNonReplicantExecutable())
|
|
{
|
|
subsystem->PerformAndWatch(till, update_stream);
|
|
#if defined(USE_FPU_ANALYSIS)
|
|
if (!Fpu_Ok())
|
|
{
|
|
Registry *registry = application->GetRegistry();
|
|
Receiver::SharedData *shared_data =
|
|
registry->GetStaticData(GetClassID());
|
|
Derivation *derivation = shared_data->derivedClasses;
|
|
DEBUG_STREAM << derivation->className
|
|
<< "\nExecuting subsystem[" << i << "], "
|
|
<< subsystem->GetName() << endl;
|
|
Fail("Simulation corrupted FPU!");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If this is a replicant, execute only those subsystems which need to
|
|
//--------------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
for (int i=0; i<subsystemCount; ++i)
|
|
{
|
|
if (subsystemArray[i])
|
|
{
|
|
Subsystem *subsystem = subsystemArray[i];
|
|
Check(subsystem);
|
|
if (subsystem->IsReplicantExecutable())
|
|
{
|
|
subsystem->PerformAndWatch(till, update_stream);
|
|
#if defined(USE_FPU_ANALYSIS)
|
|
if (!Fpu_Ok())
|
|
{
|
|
Registry *registry = application->GetRegistry();
|
|
Receiver::SharedData *shared_data =
|
|
registry->GetStaticData(GetClassID());
|
|
Derivation *derivation = shared_data->derivedClasses;
|
|
DEBUG_STREAM << derivation->className
|
|
<< " replicant\nExecuting subsystem[" << i << "], "
|
|
<< subsystem->GetName() << endl;
|
|
Fail("Simulation corrupted FPU!");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CLEAR_PERFORM_SUBSYSTEMS();
|
|
|
|
SET_PERFORM_ENTITY();
|
|
Simulation::PerformAndWatch(till, update_stream);
|
|
Check_Fpu();
|
|
CLEAR_PERFORM_ENTITY();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::PlayerLinkMessageHandler(PlayerLinkMessage *message)
|
|
{
|
|
Check(application);
|
|
HostManager *host = application->GetHostManager();
|
|
Check(host);
|
|
playerLink =(Player*) host->GetEntityPointer(message->playerID);
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::TakeDamageStreamMessageHandler(TakeDamageStreamMessage *message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Extract the common information out of the stream
|
|
//-------------------------------------------------
|
|
//
|
|
Damage damage_data;
|
|
EntityID shooting_entity = message->shootingEntity;
|
|
int damage_zone_index = message->damageZoneIndex;
|
|
damage_data.impactPoint = message->impactPoint;
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Figure out how many damage messages there are here
|
|
//---------------------------------------------------
|
|
//
|
|
DynamicMessage damage_stream(
|
|
message,
|
|
message->messageLength,
|
|
sizeof(*message)
|
|
);
|
|
int entry_count;
|
|
damage_stream >> entry_count;
|
|
|
|
for (int i=0; i<entry_count; ++i)
|
|
{
|
|
//
|
|
//-------------------------------------------------
|
|
// Read in damageType, damageAmount and subsystemID
|
|
//-------------------------------------------------
|
|
//
|
|
damage_stream >> damage_data.damageType >> damage_data.damageAmount;
|
|
int subsystem;
|
|
damage_stream >> subsystem;
|
|
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Apply the damage to the appropriate damageZone
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
TakeDamageMessage
|
|
message(
|
|
TakeDamageMessageID,
|
|
sizeof(TakeDamageMessage),
|
|
shooting_entity,
|
|
damage_zone_index,
|
|
damage_data,
|
|
subsystem
|
|
);
|
|
Dispatch(&message);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::TakeDamageMessageHandler(TakeDamageMessage *message)
|
|
{
|
|
//
|
|
//-------------------------------------
|
|
// YOU MUST CHECK FOR -1 DamageZone!!!!
|
|
//-------------------------------------
|
|
//
|
|
if (!damageZones || message->damageZone == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If we have no damage zones allocated, we ignore any damage sent to us
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Verify(message->damageZone < damageZoneCount);
|
|
Verify(message->damageZone >= 0 );
|
|
Check_Pointer(damageZones);
|
|
Check(damageZones[message->damageZone]);
|
|
damageZones[message->damageZone]->TakeDamage(message->damageData);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Entity::Entity(
|
|
Entity::MakeMessage *creation_message,
|
|
Entity::SharedData &virtual_data
|
|
):
|
|
Simulation(creation_message->classToCreate,virtual_data),
|
|
staticVideoSocket(NULL),
|
|
dynamicVideoSocket(NULL),
|
|
audioSocket(NULL)
|
|
{
|
|
Check_Pointer(this);
|
|
Check(creation_message);
|
|
|
|
//
|
|
// Get host manager pointer
|
|
//
|
|
HostManager *host_manager;
|
|
|
|
Check(application);
|
|
host_manager = application->GetHostManager();
|
|
Check(host_manager);
|
|
|
|
//
|
|
//-----------------------------
|
|
// Initialize the entity fields
|
|
//-----------------------------
|
|
//
|
|
playerLink = NULL;
|
|
resourceID = creation_message->resourceID;
|
|
owningPlayer = (Player*)host_manager->GetEntityPointer(
|
|
creation_message->owningPlayerID
|
|
);
|
|
simulationFlags = creation_message->instanceFlags;
|
|
entityID = creation_message->entityID;
|
|
ownerID = creation_message->ownerID;
|
|
creationTime = Now();
|
|
interestZoneID = creation_message->interestZoneID;
|
|
interestCount = 0;
|
|
|
|
// initialize attributes
|
|
localOrigin = creation_message->localOrigin;
|
|
updateOrigin = localOrigin;
|
|
localToWorld = localOrigin;
|
|
|
|
// initialize camera stuff
|
|
cameraOffset = Origin::Identity;
|
|
|
|
SetInvalidFlag();
|
|
|
|
// initialize model stuff
|
|
lastPerformance = creationTime;
|
|
subsystemCount = 0;
|
|
subsystemArray = NULL;
|
|
damageZones = NULL;
|
|
damageZoneCount = 0;
|
|
|
|
//
|
|
//--------------------------------
|
|
// Allocate the damageZones array
|
|
//--------------------------------
|
|
//
|
|
|
|
//
|
|
// Read In DamageZones and Create DamageZoneID's
|
|
//
|
|
if (resourceID != ResourceDescription::NullResourceID)
|
|
{
|
|
ResourceDescription *dmg_res =
|
|
application->GetResourceFile()->SearchList(
|
|
resourceID,
|
|
ResourceDescription::DamageZoneStreamResourceType
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Allocate damageZones for this entity only
|
|
// if they were defined in the resource
|
|
//--------------------------------------------
|
|
//
|
|
if (dmg_res)
|
|
{
|
|
dmg_res->Lock();
|
|
DynamicMemoryStream damage_zone_stream(
|
|
dmg_res->resourceAddress,
|
|
dmg_res->resourceSize);
|
|
|
|
damage_zone_stream >> damageZoneCount;
|
|
damageZones = new (::DamageZone(*[damageZoneCount]));
|
|
Register_Pointer(damageZones);
|
|
dmg_res->Unlock();
|
|
}
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------
|
|
// Create a new entity ID and assign ownership to this host
|
|
// If (this is a master entity) or
|
|
// (this is a dynamic hermit) or
|
|
// (this is the initial independent)
|
|
//---------------------------------------------------------
|
|
//
|
|
if (
|
|
(GetInstance() == MasterInstance) ||
|
|
(GetInstance() == HermitInstance && IsDynamic()) ||
|
|
(
|
|
(GetInstance() == IndependantInstance) &&
|
|
(creation_message->ownerID == NullHostID ||
|
|
creation_message->ownerID == MapHostID)
|
|
)
|
|
)
|
|
{
|
|
entityID = host_manager->MakeUniqueEntityID();
|
|
ownerID = host_manager->GetLocalHostID();
|
|
creation_message->entityID = entityID;
|
|
creation_message->ownerID = ownerID;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------
|
|
// Tell the host manager about this new entity
|
|
//--------------------------------------------
|
|
//
|
|
Check(host_manager);
|
|
host_manager->NotifyOfEntityCreation(this, creation_message);
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Entity*
|
|
Entity::Make(Entity::MakeMessage *creation_message)
|
|
{
|
|
return new Entity(creation_message,DefaultData);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
Entity::CreateMakeMessage(
|
|
MakeMessage *creation_message,
|
|
#if DEBUG_LEVEL>0
|
|
NotationFile *model_file,
|
|
#else
|
|
NotationFile *,
|
|
#endif
|
|
const ResourceDirectories *
|
|
)
|
|
{
|
|
Check(creation_message);
|
|
Check(model_file);
|
|
char *p;
|
|
|
|
creation_message->messageLength = sizeof(Entity::MakeMessage);
|
|
creation_message->messageID = Entity::MakeMessageID;
|
|
creation_message->owningPlayerID = EntityID::Null;
|
|
creation_message->messageFlags = 0;
|
|
creation_message->classToCreate = RegisteredClass::TrivialEntityClassID;
|
|
creation_message->instanceFlags = 0; //DefaultFlags;
|
|
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "X position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.x = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Y position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.y = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Z position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.z = atof(p);
|
|
|
|
EulerAngles pyr;
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Pitch missing!\n";
|
|
return False;
|
|
}
|
|
pyr.pitch = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Yaw missing!\n";
|
|
return False;
|
|
}
|
|
pyr.yaw = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Roll missing!\n";
|
|
return False;
|
|
}
|
|
pyr.roll = atof(p);
|
|
creation_message->localOrigin.angularPosition = pyr;
|
|
return True;
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
Entity::CreateMakeMapMessage(
|
|
MakeMapMessage *creation_message,
|
|
ResourceDescription::ResourceID resource_id,
|
|
#if DEBUG_LEVEL>0
|
|
NotationFile *model_file,
|
|
#else
|
|
NotationFile *,
|
|
#endif
|
|
const ResourceDirectories *
|
|
)
|
|
{
|
|
Check(creation_message);
|
|
Check(model_file);
|
|
char *p;
|
|
|
|
creation_message->messageLength = sizeof(Entity::MakeMapMessage);
|
|
creation_message->messageID = Entity::MakeMessageID;
|
|
creation_message->messageFlags = Entity::MakeMessage::MapStreamMarkerFlag;
|
|
|
|
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "X position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.x = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Y position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.y = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Z position coordinate missing!\n";
|
|
return False;
|
|
}
|
|
creation_message->localOrigin.linearPosition.z = atof(p);
|
|
|
|
EulerAngles pyr = EulerAngles::Identity;
|
|
#if 0
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Pitch missing!\n";
|
|
return False;
|
|
}
|
|
pyr.pitch = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Yaw missing!\n";
|
|
return False;
|
|
}
|
|
pyr.yaw = atof(p);
|
|
if ((p = strtok(NULL, " ")) == NULL)
|
|
{
|
|
std::cerr << "Roll missing!\n";
|
|
return False;
|
|
}
|
|
pyr.roll = atof(p);
|
|
#endif
|
|
|
|
creation_message->localOrigin.angularPosition = pyr;
|
|
creation_message->resourceID = resource_id;
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::DestroyEntityMessageHandler(Message *)
|
|
{
|
|
CondemnToDeathRow();
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Entity::~Entity()
|
|
{
|
|
//
|
|
// Notify the host manager that we are going away
|
|
//
|
|
Check(application);
|
|
application->GetHostManager()->NotifyOfEntityDestruction(this);
|
|
|
|
//
|
|
// Delete the subsystem array
|
|
//
|
|
if (subsystemArray)
|
|
{
|
|
Check_Pointer(subsystemArray);
|
|
for (int i=0; i<subsystemCount; ++i)
|
|
{
|
|
if (subsystemArray[i])
|
|
{
|
|
Unregister_Object(subsystemArray[i]);
|
|
delete subsystemArray[i];
|
|
}
|
|
}
|
|
Unregister_Pointer(subsystemArray);
|
|
delete subsystemArray;
|
|
}
|
|
if (damageZones)
|
|
{
|
|
Check_Pointer(damageZones);
|
|
for (int i=0; i<damageZoneCount; ++i)
|
|
{
|
|
if (damageZones[i])
|
|
{
|
|
Unregister_Object(damageZones[i]);
|
|
delete damageZones[i];
|
|
}
|
|
}
|
|
Unregister_Pointer(damageZones);
|
|
delete damageZones;
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Entity::CondemnToDeathRow()
|
|
{
|
|
Check(this);
|
|
Check(application);
|
|
EntityManager *entity_manager = application->GetEntityManager();
|
|
Check(entity_manager);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Make sure we are not entered into deathrow twice
|
|
//-------------------------------------------------
|
|
//
|
|
if (!IsCondemned())
|
|
{
|
|
entity_manager->deathRow->Add(this);
|
|
SetCondemnedFlag();
|
|
NeverExecute();
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Test Support
|
|
//
|
|
|
|
Logical
|
|
Entity::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|
|
|
|
//##########################################################################
|
|
// Renderer Support
|
|
//
|
|
void
|
|
Entity::AddStaticVideoComponent(Component *component)
|
|
{
|
|
Check(component);
|
|
Check(&staticVideoSocket);
|
|
staticVideoSocket.Add(component);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Entity::AddDynamicVideoComponent(Component *component)
|
|
{
|
|
Check(component);
|
|
Check(&dynamicVideoSocket);
|
|
dynamicVideoSocket.Add(component);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Entity::AddAudioComponent(Component *component)
|
|
{
|
|
Check(component);
|
|
Check(&audioSocket);
|
|
audioSocket.Add(component);
|
|
Check_Fpu();
|
|
}
|
|
|
|
Enumeration
|
|
Entity::GetAudioRepresentation(Entity *linked_entity)
|
|
{
|
|
Check(this);
|
|
Check(linked_entity);
|
|
|
|
return
|
|
(linked_entity == this) ?
|
|
InternalAudioRepresentation : ExternalAudioRepresentation;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Interest Support
|
|
//
|
|
void
|
|
Entity::BecomeInteresting()
|
|
{
|
|
BecomeInterestingMessage
|
|
message(
|
|
Entity::BecomeInterestingMessageID,
|
|
sizeof(BecomeInterestingMessage)
|
|
);
|
|
|
|
Receiver::Dispatch(&message);
|
|
Check_Fpu();
|
|
}
|
|
|
|
void Entity::BecomeInterestingMessageHandler(const Receiver::Message*)
|
|
{
|
|
++interestCount;
|
|
Check_Fpu();
|
|
}
|
|
|
|
void
|
|
Entity::BecomeUninterestingMessageHandler()
|
|
{
|
|
--interestCount;
|
|
Check_Fpu();
|
|
}
|
|
|
|
Enumeration
|
|
Entity::GetInterestPriority(Entity *linked_entity)
|
|
{
|
|
Check(this);
|
|
Check(linked_entity);
|
|
|
|
return (linked_entity == this) ? HighInterestPriority : LowInterestPriority;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
Entity::GetDamageZoneIndex(const CString &damage_zone_name) const
|
|
{
|
|
|
|
for(int ii=0;ii<damageZoneCount;++ii)
|
|
{
|
|
if(damageZones[ii]->damageZoneName.Compare(damage_zone_name) == 0)
|
|
{
|
|
return ii;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
Entity::CreateDamageZoneStream(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
|
|
//
|
|
//-------------------
|
|
// Find the .dmg file
|
|
//-------------------
|
|
//
|
|
const char* dmg_file_cname;
|
|
if (
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"DamageZones",
|
|
&dmg_file_cname
|
|
)
|
|
)
|
|
{
|
|
std::cerr << model_name << " is missing .dmg file specification!\n";
|
|
return -1;
|
|
}
|
|
|
|
CString dmg_filename =
|
|
MakePathedFilename(directories->modelDirectory, dmg_file_cname);
|
|
|
|
NotationFile *dmg_file = new NotationFile(dmg_filename);
|
|
Register_Object(dmg_file);
|
|
int page_count = dmg_file->PageCount();
|
|
if (!page_count)
|
|
{
|
|
std::cerr << dmg_filename << " is empty or missing!\n";
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
return -1;
|
|
}
|
|
|
|
DynamicMemoryStream damage_stream;
|
|
|
|
damage_stream << page_count;
|
|
NameList *dmg_namelist = dmg_file->MakePageList();
|
|
Register_Object(dmg_namelist);
|
|
|
|
NameList::Entry *dmg_entry = dmg_namelist->GetFirstEntry();
|
|
|
|
CString dmg_page_name;
|
|
while(dmg_entry)
|
|
{
|
|
dmg_page_name = dmg_entry->GetName();
|
|
DamageZone::CreateStreamedDamageZone(
|
|
model_file,
|
|
model_name,
|
|
NULL,
|
|
dmg_page_name,
|
|
&damage_stream,
|
|
dmg_file,
|
|
directories
|
|
);
|
|
dmg_entry = dmg_entry->GetNextEntry();
|
|
}
|
|
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Add Stream to Resource
|
|
//~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription *new_res =
|
|
resource_file->AddResourceMemoryStream(
|
|
model_name,
|
|
ResourceDescription::DamageZoneStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&damage_stream
|
|
);
|
|
Check(new_res);
|
|
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
Unregister_Object(dmg_namelist);
|
|
delete dmg_namelist;
|
|
|
|
return new_res->resourceID;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::ResourceID
|
|
Entity::CreateExplosionTableStream(
|
|
ResourceFile *resource_file,
|
|
const char *model_name,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check(model_file);
|
|
Check_Pointer(directories);
|
|
//
|
|
//~~~~~~~~~~~~~~~~~
|
|
// Find *.dmg file
|
|
//~~~~~~~~~~~~~~~~~
|
|
//
|
|
const char *entry_data;
|
|
if (!model_file->GetEntry(
|
|
"gamedata",
|
|
"DamageZones",
|
|
&entry_data
|
|
)
|
|
)
|
|
{
|
|
DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush;
|
|
return -1;
|
|
}
|
|
CString filename =
|
|
MakePathedFilename(directories->modelDirectory, entry_data);
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Make a notation file of the DMG Entries
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
NotationFile *dmg_file = new NotationFile(filename);
|
|
Register_Object(dmg_file);
|
|
|
|
if(dmg_file->IsEmpty())
|
|
{
|
|
DEBUG_STREAM << model_name << " *.dmg file is empty or missing!\n" << std::flush;
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
return -1;
|
|
}
|
|
|
|
DynamicMemoryStream explosion_stream;
|
|
NameList *dmg_namelist = dmg_file->MakePageList();
|
|
Register_Object(dmg_namelist);
|
|
|
|
NameList::Entry *dmg_entry = dmg_namelist->GetFirstEntry();
|
|
while(dmg_entry)
|
|
{
|
|
const char *entry_data;
|
|
if (dmg_file->GetEntry(
|
|
dmg_entry->GetName(),
|
|
"ExplosionTable",
|
|
&entry_data
|
|
)
|
|
)
|
|
{
|
|
CString filename =
|
|
MakePathedFilename(directories->modelDirectory, entry_data);
|
|
|
|
NotationFile *exp_file = new NotationFile(filename);
|
|
Register_Object(exp_file);
|
|
|
|
if(exp_file->IsEmpty())
|
|
{
|
|
std::cerr<<model_name<<" : "<<filename<<" is emoty or missing! \n";
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
Unregister_Object(dmg_namelist);
|
|
delete dmg_namelist;
|
|
Unregister_Object(exp_file);
|
|
delete exp_file;
|
|
return ResourceDescription::NullResourceID;
|
|
}
|
|
else
|
|
{
|
|
ExplosionTable::CreateStreamedExplosionTable(
|
|
resource_file,
|
|
model_name,
|
|
exp_file,
|
|
filename,
|
|
&explosion_stream
|
|
);
|
|
}
|
|
Unregister_Object(exp_file);
|
|
delete exp_file;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// Write Out no Explosions for this DamageZone
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int zero_int(0);
|
|
explosion_stream << zero_int;
|
|
}
|
|
dmg_entry = dmg_entry->GetNextEntry();
|
|
}
|
|
|
|
ResourceDescription *new_res =
|
|
resource_file->AddResourceMemoryStream(
|
|
model_name,
|
|
ResourceDescription::ExplosionTableStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&explosion_stream
|
|
);
|
|
Check(new_res);
|
|
Unregister_Object(dmg_file);
|
|
delete dmg_file;
|
|
Unregister_Object(dmg_namelist);
|
|
delete dmg_namelist;
|
|
return new_res->resourceID;
|
|
}
|
|
|
|
#if defined(TEST_CLASS)
|
|
# include "entity.tcp"
|
|
#endif
|