Files
BT411/engine/MUNGA/ENTITY.h
T
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

621 lines
14 KiB
C++

#pragma once
#include "simulate.h"
#include "origin.h"
#include "linmtrx.h"
class Player;
class NotationFile;
class DamageZone;
class Damage;
class Subsystem;
#define ENTITY_CONTINUATION
#include "entity2.h"
//##########################################################################
//########################### Entity #################################
//##########################################################################
class Entity:
public Simulation
{
friend class Entity__StaticVideoSocketIterator;
friend class Entity__DynamicVideoSocketIterator;
friend class Entity__AudioSocketIterator;
friend class Entity__GaugeSocketIterator;
friend class Entity__AudioLocationIterator;
//##########################################################################
// Shared Data support
//
public:
typedef Entity__SharedData SharedData;
SharedData*
GetSharedData();
static Derivation *GetClassDerivations();
static SharedData DefaultData;
//##########################################################################
// Message Support
//
public:
enum {
MakeMessageID = Simulation::NextMessageID,
MakeReadyMessageID,
RemakeEntityMessageID,
RemakeReadyMessageID,
RemakeCompleteMessageID,
RemakeIncompleteMessageID,
DestroyEntityMessageID,
TransferEntityMessageID,
TransferCompleteMessageID,
TransferIncompleteMessageID,
BecomeInterestingMessageID,
BecomeUninterestingMessageID,
UpdateMessageID,
SubscribeReplicantMessageID,
UnsubscribeReplicantMessageID,
TakeDamageMessageID,
TakeDamageStreamMessageID,
PlayerLinkMessageID,
NextMessageID
};
typedef Entity__Message Message;
typedef Entity__MakeMessage MakeMessage;
typedef Entity__MakeReadyMessage MakeReadyMessage;
typedef Entity__RemakeEntityMessage RemakeEntityMessage;
typedef Entity__RemakeReadyMessage RemakeReadyMessage;
typedef Entity__RemakeCompleteMessage RemakeCompleteMessage;
typedef Entity__RemakeIncompleteMessage RemakeIncompleteMessage;
typedef Entity__DestroyEntityMessage DestroyEntityMessage;
typedef Entity__TransferEntityMessage TransferEntityMessage;
typedef Entity__TransferCompleteMessage TransferCompleteMessage;
typedef Entity__TransferIncompleteMessage TransferIncompleteMessage;
typedef Entity__BecomeInterestingMessage BecomeInterestingMessage;
typedef Entity__BecomeUninterestingMessage BecomeUninterestingMessage;
typedef Entity__UpdateMessage UpdateMessage;
typedef Entity__SubscribeReplicantMessage SubscribeReplicantMessage;
typedef Entity__UnsubscribeReplicantMessage UnsubscribeReplicantMessage;
typedef Entity__TakeDamageMessage TakeDamageMessage;
typedef Entity__TakeDamageStreamMessage TakeDamageStreamMessage;
typedef Entity__PlayerLinkMessage PlayerLinkMessage;
typedef Entity__DynamicMessage DynamicMessage;
typedef Entity__MakeMapMessage MakeMapMessage;
void
Receive(Event *event);
void
LocalDispatch(Message *what)
{Receiver::Receive(Cast_Object(Receiver::Message*,what));}
void
Dispatch(Receiver::Message *what);
void
DispatchToReplicant(
Message *what,
HostID host
);
void
DispatchToReplicants(Message *what);
private:
static const HandlerEntry MessageHandlerEntries[];
protected:
//static MessageHandlerSet MessageHandlers;
static MessageHandlerSet& GetMessageHandlers();
//##########################################################################
// Attribute Support
//
public:
enum {
LocalToWorldAttributeID = Simulation::NextAttributeID,
LocalOriginAttributeID,
DamageZoneCountAttributeID,
DamageZonesAttributeID,
NextAttributeID
};
private:
static const IndexEntry AttributePointers[];
protected:
//static AttributeIndexSet AttributeIndex
static AttributeIndexSet& GetAttributeIndex();
//
// public attribute declarations go here
//
public:
LinearMatrix localToWorld;
Origin localOrigin;
int damageZoneCount;
DamageZone **damageZones;
int
GetDamageZoneIndex(const CString &damage_zone_name) const;
//
// virtual access
//
public:
virtual Vector3D
GetWorldLinearVelocity()
{return Vector3D(0.0f, 0.0f, 0.0f);}
virtual Vector3D
GetWorldLinearAcceleration()
{return Vector3D(0.0f, 0.0f, 0.0f);}
//##########################################################################
// Model Support
//
public:
UpdateMessage*
Execute(const Time &till);
Subsystem*
GetSubsystem(int index)
{
Check(this); Verify((unsigned)index < subsystemCount);
return subsystemArray[index];
}
Simulation*
GetSimulation(int index);
int
GetSubsystemCount()
{Check(this); return subsystemCount;}
Subsystem*
FindSubsystem(const char* name);
typedef void
(Entity::*Performance)(Scalar time_slice);
typedef Entity__UpdateRecord UpdateRecord;
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
enum {
EntitySubsystemID = -1
};
static int
FindSubsytemID(
const char *model_name,
const char *subsystem_name
);
public:
enum {
DamageZoneUpdateModelBit = Simulation::NextUpdateModelBit,
NextUpdateModelBit
};
enum {
DamageZoneUpdateModelFlag = 1 << DamageZoneUpdateModelBit
};
Logical DamageZoneUpdateModelFlagSet()
{
Check(this);
return ((updateModel & DamageZoneUpdateModelFlag) != 0);
}
protected:
void
WriteUpdateRecord(
Simulation::UpdateRecord *message,
int update_model
);
void
UpdateMessageHandler(UpdateMessage *message);
void
ReadUpdateRecord(Simulation::UpdateRecord *message);
void
ReadDamageUpdateRecord(Simulation::UpdateRecord *update_record);
void
WriteDamageUpdateRecord(Simulation::UpdateRecord *update_record);
void
PerformAndWatch(
const Time &till,
MemoryStream *update_stream
);
int
subsystemCount;
Subsystem
**subsystemArray;
Origin
updateOrigin;
void
TakeDamageStreamMessageHandler(TakeDamageStreamMessage *message);
void
TakeDamageMessageHandler(TakeDamageMessage *message);
void
PlayerLinkMessageHandler(PlayerLinkMessage *message);
//##########################################################################
// Renderer Support
//
public:
void
AddStaticVideoComponent(Component *component);
void
AddDynamicVideoComponent(Component *component);
void
AddAudioComponent(Component *component);
typedef Entity__StaticVideoSocketIterator
StaticVideoSocketIterator;
typedef Entity__DynamicVideoSocketIterator
DynamicVideoSocketIterator;
typedef Entity__AudioSocketIterator
AudioSocketIterator;
virtual Enumeration
GetAudioRepresentation(Entity *linked_entity);
private:
SChainOf<Component*>
staticVideoSocket;
SChainOf<Component*>
dynamicVideoSocket;
SChainOf<Component*>
audioSocket;
//##########################################################################
// Flag Support
//
public:
//
// MasterInstance - created on owning host, replicated on remote hosts,
// sends updates to replicants
// ReplicantInstance - created on remote hosts, receives updates from
// MasterInstance
// IndependantInstance - created on owning host, replicated on remote
// hosts, never receives updates from MasterInstance
// HermitInstance - created on owning host, is not replicated, does
// send updates
//
enum {
InstanceBits = Simulation::NextBit,
ValidBit = InstanceBits+2,
TransferableBit,
InterestBit,
InterestLockedBit,
DynamicBit,
TrappedBit,
StatueBit,
MapBit,
PreRunBit,
CondemnedBit,
NextBit
};
enum Instance
{
MasterInstance=0,
ReplicantInstance=1<<InstanceBits,
IndependantInstance=2<<InstanceBits,
HermitInstance=3<<InstanceBits
};
enum {
InstanceMask = HermitInstance,
ValidFlag = 1<<ValidBit,
TransferableFlag = 1<<TransferableBit,
InterestFlag = 1<<InterestBit,
InterestLockedFlag = 1<<InterestLockedBit,
DynamicFlag = 1<<DynamicBit,
TrappedFlag = 1<<TrappedBit,
StatueFlag = 1<<StatueBit,
MapFlag = 1<<MapBit,
PreRunFlag = 1<<PreRunBit,
CondemnedFlag = 1<<CondemnedBit,
TypeMask = DynamicFlag|TrappedFlag|MapFlag,
// DefaultFlags = MasterInstance
DefaultFlags = DynamicFlag|MasterInstance
};
enum Type {
StaticType = 0,
DynamicType = DynamicFlag,
TrappedType = DynamicFlag|TrappedFlag,
StatueType = DynamicFlag|TrappedFlag|StatueFlag,
MapType = DynamicFlag|MapFlag,
TrappedMapType = DynamicFlag|TrappedFlag|MapFlag,
};
//##########################################################################
// Instance Type support
//
public:
void
SetInstance(Instance type)
{Check(this); simulationFlags &= ~InstanceMask; simulationFlags |= type;}
Instance
GetInstance()
{Check(this); return (Instance)(simulationFlags&InstanceMask);}
static LWord
EntityFlagsSetInstance(LWord entity_flags, Instance type)
{
entity_flags &= ~InstanceMask;
entity_flags |= type;
return entity_flags;
}
static Instance
EntityFlagsGetInstance(LWord entity_flags)
{return (Instance)(entity_flags&InstanceMask);}
//##########################################################################
// Validity Support
//
public:
void
SetValidFlag()
{Check(this); simulationFlags |= ValidFlag;}
void
SetInvalidFlag()
{Check(this); simulationFlags &= ~ValidFlag;}
Logical
IsValid()
{Check(this); return (simulationFlags&ValidFlag) != 0;}
void
SetPreRunFlag()
{Check(this); simulationFlags |= PreRunFlag;}
void
SetNoPreRunFlag()
{Check(this); simulationFlags &= ~PreRunFlag;}
Logical
IsPreRunnable()
{Check(this); return (simulationFlags&PreRunFlag) != 0;}
void
SetCondemnedFlag()
{Check(this); simulationFlags |= CondemnedFlag;}
Logical
IsCondemned()
{Check(this); return (simulationFlags&CondemnedFlag) != 0;}
//##########################################################################
// Transference Support
//
public:
void
SetTransferableFlag()
{Check(this); simulationFlags |= TransferableFlag;}
void
SetNontransferableFlag()
{Check(this); simulationFlags &= ~TransferableFlag;}
Logical
IsTransferable()
{Check(this); return (simulationFlags&TransferableFlag) != 0;}
const EntityID&
GetEntityID()
{return entityID;}
HostID
GetOwnerID()
{return ownerID;}
EntityID
entityID;
HostID
ownerID;
Player
*playerLink;
Player*
GetPlayerLink()
{return playerLink;}
//##########################################################################
// Interest Support
//
public:
void
BecomeInteresting();
void
BecomeInterestingMessageHandler(const Receiver::Message*);
void
BecomeUninterestingMessageHandler();
Logical
IsInteresting()
{Check(this); return interestCount != 0;}
void
SetInterestLockedFlag();
void
SetInterestUnlockedFlag();
Logical
IsInterestLocked()
{Check(this); return (simulationFlags&InterestLockedFlag) != 0;}
void
SetInterestZoneID(InterestZoneID interest_zone_ID)
{interestZoneID = interest_zone_ID;}
InterestZoneID
GetInterestZoneID()
{return interestZoneID;}
Logical
IsStatic()
{Check(this); return (simulationFlags&DynamicFlag) == 0;}
Logical
IsDynamic()
{Check(this); return (simulationFlags&DynamicFlag) != 0;}
void
SetTrappedFlag()
{Check(this); simulationFlags |= TrappedFlag;}
Logical
IsTrapped()
{Check(this); return (simulationFlags&TrappedFlag) != 0;}
Logical
IsStatue()
{Check(this); return (simulationFlags&StatueFlag) != 0;}
Logical
IsMap()
{Check(this); return (simulationFlags&MapFlag) != 0;}
static Logical
EntityFlagsIsMap(LWord entity_flags)
{return (entity_flags&MapFlag) != 0;}
Type
GetType()
{Check(this); return (Type)(simulationFlags&TypeMask);}
virtual Enumeration
GetInterestPriority(Entity *linked_entity);
InterestZoneID
interestZoneID;
int
interestCount;
Time
creationTime;
//##########################################################################
// Camera Support
//
protected:
Origin
cameraOffset;
public:
Origin
GetCameraOffset() const
{Check(this); return cameraOffset; }
//##########################################################################
// Scoring support
//
public:
//
// HACK - ECH 7/6/95 - Allow the player vehicle to respond to score
// messages, allows attribute system to be used for scoring
//
virtual void
RespondToScoreMessage(Message *message) {}
//##########################################################################
// Construction and Destruction
//
public:
typedef Entity* (*MakeHandler)(MakeMessage *);
static Entity*
Make(MakeMessage *creation_message);
//
// Warning... This function requires a properly set up strtok!!!!
//
static Logical
CreateMakeMessage(
MakeMessage *creation_message,
NotationFile *model_file,
const ResourceDirectories *directories
);
static Logical
CreateMakeMapMessage(
MakeMapMessage *creation_message,
ResourceDescription::ResourceID resource_id,
NotationFile *model_file,
const ResourceDirectories * //directories
);
static ResourceDescription::ResourceID
CreateDamageZoneStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
static ResourceDescription::ResourceID
CreateExplosionTableStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
);
ResourceDescription::ResourceID
GetResourceID()
{Check(this); return resourceID;}
Player*
GetOwningPlayer()
{Check(this); return owningPlayer;}
void
SetupNetworkMessage(Entity::Message *message);
protected:
Entity(
MakeMessage *creation_message,
SharedData &virtual_data
);
ResourceDescription::ResourceID
resourceID;
Player
*owningPlayer;
void
DestroyEntityMessageHandler(Message *message);
public:
~Entity();
void
CondemnToDeathRow();
//##########################################################################
// Test Support
//
public:
Logical
TestInstance() const;
static Logical
TestClass();
};
inline void
Entity::SetupNetworkMessage(Entity::Message *message)
{
Check(this);
Check(message);
message->entityID = entityID;
message->interestZoneID = interestZoneID;
message->ownerID = ownerID;
}
#include "entity3.h"
#undef ENTITY_CONTINUATION