Files
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
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>
2026-07-05 21:03:40 -05:00

1032 lines
26 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "renderer.h"
#include "rndorgn.h"
#include "explode.h"
#include "app.h"
#include "objstrm.h"
#include "hostmgr.h"
#include "subsystm.h"
//#############################################################################
//############################ Renderer #################################
//#############################################################################
const RendererRate DefaultRendererRate = 30.0f;
const RendererComplexity MaxRendererComplexity = 3.0f;
const RendererComplexity MinRendererComplexity = 0.0f;
const RendererFrameBudget DefaultRendererFrameBudget = 0.14f; //0.12f; // 0.16f;
//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
Renderer::MessageHandlerEntries[]=
{
{
Renderer::NotifyOfNewInterestingEntityMessageID,
"NotifyOfNewInterestingEntity",
(Renderer::Handler)&Renderer::NotifyOfNewInterestingEntityMessageHandler
},
{
Renderer::NotifyOfBecomingUninterestingEntityMessageID,
"NotifyOfBecomingUninterestingEntity",
(Renderer::Handler)&Renderer::NotifyOfBecomingUninterestingEntityMessageHandler
},
{
Renderer::StartEntityEffectMessageID,
"StartEntityEffect",
(Renderer::Handler)&Renderer::StartEntityEffectMessageHandler
},
{
Renderer::StopAllEntityEffectsMessageID,
"StopAllEntityEffects",
(Renderer::Handler)&Renderer::StopAllEntityEffectsMessageHandler
},
{
Renderer::StartEntityAlarmMessageID,
"StartEntityAlarm",
(Renderer::Handler)&Renderer::StartEntityAlarmMessageHandler
},
{
Renderer::StopEntityAlarmMessageID,
"StopEntityAlarm",
(Renderer::Handler)&Renderer::StopEntityAlarmMessageHandler
}
};
Renderer::MessageHandlerSet& Renderer::GetMessageHandlers()
{
static Renderer::MessageHandlerSet messageHandlers(ELEMENTS(Renderer::MessageHandlerEntries), Renderer::MessageHandlerEntries, Receiver::GetMessageHandlers());
return messageHandlers;
}
//#############################################################################
// Shared Data Support
//
Derivation* Renderer::GetClassDerivations()
{
static Derivation classDerivations(Receiver::GetClassDerivations(), "Renderer");
return &classDerivations;
}
Renderer::SharedData
Renderer::DefaultData(
Renderer::GetClassDerivations(),
Renderer::GetMessageHandlers()
);
//
//#############################################################################
// Renderer
//#############################################################################
//
Renderer::Renderer(
RendererRate calibration_rate,
RendererComplexity calibration_complexity,
RendererPriority calibration_priority,
InterestType interest_type,
InterestDepth depth_calibration,
ClassID class_ID,
SharedData &virtual_data
):
Receiver(class_ID, virtual_data),
rendererOriginSocket(NULL),
entitySocket(NULL)
{
rendererStatus = InactiveRendererStatus;
calibrationRate = calibration_rate;
calibrationFrameDuration = 1.0f / calibration_rate;
nextRenderTime = 0.0f;
Verify(MinRendererComplexity < calibration_complexity);
Verify(calibration_complexity <= MaxRendererComplexity);
calibrationComplexity = calibration_complexity;
calibrationPriority = calibration_priority;
interestType = interest_type;
depthCalibration = depth_calibration;
framePercentBudget = DefaultRendererFrameBudget;
Check_Fpu();
}
//
//#############################################################################
// ~Renderer
//#############################################################################
//
Renderer::~Renderer()
{
UnlinkFromEntity();
Check_Fpu();
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
Renderer::TestInstance() const
{
if (!IsDerivedFrom(*GetClassDerivations()))
{
return False;
}
Check(&rendererOriginSocket);
Check(&entitySocket);
return True;
}
//
//#############################################################################
// LinkToEntity
//#############################################################################
//
void
Renderer::LinkToEntity(Entity *entity)
{
Check(this);
Check(entity);
//
//--------------------------------------------------------------------------
// Unlink from current entity and add new entity
//--------------------------------------------------------------------------
//
UnlinkFromEntity();
entitySocket.Add(entity);
//
//--------------------------------------------------------------------------
// Make an interest origin for the given entity
//--------------------------------------------------------------------------
//
RendererOrigin *rendererOrigin;
rendererOrigin =
new RendererOrigin(
this,
entity,
interestType,
depthCalibration
);
Register_Object(rendererOrigin);
Check(application);
application->GetInterestManager()->AdoptInterestOrigin(rendererOrigin);
rendererOriginSocket.Add(rendererOrigin);
Check_Fpu();
}
//
//#############################################################################
// UnlinkFromEntity
//#############################################################################
//
void
Renderer::UnlinkFromEntity()
{
Check(this);
//
//--------------------------------------------------------------------------
// Delete current interest origin
//--------------------------------------------------------------------------
//
RendererOrigin *rendererOrigin;
if ((rendererOrigin = rendererOriginSocket.GetCurrent()) != NULL)
{
Check(rendererOrigin);
//
// Remove entity from socket
//
entitySocket.Remove();
//
// Delete the interest origin
//
Check(application);
Check(application->GetInterestManager());
application->GetInterestManager()->OrphanInterestOrigin(rendererOrigin);
Unregister_Object(rendererOrigin);
delete rendererOrigin;
}
Check_Fpu();
}
//
//#############################################################################
// LoadMission
//#############################################################################
//
void
Renderer::LoadMission(Mission *mission)
{
Check(this);
Verify(rendererStatus == InactiveRendererStatus);
rendererStatus = LoadingRendererStatus;
nextRenderTime = Now();
Check(application);
application->GetRendererManager()->StartRenderer(this);
LoadMissionImplementation(mission);
Check_Fpu();
}
//
//#############################################################################
// Shutdown
//#############################################################################
//
void
Renderer::Shutdown()
{
Check(this);
//
//-------------------------------------------------
// Shutdown and unhook the renderer if it is active
//-------------------------------------------------
//
if (rendererStatus == InactiveRendererStatus)
{
return;
}
rendererStatus = InactiveRendererStatus;
Check(application);
application->GetRendererManager()->StopRenderer(this);
ShutdownImplementation();
Check_Fpu();
}
//
//#############################################################################
// SetRendererStatusToRunning
//#############################################################################
//
void
Renderer::SetRendererStatusToRunning()
{
Verify(rendererStatus == LoadingRendererStatus);
rendererStatus = RunningRendererStatus;
Check_Fpu();
}
//
//#############################################################################
// UpdateRendererOrigin
//#############################################################################
//
void
Renderer::UpdateInterestOrigin(Time target_render_time)
{
Check(this);
//
//--------------------------------------------------------------------------
// If the renderer is inactive, skip it
//--------------------------------------------------------------------------
//
if (rendererStatus == InactiveRendererStatus)
return;
//
//--------------------------------------------------------------------------
// Does the renderer need to run this frame to meet its frame rate?
//--------------------------------------------------------------------------
//
if (nextRenderTime > target_render_time)
return;
//
//--------------------------------------------------------------------------
// Update the interest origin
//--------------------------------------------------------------------------
//
Entity *entity;
RendererOrigin *rendererOrigin;
entity = entitySocket.GetCurrent();
Check(entity);
rendererOrigin = rendererOriginSocket.GetCurrent();
Check(rendererOrigin);
rendererOrigin->Update(entity->GetInterestZoneID());
Check_Fpu();
}
//
//#############################################################################
// Execute
//#############################################################################
//
void
Renderer::Execute(
Time target_render_time,
RendererComplexity complexity_update
)
{
Check(this);
mTargetRenderTime = target_render_time;
//
//--------------------------------------------------------------------------
// If the renderer is suspended, skip it
//--------------------------------------------------------------------------
//
if (rendererStatus == InactiveRendererStatus)
return;
//
//--------------------------------------------------------------------------
// Does the renderer need to run this frame to meet its frame rate?
//--------------------------------------------------------------------------
//
// if (nextRenderTime > target_render_time)
// return;
// nextRenderTime += calibrationFrameDuration;
nextRenderTime = Now();
nextRenderTime += calibrationFrameDuration;
//
//--------------------------------------------------------------------------
// Keep track of renderers frame budget
//--------------------------------------------------------------------------
//
RendererFrameBudget start_percent;
RendererFrameBudget end_percent;
start_percent = Get_Frame_Percent_Used();
end_percent = start_percent + framePercentBudget;
Clamp(end_percent, 0.0f, 1.0f);
//
//--------------------------------------------------------------------------
// Get interest orign
//--------------------------------------------------------------------------
//
RendererOrigin *rendererOrigin;
rendererOrigin = rendererOriginSocket.GetCurrent();
Check(rendererOrigin);
//
//--------------------------------------------------------------------------
// Get iterators for the renderers interest origin and execute
// renderer
//--------------------------------------------------------------------------
//
RendererOrigin::InterestingEntityIterator
interesting_entity_iterator(rendererOrigin);
Check(&interesting_entity_iterator);
Time startImplementation = Now();
ExecuteImplementation(
complexity_update,
&interesting_entity_iterator
);
Time endImplementation = Now();
//
//--------------------------------------------------------------------------
// If the renderer is under budget then run background if available
//--------------------------------------------------------------------------
//
RendererFrameBudget current_frame_use;
/*for (
current_frame_use = Get_Frame_Percent_Used();
(start_percent < current_frame_use) && (current_frame_use < end_percent);
current_frame_use = Get_Frame_Percent_Used()
)
{
if (!ExecuteBackground())
break;
}*/
Check_Fpu();
}
//
//#############################################################################
// NotifyOfNewInterestingEntityMessageHandler
//#############################################################################
//
void
Renderer::NotifyOfNewInterestingEntityMessageHandler(
NotifyOfNewInterestingEntityMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == NotifyOfNewInterestingEntityMessageID);
Entity *entity;
if ((entity = message->GetInterestingEntity()) != NULL)
{
Check(entity);
NotifyOfNewInterestingEntity(entity);
}
Check_Fpu();
}
//
//#############################################################################
// NotifyOfBecomingUninterestingEntityMessageHandler
//#############################################################################
//
void
Renderer::NotifyOfBecomingUninterestingEntityMessageHandler(
NotifyOfBecomingUninterestingEntityMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == NotifyOfBecomingUninterestingEntityMessageID);
NotifyOfBecomingUninterestingEntity(message->GetUninterestingEntity());
Check_Fpu();
}
//
//#############################################################################
// StartEntityEffectMessageHandler
//#############################################################################
//
void
Renderer::StartEntityEffectMessageHandler(
StartEntityEffectMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == StartEntityEffectMessageID);
//
//--------------------------------------------------------------------------
// If the entity does not exists then return
//--------------------------------------------------------------------------
//
Entity *entity;
if ((entity = message->GetEntity()) == NULL)
return;
Check(entity);
//
//--------------------------------------------------------------------------
// Get the effect model resource
//--------------------------------------------------------------------------
//
ResourceDescription::ResourceID effect_resource_ID;
ResourceDescription *resource_description;
Explosion::ModelResource *model_resource;
effect_resource_ID = message->GetResourceID();
Check(application);
Check(application->GetResourceFile());
resource_description =
application->GetResourceFile()->SearchList(
effect_resource_ID,
ResourceDescription::GameModelResourceType
);
Check(resource_description);
resource_description->Lock();
model_resource =
(Explosion::ModelResource*)resource_description->resourceAddress;
Check_Pointer(model_resource);
//
//--------------------------------------------------------------------------
// If an explosion resource table is being used then use it to set
// the resource
//--------------------------------------------------------------------------
//
if (model_resource->resourceTableObjectID != NullObjectID)
{
//
// Get resource table pointer,
// Then set the appropriate resource ID
//
Plug *plug;
ExplosionResourceTable *resource_table;
ResourceDescription::ResourceID new_resource_ID;
plug = PlugStreamManager::FindPlug(model_resource->resourceTableObjectID);
resource_table = Cast_Object(ExplosionResourceTable*, plug);
Check(resource_table);
new_resource_ID = resource_table->FindResourceID(entity);
if (new_resource_ID != ResourceDescription::NullResourceID)
{
effect_resource_ID = new_resource_ID;
}
}
//
//--------------------------------------------------------------------------
// Call the start effect implementation
//--------------------------------------------------------------------------
//
StartEntityEffectImplementation(
entity,
message->GetDamageZone(),
effect_resource_ID
);
resource_description->Unlock();
}
//
//#############################################################################
// StopAllEntityEffectsMessageHandler
//#############################################################################
//
void
Renderer::StopAllEntityEffectsMessageHandler(
StopAllEntityEffectsMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == StopAllEntityEffectsMessageID);
Entity *entity;
if ((entity = message->GetEntity()) != NULL)
{
Check(entity);
StopAllEntityEffectsImplementation(entity);
}
}
//
//#############################################################################
// StartEntityAlarmMessageHandler
//#############################################################################
//
void
Renderer::StartEntityAlarmMessageHandler(
StartEntityAlarmMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == StartEntityAlarmMessageID);
Entity *entity;
if ((entity = message->GetEntity()) != NULL)
{
Check(entity);
StartEntityAlarmImplementation(
entity,
message->GetSubsystem(),
message->GetCondition(),
message->GetResourceID()
);
}
}
//
//#############################################################################
// StopEntityAlarmMessageHandler
//#############################################################################
//
void
Renderer::StopEntityAlarmMessageHandler(
StopEntityAlarmMessage *message
)
{
Check(this);
Check(message);
Verify(message->messageID == StopEntityAlarmMessageID);
Entity *entity;
if ((entity = message->GetEntity()) != NULL)
{
Check(entity);
StopEntityAlarmImplementation(
entity,
message->GetSubsystem(),
message->GetCondition()
);
}
}
//
//#############################################################################
// ExecuteImplementation
//#############################################################################
//
void
Renderer::ExecuteImplementation(
RendererComplexity,
RendererOrigin::InterestingEntityIterator*
)
{
Fail("Renderer::ExecuteImplementation - Should never reach here");
}
//
//#############################################################################
// LoadMissionImplementation
//#############################################################################
//
void
Renderer::LoadMissionImplementation(Mission*)
{
Fail("Renderer::LoadMissionImplementation - Should never reach here");
}
//
//#############################################################################
// ShutdownImplementation
//#############################################################################
//
void
Renderer::ShutdownImplementation()
{
Fail("Renderer::ShutdownImplementation - Should never reach here");
}
//#############################################################################
//######################### RendererManager #############################
//#############################################################################
//
//#############################################################################
// RendererManager
//#############################################################################
//
RendererManager::RendererManager():
Receiver(RendererManagerClassID, DefaultData),
renderers(NULL)
{
}
//
//#############################################################################
// ~RendererManager
//#############################################################################
//
RendererManager::~RendererManager()
{
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
RendererManager::TestInstance() const
{
Receiver::TestInstance();
Check(&renderers);
Check_Fpu();
return True;
}
//
//#############################################################################
// StartRenderer
//#############################################################################
//
void
RendererManager::StartRenderer(Renderer *renderer)
{
Check(this);
Check(renderer);
#if DEBUG_LEVEL>0
SChainIterator iterator(&renderers);
Verify(iterator.IsPlugMember(renderer) == False);
#endif
renderers.Add(renderer);
Check_Fpu();
}
//
//#############################################################################
// StopRenderer
//#############################################################################
//
void
RendererManager::StopRenderer(Renderer *renderer)
{
Check(this);
Check(renderer);
#if DEBUG_LEVEL>0
{
SChainIteratorOf<Renderer*> iterator(&renderers);
Verify(iterator.IsPlugMember(renderer) == True);
}
#endif
SChainIteratorOf<Renderer*> iterator(&renderers);
iterator.RemovePlug(renderer);
Check_Fpu();
}
//
//#############################################################################
// AreRenderersRunning
//#############################################################################
//
Logical
RendererManager::AreRenderersRunning()
{
Check(this);
Renderer
*renderer;
SChainIteratorOf<Renderer*>
renderers_iterator(&renderers);
if (renderers_iterator.GetCurrent() == NULL)
return False;
while ((renderer = renderers_iterator.ReadAndNext()) != NULL)
{
Check(renderer);
if (renderer->GetRendererStatus() != RunningRendererStatus)
return False;
}
Check_Fpu();
return True;
}
//
//#############################################################################
// UpdateInterestOrigins
//#############################################################################
//
void
RendererManager::UpdateInterestOrigins(Time target_render_time)
{
Check(this);
//
// HACK - under construction, does not include complexity or priorities
//
Renderer
*renderer;
SChainIteratorOf<Renderer*>
renderers_iterator(&renderers);
while ((renderer = renderers_iterator.ReadAndNext()) != NULL)
{
Check(renderer);
renderer->UpdateInterestOrigin(target_render_time);
}
Check_Fpu();
}
//
//#############################################################################
// Execute
//#############################################################################
//
void
RendererManager::Execute(
Time target_render_time,
Time,
Time
)
{
Check(this);
//
// HACK - under construction, does not include complexity or priorities
//
Renderer *renderer;
RendererComplexity complexity_update;
SChainIteratorOf<Renderer*> renderers_iterator(&renderers);
Time startRender = Now();
int renderItem = 0;
while ((renderer = renderers_iterator.ReadAndNext()) != NULL)
{
Check(renderer);
// Calculate current complexity
// HACK - use calibration complexity for now
Time startComplexity = Now();
complexity_update = renderer->GetCalibrationComplexity();
Time endComplexity = Now();
renderer->Execute(target_render_time, complexity_update);
renderItem++;
}
Time endRender = Now();
endRender = endRender;
Check_Fpu();
}
//
//#############################################################################
// ExecuteIdle
//#############################################################################
//
void RendererManager::ExecuteIdle()
{
Renderer *renderer;
SChainIteratorOf<Renderer*> renderers_iterator(&renderers);
while ((renderer = renderers_iterator.ReadAndNext()) != NULL)
{
Check(renderer);
renderer->ExecuteIdle();
}
Check_Fpu();
}
//
//#############################################################################
// PostRendererEvent
//#############################################################################
//
void
RendererManager::PostRendererEvent(
Entity *entity,
Receiver::Message *message
)
{
Check(this);
Check(entity);
Check(message);
Renderer
*renderer;
SChainIteratorOf<Renderer*>
renderers_iterator(&renderers);
while ((renderer = renderers_iterator.ReadAndNext()) != NULL)
{
Check(renderer);
Check(application);
Check(application->GetInterestManager());
application->GetInterestManager()->PostRendererEvent(
entity,
renderer,
message
);
}
}
//~~~~~~~~~~~~~~~~~ Renderer__NotifyOfNewInterestingEntityMessage ~~~~~~~~~~~~~
Renderer__NotifyOfNewInterestingEntityMessage::
Renderer__NotifyOfNewInterestingEntityMessage(
Entity *interesting_entity
):
Receiver::Message(
Renderer::NotifyOfNewInterestingEntityMessageID,
sizeof(Renderer__NotifyOfNewInterestingEntityMessage)
)
{
Check(interesting_entity);
entityID = interesting_entity->GetEntityID();
Check_Fpu();
}
Entity*
Renderer__NotifyOfNewInterestingEntityMessage::GetInterestingEntity()
{
Check(this);
Check(application);
Check(application->GetHostManager());
Check_Fpu();
return application->GetHostManager()->GetEntityPointer(entityID);
}
//~~~~~~~~~~~~~~ Renderer__NotifyOfBecomingUninterestingEntityMessage ~~~~~~~~~
Renderer__NotifyOfBecomingUninterestingEntityMessage::
Renderer__NotifyOfBecomingUninterestingEntityMessage(
Entity *uninteresting_entity
):
Receiver::Message(
Renderer::NotifyOfBecomingUninterestingEntityMessageID,
sizeof(Renderer__NotifyOfBecomingUninterestingEntityMessage)
)
{
uninterestingEntity = uninteresting_entity;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~ Renderer__EntityMessage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Renderer__EntityMessage::Renderer__EntityMessage(
Receiver::MessageID message_ID,
size_t message_size,
Entity *entity
):
Receiver::Message(
message_ID,
message_size
)
{
Check(entity);
entityID = entity->GetEntityID();
}
Entity*
Renderer__EntityMessage::GetEntity()
{
Check(this);
Check(application);
Check(application->GetHostManager());
return application->GetHostManager()->GetEntityPointer(entityID);
}
//~~~~~~~~~~~~~~~~~~ Renderer__StartEntityEffectMessage ~~~~~~~~~~~~~~~~~~~~~~~
Renderer__StartEntityEffectMessage::Renderer__StartEntityEffectMessage(
Entity *entity,
DamageZone *damage_zone,
ResourceDescription::ResourceID resource_ID
):
Renderer__EntityMessage(
Renderer::StartEntityEffectMessageID,
sizeof(Renderer__StartEntityEffectMessage),
entity
)
{
Check(damage_zone);
Verify(resource_ID != ResourceDescription::NullResourceID);
damageZone = damage_zone;
resourceID = resource_ID;
}
//~~~~~~~~~~~~~~~~~~ Renderer__StopAllEntityEffectsMessage ~~~~~~~~~~~~~~~~~~~~
Renderer__StopAllEntityEffectsMessage::Renderer__StopAllEntityEffectsMessage(
Entity *entity
):
Renderer__EntityMessage(
Renderer::StopAllEntityEffectsMessageID,
sizeof(Renderer__StopAllEntityEffectsMessage),
entity
)
{
}
//~~~~~~~~~~~~~~~~~~ Renderer__StartEntityAlarmMessage ~~~~~~~~~~~~~~~~~~~~~~~~
Renderer__StartEntityAlarmMessage::Renderer__StartEntityAlarmMessage(
Entity *entity,
Subsystem *alarm_subsystem,
Enumeration alarm_condition,
ResourceDescription::ResourceID resource_ID
):
Renderer__EntityMessage(
Renderer::StartEntityAlarmMessageID,
sizeof(Renderer__StartEntityAlarmMessage),
entity
)
{
Check(alarm_subsystem);
#if DEBUG_LEVEL>0
if (resource_ID == ResourceDescription::NullResourceID)
{
Dump(alarm_subsystem->GetClassID());
Dump(alarm_condition);
}
#endif
Warn(resource_ID == ResourceDescription::NullResourceID); // HACK - ECH Why is this sometimes NULL?
subsystem = alarm_subsystem;
condition = alarm_condition;
resourceID = resource_ID;
}
//~~~~~~~~~~~~~~~~~~ Renderer__StopEntityAlarmMessage ~~~~~~~~~~~~~~~~~~~~~~~~~
Renderer__StopEntityAlarmMessage::Renderer__StopEntityAlarmMessage(
Entity *entity,
Subsystem *alarm_subsystem,
Enumeration alarm_condition
):
Renderer__EntityMessage(
Renderer::StopEntityAlarmMessageID,
sizeof(Renderer__StopEntityAlarmMessage),
entity
)
{
Check(alarm_subsystem);
subsystem = alarm_subsystem;
condition = alarm_condition;
}
#ifdef TEST_CLASS
# include "renderer.tcp"
#endif