BT410 Phase 5.3.23: the cylinder hit-location table -- unaimed hits land WHERE THEY STRIKE
DMGTABLE born (type-29 streams, BT411 byte-verified format): the height x angle grid -- DamageLookupTable rows by height, PieSlice cells by angle (rotate-with-torso rows add the live twist), DamageZonePercentTable = the cumulative hit-distribution roll (first threshold above the roll -- the BattleTech dice scatter). Loaded in the Mech ctor by the DamageZoneStream member's NAME (mech+0x444, resident); Mech::TakeDamageMessageHandler resolves invalidDamageZone hits through it (binary @0x4a0264 tail). The missile fuse becomes the authentic unaimed producer: zone -1 + the round's world position as the impact point (ENTITY3.HPP: "damage zones are only valid via reticle based weapons"). Fight-verified on the authentic 7-row bhk1 table: flat-flying SRMs strike low -> row 0 -> FEET AND LEG zones, side-correct by impact angle (+x right, -x left), identical geometry spread across the cell distribution by the roll. 17/17 missile lifecycle, zero faults; smoke + novice green. Also fixed: the ctor's reservedState zero-loop counted past the shrunken array. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
//===========================================================================//
|
||||
// File: dmgtable.cpp //
|
||||
// Project: BattleTech Brick: Mech damage //
|
||||
// Contents: The cylinder hit-location table (unaimed damage -> zone) //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
//
|
||||
// RECONSTRUCTED from the binary via the BT411 reversal (byte-verified
|
||||
// against the real type-29 streams, 18 tables). STREAM FORMAT:
|
||||
//
|
||||
// Table { i32 rowCount; Row[rowCount] }
|
||||
// Row { i32 rotateWithTorso; i32 cellCount; Cell[cellCount] }
|
||||
// Cell { i32 nameLen; char name[nameLen]; u8 0; i32 entryCount;
|
||||
// Entry[entryCount] }
|
||||
// Entry { f32 cumulativeThreshold; i32 zoneIndex }
|
||||
//
|
||||
// Real sample (ava1, 7 rows x 8 cells): rows 0-2 fixed to the chassis
|
||||
// (feet/legs), rows 3-6 rotate with the torso; a foot cell =
|
||||
// {0.5->16, 0.8->10, 1.0->5}.
|
||||
//
|
||||
|
||||
#include <bt.hpp>
|
||||
#pragma hdrstop
|
||||
|
||||
#if !defined(DMGTABLE_HPP)
|
||||
# include <dmgtable.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(RANDOM_HPP)
|
||||
# include <random.hpp>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
static const Scalar kTwoPi = 6.2831855f; // _DAT_0049e810
|
||||
static const Scalar kDegenerateEps = 1.0e-4f; // _DAT_0049ed04
|
||||
|
||||
//
|
||||
// STAGED: the binary reads the mech's collision-cylinder height
|
||||
// (mech+0x2ec -> +0xc); the collision volume isn't reconstructed yet, so a
|
||||
// fixed standing height maps impacts onto the rows. (bhk1 torso mounts sit
|
||||
// at y~4; 10u spans feet->cockpit plausibly.)
|
||||
//
|
||||
static const Scalar kStagedMechHeight = 10.0f;
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DamageZonePercentTable -- one angular cell (binary ctor @0049deb0; the
|
||||
// entry fill @0049e5e4). The cell NAME string ([i32 len][chars][NUL]) is
|
||||
// consumed and discarded, exactly as the binary does.
|
||||
//#############################################################################
|
||||
//
|
||||
DamageZonePercentTable::DamageZonePercentTable(MemoryStream *stream)
|
||||
{
|
||||
Check(stream);
|
||||
|
||||
int name_length = 0;
|
||||
*stream >> name_length;
|
||||
if (name_length > 0)
|
||||
{
|
||||
stream->AdvancePointer(name_length);
|
||||
}
|
||||
stream->AdvancePointer(1); // the NUL
|
||||
|
||||
*stream >> entryCount;
|
||||
thresholds = NULL;
|
||||
zoneIndices = NULL;
|
||||
if (entryCount > 0)
|
||||
{
|
||||
thresholds = new Scalar[entryCount];
|
||||
Register_Pointer(thresholds);
|
||||
zoneIndices = new int[entryCount];
|
||||
Register_Pointer(zoneIndices);
|
||||
for (int ee = 0; ee < entryCount; ++ee)
|
||||
{
|
||||
*stream >> thresholds[ee];
|
||||
*stream >> zoneIndices[ee];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DamageZonePercentTable::~DamageZonePercentTable()
|
||||
{
|
||||
if (thresholds != NULL)
|
||||
{
|
||||
Unregister_Pointer(thresholds);
|
||||
delete [] thresholds;
|
||||
}
|
||||
if (zoneIndices != NULL)
|
||||
{
|
||||
Unregister_Pointer(zoneIndices);
|
||||
delete [] zoneIndices;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// SelectZone (binary @0049de14): uniform roll, first cumulative threshold
|
||||
// ABOVE the roll wins (entries ascend); the last entry backstops.
|
||||
//#############################################################################
|
||||
//
|
||||
int
|
||||
DamageZonePercentTable::SelectZone()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (entryCount <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
Scalar roll = (Scalar)Random;
|
||||
for (int ee = 0; ee < entryCount; ++ee)
|
||||
{
|
||||
if (thresholds[ee] > roll)
|
||||
{
|
||||
return zoneIndices[ee];
|
||||
}
|
||||
}
|
||||
return zoneIndices[entryCount - 1];
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// PieSlice -- one height row (binary ctor @0049e740).
|
||||
//#############################################################################
|
||||
//
|
||||
PieSlice::PieSlice(Mech *mech, MemoryStream *stream)
|
||||
{
|
||||
Check(mech);
|
||||
Check(stream);
|
||||
|
||||
owner = mech;
|
||||
*stream >> rotateWithTorso;
|
||||
*stream >> cellCount;
|
||||
cells = NULL;
|
||||
if (cellCount > 0)
|
||||
{
|
||||
cells = new DamageZonePercentTable *[cellCount];
|
||||
Register_Pointer(cells);
|
||||
for (int cc = 0; cc < cellCount; ++cc)
|
||||
{
|
||||
cells[cc] = new DamageZonePercentTable(stream);
|
||||
Register_Object(cells[cc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PieSlice::~PieSlice()
|
||||
{
|
||||
if (cells != NULL)
|
||||
{
|
||||
for (int cc = 0; cc < cellCount; ++cc)
|
||||
{
|
||||
Unregister_Object(cells[cc]);
|
||||
delete cells[cc];
|
||||
}
|
||||
Unregister_Pointer(cells);
|
||||
delete [] cells;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// SelectSlice (binary @0049e678): rotate-with-torso rows add the LIVE torso
|
||||
// twist to the impact angle (an upper-body ring follows the twist; legs
|
||||
// stay chassis-fixed), wrap to [0,2pi), pick the cell by angular fraction.
|
||||
//#############################################################################
|
||||
//
|
||||
DamageZonePercentTable*
|
||||
PieSlice::SelectSlice(Scalar angle)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (cellCount <= 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Scalar a = angle;
|
||||
if (rotateWithTorso)
|
||||
{
|
||||
a += owner->CurrentTorsoTwist();
|
||||
while (a >= kTwoPi)
|
||||
{
|
||||
a -= kTwoPi;
|
||||
}
|
||||
while (a < 0.0f)
|
||||
{
|
||||
a += kTwoPi;
|
||||
}
|
||||
}
|
||||
|
||||
int index = (int)((Scalar)cellCount * a / kTwoPi);
|
||||
if (index < 0)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
if (index >= cellCount)
|
||||
{
|
||||
index = cellCount - 1;
|
||||
}
|
||||
return cells[index];
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DamageLookupTable (binary ctor @0049ea48).
|
||||
//#############################################################################
|
||||
//
|
||||
DamageLookupTable::DamageLookupTable(Mech *mech, MemoryStream *stream)
|
||||
{
|
||||
Check(mech);
|
||||
Check(stream);
|
||||
|
||||
owner = mech;
|
||||
*stream >> rowCount;
|
||||
rows = NULL;
|
||||
if (rowCount > 0)
|
||||
{
|
||||
rows = new PieSlice *[rowCount];
|
||||
Register_Pointer(rows);
|
||||
for (int rr = 0; rr < rowCount; ++rr)
|
||||
{
|
||||
rows[rr] = new PieSlice(mech, stream);
|
||||
Register_Object(rows[rr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DamageLookupTable::~DamageLookupTable()
|
||||
{
|
||||
if (rows != NULL)
|
||||
{
|
||||
for (int rr = 0; rr < rowCount; ++rr)
|
||||
{
|
||||
Unregister_Object(rows[rr]);
|
||||
delete rows[rr];
|
||||
}
|
||||
Unregister_Pointer(rows);
|
||||
delete [] rows;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ResolveHit (binary @0049eb54 + the glue @0049ed0c): world impact ->
|
||||
// mech-local; clamp local height onto the cylinder -> row; impact angle =
|
||||
// atan2(z, x) wrapped [0,2pi) (a degenerate on-axis hit rolls a random
|
||||
// angle); row/angle -> cell -> the distribution roll -> a hull zone.
|
||||
//#############################################################################
|
||||
//
|
||||
int
|
||||
DamageLookupTable::ResolveHit(const Point3D &world_impact)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (rowCount <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// World -> mech-local.
|
||||
//
|
||||
LinearMatrix to_world;
|
||||
to_world = owner->localToWorld;
|
||||
LinearMatrix to_local;
|
||||
to_local.Invert(to_world);
|
||||
Point3D local;
|
||||
local.Multiply(world_impact, to_local);
|
||||
|
||||
//
|
||||
// Height -> row.
|
||||
//
|
||||
Scalar height = kStagedMechHeight;
|
||||
Scalar y = local.y;
|
||||
if (y < 0.0f)
|
||||
{
|
||||
y = 0.0f;
|
||||
}
|
||||
if (y > height)
|
||||
{
|
||||
y = height;
|
||||
}
|
||||
int row = (int)((Scalar)rowCount * y / height);
|
||||
if (row < 0)
|
||||
{
|
||||
row = 0;
|
||||
}
|
||||
if (row >= rowCount)
|
||||
{
|
||||
row = rowCount - 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Impact angle.
|
||||
//
|
||||
Scalar angle;
|
||||
Scalar ax = (local.x < 0.0f) ? -local.x : local.x;
|
||||
Scalar az = (local.z < 0.0f) ? -local.z : local.z;
|
||||
if (ax > kDegenerateEps || az > kDegenerateEps)
|
||||
{
|
||||
angle = (Scalar)atan2((double)local.z, (double)local.x);
|
||||
if (angle < 0.0f)
|
||||
{
|
||||
angle += kTwoPi;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = (Scalar)Random * kTwoPi;
|
||||
}
|
||||
|
||||
DamageZonePercentTable *cell = rows[row]->SelectSlice(angle);
|
||||
int zone = (cell != NULL) ? cell->SelectZone() : -1;
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[cyl] impact local=(" << local.x
|
||||
<< "," << local.y << "," << local.z
|
||||
<< ") row=" << row << "/" << rowCount
|
||||
<< " angle=" << angle
|
||||
<< " -> zone " << zone << endl << flush;
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//===========================================================================//
|
||||
// File: dmgtable.hpp //
|
||||
// Project: BattleTech //
|
||||
// Contents: The cylinder hit-location table (unaimed damage -> zone) //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
//
|
||||
// STAGED header -- no dmgtable.hpp survives; the interface is reconstructed
|
||||
// from the binary (BT411, byte-verified vs the real type-29 .RES streams):
|
||||
// a passive nested height x angle grid of three container classes,
|
||||
//
|
||||
// DamageLookupTable (binary ctor @0049ea48, vtable @0050bd84, 0x2c)
|
||||
// -> PieSlice ROWS by height (@0049e740, @0050bd90, 0x30)
|
||||
// -> DamageZonePercentTable CELLS by angle (@0049deb0, @0050bd9c, 0x2c)
|
||||
// -> the cumulative hit-distribution (roll -> zone index).
|
||||
//
|
||||
// Class names follow the BT411 reconstruction (no 1995 names survive).
|
||||
// The binary keys rows/cells into refcounted keyed lists; direct-indexed
|
||||
// arrays are equivalent (the float key (i+1)*2pi/count === cells[i]).
|
||||
//
|
||||
|
||||
#if !defined(DMGTABLE_HPP)
|
||||
# define DMGTABLE_HPP
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
|
||||
//###########################################################################
|
||||
//##################### DamageZonePercentTable ##########################
|
||||
//###########################################################################
|
||||
|
||||
//
|
||||
// One angular CELL: the cumulative hit-distribution. Entries ascend;
|
||||
// SelectZone (binary @0049de14) rolls uniform [0,1) and returns the
|
||||
// first entry whose threshold exceeds the roll -- the classic
|
||||
// BattleTech dice hit-scatter.
|
||||
//
|
||||
class DamageZonePercentTable
|
||||
{
|
||||
public:
|
||||
DamageZonePercentTable(MemoryStream *stream);
|
||||
~DamageZonePercentTable();
|
||||
|
||||
int
|
||||
SelectZone();
|
||||
|
||||
int entryCount;
|
||||
Scalar *thresholds; // cumulative, ascending
|
||||
int *zoneIndices; // -> Entity::damageZones[]
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//############################ PieSlice #################################
|
||||
//###########################################################################
|
||||
|
||||
//
|
||||
// One height ROW: the ring of angular cells. rotateWithTorso rows
|
||||
// (upper body) add the live torso twist to the impact angle before the
|
||||
// cell pick (binary @0049e678).
|
||||
//
|
||||
class PieSlice
|
||||
{
|
||||
public:
|
||||
PieSlice(Mech *mech, MemoryStream *stream);
|
||||
~PieSlice();
|
||||
|
||||
DamageZonePercentTable*
|
||||
SelectSlice(Scalar angle);
|
||||
|
||||
Mech *owner;
|
||||
int rotateWithTorso;
|
||||
int cellCount;
|
||||
DamageZonePercentTable
|
||||
**cells;
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//######################## DamageLookupTable ############################
|
||||
//###########################################################################
|
||||
|
||||
//
|
||||
// The table: rows stacked by height over the mech's cylinder.
|
||||
// ResolveHit (binary @0049eb54 + glue @0049ed0c) maps a WORLD impact
|
||||
// point -> mech-local -> (height row, impact angle) -> cell -> the
|
||||
// distribution roll -> a hull zone index.
|
||||
//
|
||||
class DamageLookupTable
|
||||
{
|
||||
public:
|
||||
DamageLookupTable(Mech *mech, MemoryStream *stream);
|
||||
~DamageLookupTable();
|
||||
|
||||
int
|
||||
ResolveHit(const Point3D &world_impact);
|
||||
|
||||
Mech *owner;
|
||||
int rowCount;
|
||||
PieSlice
|
||||
**rows;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
# DMGTABLE.CPP — the cylinder hit-location table
|
||||
|
||||
**Status: RECONSTRUCTED (5.3.23) — loads the authentic type-29 streams and
|
||||
resolves unaimed hits live (fight-verified).**
|
||||
|
||||
No 1995 header survives; class names (DamageLookupTable / PieSlice /
|
||||
DamageZonePercentTable) follow the BT411 reversal, which byte-verified the
|
||||
stream against the real .RES (18 tables). Binary anatomy: TABLE ctor
|
||||
@0049ea48 (vtable @0050bd84) → ROW @0049e740 (@0050bd90) → CELL @0049deb0
|
||||
(@0050bd9c); lookup @0049eb54 (height→row, atan2 angle) + @0049e678
|
||||
(rotate-with-torso, angle→cell) + @0049de14 (the cumulative-distribution
|
||||
roll); glue @0049ed0c. The binary's refcounted keyed lists are replaced by
|
||||
direct-indexed arrays (the float key (i+1)·2π/count ≡ cells[i]).
|
||||
|
||||
Stream (type 29, found by the mech's DamageZoneStream member NAME):
|
||||
`Table{i32 rows; Row[]} Row{i32 rotateWithTorso; i32 cells; Cell[]}
|
||||
Cell{i32 nameLen; chars; NUL; i32 entries; {f32 cumThreshold; i32 zone}[]}`.
|
||||
|
||||
Wiring: the Mech ctor Pass-3 tail loads it (mech+0x444, kept locked);
|
||||
Mech::TakeDamageMessageHandler resolves `invalidDamageZone` hits through it
|
||||
before chaining the base. The MISSILE fuse is the authentic unaimed
|
||||
producer: zone −1 + the round's world position as impactPoint (the
|
||||
ENTITY3.HPP warning: "damage zones are only valid via reticle based
|
||||
weapons"); victims without a table keep the interim random pick.
|
||||
|
||||
Verified (bhk1, 7 rows): flat-flying SRMs strike low → row 0 → feet/leg
|
||||
zones, side-correct by impact angle (+x hits right, −x left), identical
|
||||
geometry spread across the cell's distribution by the roll. Zero faults.
|
||||
|
||||
STAGED: mech cylinder height = 10.0 constant (binary reads the collision
|
||||
cylinder mech+0x2ec→+0xc — not reconstructed); torso twist feeds
|
||||
rotate-with-torso rows via Mech::CurrentTorsoTwist (Torso::CurrentTwist).
|
||||
+1153
-1077
File diff suppressed because it is too large
Load Diff
+468
-455
@@ -1,455 +1,468 @@
|
||||
//===========================================================================//
|
||||
// File: mech.hpp //
|
||||
// Project: BattleTech Brick: Entity Manager //
|
||||
// Contents: Implementation details for the Mech entity //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# define MECH_HPP
|
||||
|
||||
# if !defined(JMOVER_HPP)
|
||||
# include <jmover.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(RETICLE_HPP)
|
||||
# include <reticle.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(ALARM_HPP)
|
||||
# include <alarm.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(STATE_HPP)
|
||||
# include <state.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CHAIN_HPP)
|
||||
# include <chain.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(AVERAGE_HPP)
|
||||
# include <average.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CSTR_HPP)
|
||||
# include <cstr.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(SEQCTL_HPP)
|
||||
# include <seqctl.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(NAMEFILT_HPP)
|
||||
# include <namefilt.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(ROTATION_HPP)
|
||||
# include <rotation.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class Subsystem;
|
||||
class SubsystemMessageManager;
|
||||
class ControlsMapping;
|
||||
class PlatformTool;
|
||||
class MechControlsMapper;
|
||||
class Mech__DamageZone;
|
||||
class Joint;
|
||||
|
||||
//###########################################################################
|
||||
//######################### Mech Model Resource #########################
|
||||
//###########################################################################
|
||||
|
||||
struct Mech__ModelResource:
|
||||
public JointedMover::ModelResource
|
||||
{
|
||||
char animationPrefix[4];
|
||||
Scalar maxAcceleration;
|
||||
Scalar superStopAcceleration;
|
||||
Scalar throttleAdjustment;
|
||||
Scalar lookLeftAngle;
|
||||
Scalar lookRightAngle;
|
||||
Scalar lookFrontAngle;
|
||||
Scalar lookBackAngle;
|
||||
Scalar walkingTurnRate;
|
||||
Scalar runningTurnRate;
|
||||
Scalar reticleX;
|
||||
Scalar reticleY;
|
||||
Scalar relativeMechValue;
|
||||
int deathEffectResourceID;
|
||||
Scalar deathSplashDamage;
|
||||
Scalar deathSplashRadius;
|
||||
Scalar maxUnstableAcceleration;
|
||||
Scalar unstableAccelerationEffect;
|
||||
Scalar unstableGunTheEngineEffect;
|
||||
Scalar unstableSuperStopEffect;
|
||||
Scalar unstableHighVelocityEffect;
|
||||
Scalar unstableStopedTurnEffect;
|
||||
Scalar updatePositionDiffrence;
|
||||
Scalar updateTurnVelocityDiffrence;
|
||||
Scalar updateTurnDegreeDiffrence;
|
||||
Scalar timeDelay;
|
||||
Vector3D cameraOffset;
|
||||
char shadowJointName[20];
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//########################## Mech Make Message ##########################
|
||||
//###########################################################################
|
||||
|
||||
class Mech__MakeMessage:
|
||||
public JointedMover::MakeMessage
|
||||
{
|
||||
public:
|
||||
char resourceNameA[20];
|
||||
char resourceNameB[20];
|
||||
char resourceNameC[20];
|
||||
|
||||
Mech__MakeMessage(
|
||||
Receiver::MessageID message_ID,
|
||||
size_t length,
|
||||
const EntityID &entity_ID,
|
||||
Entity::ClassID class_ID,
|
||||
const EntityID &owner_ID,
|
||||
ResourceDescription::ResourceID resource_ID,
|
||||
LWord instance_flags,
|
||||
const Origin &origin,
|
||||
const Motion &velocity,
|
||||
const Motion &acceleration,
|
||||
const char *badge,
|
||||
const char *color,
|
||||
const char *patch
|
||||
):
|
||||
JointedMover::MakeMessage(
|
||||
message_ID, length, entity_ID, class_ID, owner_ID,
|
||||
resource_ID, instance_flags, origin, velocity, acceleration
|
||||
)
|
||||
{
|
||||
Str_Copy(resourceNameA, badge, sizeof(resourceNameA));
|
||||
Str_Copy(resourceNameB, color, sizeof(resourceNameB));
|
||||
Str_Copy(resourceNameC, patch, sizeof(resourceNameC));
|
||||
}
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//############################## Mech ###############################
|
||||
//###########################################################################
|
||||
|
||||
class Mech:
|
||||
public JointedMover
|
||||
{
|
||||
//
|
||||
// The per-zone damage code walks the roster / segment table / alarms
|
||||
// directly (the 1995 arrangement, mirrored by the reconstruction).
|
||||
//
|
||||
friend class Mech__DamageZone;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const HandlerEntry
|
||||
MessageHandlerEntries[];
|
||||
static MessageHandlerSet
|
||||
MessageHandlers;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Test Class Support
|
||||
//
|
||||
public:
|
||||
static Logical
|
||||
TestClass(Mech &);
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
typedef Mech__ModelResource ModelResource;
|
||||
typedef Mech__MakeMessage MakeMessage;
|
||||
typedef Mech__DamageZone DamageZone;
|
||||
|
||||
static Mech*
|
||||
Make(MakeMessage *creation_message);
|
||||
|
||||
Mech(
|
||||
MakeMessage *creation_message,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
~Mech();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// App interface
|
||||
//
|
||||
public:
|
||||
void
|
||||
SetMappingSubsystem(Subsystem *mapper);
|
||||
|
||||
//
|
||||
// Resolve a skeleton joint by name (the shared resolver the subsystems
|
||||
// use to bind their animated joints -- Torso twist, etc.):
|
||||
// GetSegment(name) -> segment jointIndex -> JointSubsystem::GetJoint.
|
||||
//
|
||||
Joint*
|
||||
ResolveJoint(const char *joint_name);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame simulation (the mech's body Performance; installed by the ctor
|
||||
// and dispatched each frame from Simulation::PerformAndWatch).
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
(Mech::*Performance)(Scalar time_slice);
|
||||
void
|
||||
SetPerformance(Performance performance)
|
||||
{
|
||||
Check(this);
|
||||
activePerformance = (Simulation::Performance)performance;
|
||||
}
|
||||
|
||||
void
|
||||
Simulate(Scalar time_slice);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Resource creation
|
||||
//
|
||||
public:
|
||||
static void
|
||||
CreateMakeMessage(
|
||||
MakeMessage *creation_message,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories
|
||||
);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateModelResource(
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories,
|
||||
ModelResource *model = 0
|
||||
);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateSubsystemStream(
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
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
|
||||
CreateSkeletonStream(
|
||||
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
|
||||
);
|
||||
|
||||
typedef Logical (*FindNameFunction)(
|
||||
const char *control_name, ControlsMapping *mapping);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateControlMappingStream(
|
||||
const char *mapping_name,
|
||||
NotationFile *mapping_file,
|
||||
FindNameFunction find_name,
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories,
|
||||
PlatformTool *current_tool
|
||||
);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Subsystem roster accessors (the roster itself -- subsystemArray /
|
||||
// subsystemCount -- lives in the base Entity)
|
||||
//
|
||||
public:
|
||||
Subsystem*
|
||||
GetGyroSubsystem() { Check(this); return gyroSubsystem; }
|
||||
Subsystem*
|
||||
GetTorsoSubsystem() { Check(this); return sinkSourceSubsystem; }
|
||||
Subsystem*
|
||||
GetHudSubsystem() { Check(this); return hudSubsystem; }
|
||||
Subsystem*
|
||||
GetSensorSubsystem() { Check(this); return sensorSubsystem; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Locomotion parameters (read by MechControlsMapper::InterpretControls to
|
||||
// shape the demands, and by the drive in Simulate). reverseStrideLength is
|
||||
// the top/run cycle speed the throttle scales (the naming is the 1995
|
||||
// field's; LoadLocomotionClips measures it from the run clips), walkStride
|
||||
// Length the walk speed. Turn rates are radians/sec.
|
||||
//
|
||||
public:
|
||||
Scalar GetReverseStrideLength() const { Check(this); return reverseStrideLength; }
|
||||
Scalar GetWalkStrideLength() const { Check(this); return walkStrideLength; }
|
||||
Scalar GetForwardThrottleScale() const { Check(this); return forwardThrottleScale; }
|
||||
void SetForwardThrottleScale(Scalar s){ Check(this); forwardThrottleScale = s; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Eyepoint / aim-ray composition. The pilot's torso-elevation aim (stick
|
||||
// pitch) does NOT tilt any skeleton joint on this mech family -- it pitches
|
||||
// the COCKPIT EYE and the weapon boresight (pixel-calibrated in the BT411
|
||||
// reverse-engineering: "pitch does not work" turned out to mean nothing
|
||||
// consumed Torso's currentElevation, not that a joint was un-animated).
|
||||
// Composed each frame in Simulate; DPLEyeRenderable / the aim ray read it.
|
||||
//
|
||||
public:
|
||||
const EulerAngles&
|
||||
GetEyepointRotation() const { Check(this); return eyepointRotation; }
|
||||
|
||||
//
|
||||
// The current target (the binary's mech target slot @0x388): read by
|
||||
// the weapons' HasActiveTarget / UpdateTargeting and the fire path.
|
||||
// The authentic writer is the reticle targeting step (mech4, the
|
||||
// render wave); the dev harness sets it directly.
|
||||
//
|
||||
Entity*
|
||||
GetTargetEntity() const { Check(this); return targetEntity; }
|
||||
void
|
||||
SetTargetEntity(Entity *target) { Check(this); targetEntity = target; }
|
||||
|
||||
//
|
||||
// Damage entry (overrides the Entity handler): latches the attacker
|
||||
// (mech+0x43c, read by the zone LOD router) and feeds the gyro
|
||||
// cockpit bounce before chaining to the base zone routing. The
|
||||
// cylinder unaimed-hit resolver (type-0x1d table) is a later wave.
|
||||
//
|
||||
void
|
||||
TakeDamageMessageHandler(TakeDamageMessage *message);
|
||||
|
||||
//
|
||||
// Damage-side death flag: the body graphic alarm (statusAlarm,
|
||||
// binary @0x714) at level >= 9 = the death/fall state.
|
||||
//
|
||||
Logical
|
||||
IsMechDestroyed() { Check(this); return statusAlarm.GetLevel() >= 9; }
|
||||
|
||||
//
|
||||
// Look-state commit (called by the controls mapper on a look-button
|
||||
// state change): re-aim the eyepoint from the model's authored look
|
||||
// angles. look_state = MechControlsMapper::LookState.
|
||||
//
|
||||
void
|
||||
CommitLookState(int look_state);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data
|
||||
//
|
||||
protected:
|
||||
//
|
||||
// Cached subsystem back-pointers (also held in the base roster).
|
||||
//
|
||||
Subsystem *sensorSubsystem;
|
||||
Subsystem *gyroSubsystem;
|
||||
Subsystem *sinkSourceSubsystem; // the real Torso
|
||||
Subsystem *hudSubsystem;
|
||||
SubsystemMessageManager *messageManager;
|
||||
int weaponCount;
|
||||
|
||||
//
|
||||
// Capability sub-rosters (Socket views onto the subsystem roster;
|
||||
// populated per-frame -- phase 5).
|
||||
//
|
||||
ChainOf<Subsystem*> controllableSubsystems;
|
||||
ChainOf<Subsystem*> watchedSubsystems;
|
||||
ChainOf<Subsystem*> heatableSubsystems;
|
||||
ChainOf<Subsystem*> weaponRoster;
|
||||
ChainOf<Subsystem*> damageableSubsystems;
|
||||
|
||||
//
|
||||
// Embedded status / animation / naming state.
|
||||
//
|
||||
NameFilter mechNameFilter;
|
||||
AlarmIndicator masterAlarm;
|
||||
AlarmIndicator heatAlarm;
|
||||
AlarmIndicator stabilityAlarm;
|
||||
AlarmIndicator statusAlarm;
|
||||
Reticle targetReticle;
|
||||
StateIndicator animationState;
|
||||
StateIndicator replicantAnimationState;
|
||||
StateIndicator collisionState;
|
||||
SequenceController legAnimation;
|
||||
SequenceController bodyAnimation;
|
||||
AverageOf<Scalar> telemetryFilter[5];
|
||||
CString resourceNameA;
|
||||
CString resourceNameB;
|
||||
CString resourceNameC;
|
||||
|
||||
//
|
||||
// Locomotion state (Phase 5.3). Turn rates in rad/s, speeds/strides in
|
||||
// world-units/s. reverseStrideLength = top (run) speed; walkStrideLength
|
||||
// = walk speed; reverseSpeedMax = the low-speed turn-rate gate; forward
|
||||
// ThrottleScale multiplies the forward demand; bodyTargetSpeed = the
|
||||
// demanded speed; currentBodySpeed = the accel-tracked actual speed.
|
||||
//
|
||||
Scalar walkingTurnRate;
|
||||
Scalar runningTurnRate;
|
||||
Scalar reverseStrideLength;
|
||||
Scalar walkStrideLength;
|
||||
Scalar reverseSpeedMax;
|
||||
Scalar forwardThrottleScale;
|
||||
Scalar maxBodyAcceleration;
|
||||
Scalar bodyTargetSpeed;
|
||||
Scalar currentBodySpeed;
|
||||
|
||||
//
|
||||
// Composed each frame in Simulate: the look-state eye component
|
||||
// (lookPitch/lookYaw, set by CommitLookState from the authored look
|
||||
// angles) plus the Torso elevation.
|
||||
//
|
||||
EulerAngles eyepointRotation;
|
||||
Scalar lookPitch;
|
||||
Scalar lookYaw;
|
||||
Entity *targetEntity;
|
||||
EntityID lastInflictingID; // binary mech+0x43c -- last
|
||||
// attacker (zone LOD router)
|
||||
|
||||
//
|
||||
// The model's authored look-view angles (rad; deg in the resource).
|
||||
//
|
||||
Scalar lookLeftAngle;
|
||||
Scalar lookRightAngle;
|
||||
Scalar lookFrontAngle;
|
||||
Scalar lookBackAngle;
|
||||
|
||||
//
|
||||
// Remaining per-frame targeting / animation state, advanced by the
|
||||
// mech2/mech3/mech4 simulation. Reserved until that path is
|
||||
// reconstructed with named fields.
|
||||
//
|
||||
int reservedState[199];
|
||||
};
|
||||
|
||||
#endif
|
||||
//===========================================================================//
|
||||
// File: mech.hpp //
|
||||
// Project: BattleTech Brick: Entity Manager //
|
||||
// Contents: Implementation details for the Mech entity //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(MECH_HPP)
|
||||
# define MECH_HPP
|
||||
|
||||
# if !defined(JMOVER_HPP)
|
||||
# include <jmover.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(RETICLE_HPP)
|
||||
# include <reticle.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(ALARM_HPP)
|
||||
# include <alarm.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(STATE_HPP)
|
||||
# include <state.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CHAIN_HPP)
|
||||
# include <chain.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(AVERAGE_HPP)
|
||||
# include <average.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CSTR_HPP)
|
||||
# include <cstr.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(SEQCTL_HPP)
|
||||
# include <seqctl.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(NAMEFILT_HPP)
|
||||
# include <namefilt.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(ROTATION_HPP)
|
||||
# include <rotation.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class Subsystem;
|
||||
class SubsystemMessageManager;
|
||||
class ControlsMapping;
|
||||
class PlatformTool;
|
||||
class MechControlsMapper;
|
||||
class Mech__DamageZone;
|
||||
class Joint;
|
||||
class DamageLookupTable;
|
||||
|
||||
//###########################################################################
|
||||
//######################### Mech Model Resource #########################
|
||||
//###########################################################################
|
||||
|
||||
struct Mech__ModelResource:
|
||||
public JointedMover::ModelResource
|
||||
{
|
||||
char animationPrefix[4];
|
||||
Scalar maxAcceleration;
|
||||
Scalar superStopAcceleration;
|
||||
Scalar throttleAdjustment;
|
||||
Scalar lookLeftAngle;
|
||||
Scalar lookRightAngle;
|
||||
Scalar lookFrontAngle;
|
||||
Scalar lookBackAngle;
|
||||
Scalar walkingTurnRate;
|
||||
Scalar runningTurnRate;
|
||||
Scalar reticleX;
|
||||
Scalar reticleY;
|
||||
Scalar relativeMechValue;
|
||||
int deathEffectResourceID;
|
||||
Scalar deathSplashDamage;
|
||||
Scalar deathSplashRadius;
|
||||
Scalar maxUnstableAcceleration;
|
||||
Scalar unstableAccelerationEffect;
|
||||
Scalar unstableGunTheEngineEffect;
|
||||
Scalar unstableSuperStopEffect;
|
||||
Scalar unstableHighVelocityEffect;
|
||||
Scalar unstableStopedTurnEffect;
|
||||
Scalar updatePositionDiffrence;
|
||||
Scalar updateTurnVelocityDiffrence;
|
||||
Scalar updateTurnDegreeDiffrence;
|
||||
Scalar timeDelay;
|
||||
Vector3D cameraOffset;
|
||||
char shadowJointName[20];
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//########################## Mech Make Message ##########################
|
||||
//###########################################################################
|
||||
|
||||
class Mech__MakeMessage:
|
||||
public JointedMover::MakeMessage
|
||||
{
|
||||
public:
|
||||
char resourceNameA[20];
|
||||
char resourceNameB[20];
|
||||
char resourceNameC[20];
|
||||
|
||||
Mech__MakeMessage(
|
||||
Receiver::MessageID message_ID,
|
||||
size_t length,
|
||||
const EntityID &entity_ID,
|
||||
Entity::ClassID class_ID,
|
||||
const EntityID &owner_ID,
|
||||
ResourceDescription::ResourceID resource_ID,
|
||||
LWord instance_flags,
|
||||
const Origin &origin,
|
||||
const Motion &velocity,
|
||||
const Motion &acceleration,
|
||||
const char *badge,
|
||||
const char *color,
|
||||
const char *patch
|
||||
):
|
||||
JointedMover::MakeMessage(
|
||||
message_ID, length, entity_ID, class_ID, owner_ID,
|
||||
resource_ID, instance_flags, origin, velocity, acceleration
|
||||
)
|
||||
{
|
||||
Str_Copy(resourceNameA, badge, sizeof(resourceNameA));
|
||||
Str_Copy(resourceNameB, color, sizeof(resourceNameB));
|
||||
Str_Copy(resourceNameC, patch, sizeof(resourceNameC));
|
||||
}
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//############################## Mech ###############################
|
||||
//###########################################################################
|
||||
|
||||
class Mech:
|
||||
public JointedMover
|
||||
{
|
||||
//
|
||||
// The per-zone damage code walks the roster / segment table / alarms
|
||||
// directly (the 1995 arrangement, mirrored by the reconstruction).
|
||||
//
|
||||
friend class Mech__DamageZone;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const HandlerEntry
|
||||
MessageHandlerEntries[];
|
||||
static MessageHandlerSet
|
||||
MessageHandlers;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Test Class Support
|
||||
//
|
||||
public:
|
||||
static Logical
|
||||
TestClass(Mech &);
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
typedef Mech__ModelResource ModelResource;
|
||||
typedef Mech__MakeMessage MakeMessage;
|
||||
typedef Mech__DamageZone DamageZone;
|
||||
|
||||
static Mech*
|
||||
Make(MakeMessage *creation_message);
|
||||
|
||||
Mech(
|
||||
MakeMessage *creation_message,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
~Mech();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// App interface
|
||||
//
|
||||
public:
|
||||
void
|
||||
SetMappingSubsystem(Subsystem *mapper);
|
||||
|
||||
//
|
||||
// Resolve a skeleton joint by name (the shared resolver the subsystems
|
||||
// use to bind their animated joints -- Torso twist, etc.):
|
||||
// GetSegment(name) -> segment jointIndex -> JointSubsystem::GetJoint.
|
||||
//
|
||||
Joint*
|
||||
ResolveJoint(const char *joint_name);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame simulation (the mech's body Performance; installed by the ctor
|
||||
// and dispatched each frame from Simulation::PerformAndWatch).
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
(Mech::*Performance)(Scalar time_slice);
|
||||
void
|
||||
SetPerformance(Performance performance)
|
||||
{
|
||||
Check(this);
|
||||
activePerformance = (Simulation::Performance)performance;
|
||||
}
|
||||
|
||||
void
|
||||
Simulate(Scalar time_slice);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Resource creation
|
||||
//
|
||||
public:
|
||||
static void
|
||||
CreateMakeMessage(
|
||||
MakeMessage *creation_message,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories
|
||||
);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateModelResource(
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories,
|
||||
ModelResource *model = 0
|
||||
);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateSubsystemStream(
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
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
|
||||
CreateSkeletonStream(
|
||||
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
|
||||
);
|
||||
|
||||
typedef Logical (*FindNameFunction)(
|
||||
const char *control_name, ControlsMapping *mapping);
|
||||
|
||||
static ResourceDescription::ResourceID
|
||||
CreateControlMappingStream(
|
||||
const char *mapping_name,
|
||||
NotationFile *mapping_file,
|
||||
FindNameFunction find_name,
|
||||
ResourceFile *resource_file,
|
||||
const char *model_name,
|
||||
NotationFile *model_file,
|
||||
const ResourceDirectories *directories,
|
||||
PlatformTool *current_tool
|
||||
);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Subsystem roster accessors (the roster itself -- subsystemArray /
|
||||
// subsystemCount -- lives in the base Entity)
|
||||
//
|
||||
public:
|
||||
Subsystem*
|
||||
GetGyroSubsystem() { Check(this); return gyroSubsystem; }
|
||||
Subsystem*
|
||||
GetTorsoSubsystem() { Check(this); return sinkSourceSubsystem; }
|
||||
Subsystem*
|
||||
GetHudSubsystem() { Check(this); return hudSubsystem; }
|
||||
Subsystem*
|
||||
GetSensorSubsystem() { Check(this); return sensorSubsystem; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Locomotion parameters (read by MechControlsMapper::InterpretControls to
|
||||
// shape the demands, and by the drive in Simulate). reverseStrideLength is
|
||||
// the top/run cycle speed the throttle scales (the naming is the 1995
|
||||
// field's; LoadLocomotionClips measures it from the run clips), walkStride
|
||||
// Length the walk speed. Turn rates are radians/sec.
|
||||
//
|
||||
public:
|
||||
Scalar GetReverseStrideLength() const { Check(this); return reverseStrideLength; }
|
||||
Scalar GetWalkStrideLength() const { Check(this); return walkStrideLength; }
|
||||
Scalar GetForwardThrottleScale() const { Check(this); return forwardThrottleScale; }
|
||||
void SetForwardThrottleScale(Scalar s){ Check(this); forwardThrottleScale = s; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Eyepoint / aim-ray composition. The pilot's torso-elevation aim (stick
|
||||
// pitch) does NOT tilt any skeleton joint on this mech family -- it pitches
|
||||
// the COCKPIT EYE and the weapon boresight (pixel-calibrated in the BT411
|
||||
// reverse-engineering: "pitch does not work" turned out to mean nothing
|
||||
// consumed Torso's currentElevation, not that a joint was un-animated).
|
||||
// Composed each frame in Simulate; DPLEyeRenderable / the aim ray read it.
|
||||
//
|
||||
public:
|
||||
const EulerAngles&
|
||||
GetEyepointRotation() const { Check(this); return eyepointRotation; }
|
||||
|
||||
//
|
||||
// The current target (the binary's mech target slot @0x388): read by
|
||||
// the weapons' HasActiveTarget / UpdateTargeting and the fire path.
|
||||
// The authentic writer is the reticle targeting step (mech4, the
|
||||
// render wave); the dev harness sets it directly.
|
||||
//
|
||||
Entity*
|
||||
GetTargetEntity() const { Check(this); return targetEntity; }
|
||||
void
|
||||
SetTargetEntity(Entity *target) { Check(this); targetEntity = target; }
|
||||
|
||||
//
|
||||
// Damage entry (overrides the Entity handler): latches the attacker
|
||||
// (mech+0x43c, read by the zone LOD router) and feeds the gyro
|
||||
// cockpit bounce before chaining to the base zone routing. The
|
||||
// cylinder unaimed-hit resolver (type-0x1d table) is a later wave.
|
||||
//
|
||||
void
|
||||
TakeDamageMessageHandler(TakeDamageMessage *message);
|
||||
|
||||
//
|
||||
// Damage-side death flag: the body graphic alarm (statusAlarm,
|
||||
// binary @0x714) at level >= 9 = the death/fall state.
|
||||
//
|
||||
Logical
|
||||
IsMechDestroyed() { Check(this); return statusAlarm.GetLevel() >= 9; }
|
||||
|
||||
//
|
||||
// The live torso twist (the cylinder table's rotate-with-torso rows
|
||||
// follow it). NULL-safe: 0 with no torso subsystem.
|
||||
//
|
||||
Scalar
|
||||
CurrentTorsoTwist();
|
||||
|
||||
DamageLookupTable*
|
||||
GetDamageLookupTable() const { Check(this); return damageLookupTable; }
|
||||
|
||||
//
|
||||
// Look-state commit (called by the controls mapper on a look-button
|
||||
// state change): re-aim the eyepoint from the model's authored look
|
||||
// angles. look_state = MechControlsMapper::LookState.
|
||||
//
|
||||
void
|
||||
CommitLookState(int look_state);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data
|
||||
//
|
||||
protected:
|
||||
//
|
||||
// Cached subsystem back-pointers (also held in the base roster).
|
||||
//
|
||||
Subsystem *sensorSubsystem;
|
||||
Subsystem *gyroSubsystem;
|
||||
Subsystem *sinkSourceSubsystem; // the real Torso
|
||||
Subsystem *hudSubsystem;
|
||||
SubsystemMessageManager *messageManager;
|
||||
int weaponCount;
|
||||
|
||||
//
|
||||
// Capability sub-rosters (Socket views onto the subsystem roster;
|
||||
// populated per-frame -- phase 5).
|
||||
//
|
||||
ChainOf<Subsystem*> controllableSubsystems;
|
||||
ChainOf<Subsystem*> watchedSubsystems;
|
||||
ChainOf<Subsystem*> heatableSubsystems;
|
||||
ChainOf<Subsystem*> weaponRoster;
|
||||
ChainOf<Subsystem*> damageableSubsystems;
|
||||
|
||||
//
|
||||
// Embedded status / animation / naming state.
|
||||
//
|
||||
NameFilter mechNameFilter;
|
||||
AlarmIndicator masterAlarm;
|
||||
AlarmIndicator heatAlarm;
|
||||
AlarmIndicator stabilityAlarm;
|
||||
AlarmIndicator statusAlarm;
|
||||
Reticle targetReticle;
|
||||
StateIndicator animationState;
|
||||
StateIndicator replicantAnimationState;
|
||||
StateIndicator collisionState;
|
||||
SequenceController legAnimation;
|
||||
SequenceController bodyAnimation;
|
||||
AverageOf<Scalar> telemetryFilter[5];
|
||||
CString resourceNameA;
|
||||
CString resourceNameB;
|
||||
CString resourceNameC;
|
||||
|
||||
//
|
||||
// Locomotion state (Phase 5.3). Turn rates in rad/s, speeds/strides in
|
||||
// world-units/s. reverseStrideLength = top (run) speed; walkStrideLength
|
||||
// = walk speed; reverseSpeedMax = the low-speed turn-rate gate; forward
|
||||
// ThrottleScale multiplies the forward demand; bodyTargetSpeed = the
|
||||
// demanded speed; currentBodySpeed = the accel-tracked actual speed.
|
||||
//
|
||||
Scalar walkingTurnRate;
|
||||
Scalar runningTurnRate;
|
||||
Scalar reverseStrideLength;
|
||||
Scalar walkStrideLength;
|
||||
Scalar reverseSpeedMax;
|
||||
Scalar forwardThrottleScale;
|
||||
Scalar maxBodyAcceleration;
|
||||
Scalar bodyTargetSpeed;
|
||||
Scalar currentBodySpeed;
|
||||
|
||||
//
|
||||
// Composed each frame in Simulate: the look-state eye component
|
||||
// (lookPitch/lookYaw, set by CommitLookState from the authored look
|
||||
// angles) plus the Torso elevation.
|
||||
//
|
||||
EulerAngles eyepointRotation;
|
||||
Scalar lookPitch;
|
||||
Scalar lookYaw;
|
||||
Entity *targetEntity;
|
||||
EntityID lastInflictingID; // binary mech+0x43c -- last
|
||||
// attacker (zone LOD router)
|
||||
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
|
||||
// cylinder hit-location table
|
||||
|
||||
//
|
||||
// The model's authored look-view angles (rad; deg in the resource).
|
||||
//
|
||||
Scalar lookLeftAngle;
|
||||
Scalar lookRightAngle;
|
||||
Scalar lookFrontAngle;
|
||||
Scalar lookBackAngle;
|
||||
|
||||
//
|
||||
// Remaining per-frame targeting / animation state, advanced by the
|
||||
// mech2/mech3/mech4 simulation. Reserved until that path is
|
||||
// reconstructed with named fields.
|
||||
//
|
||||
int reservedState[198];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -313,8 +313,27 @@ void
|
||||
if (range <= fuseRadius && seeker->targetEntity != NULL)
|
||||
{
|
||||
Entity *victim = seeker->targetEntity;
|
||||
//
|
||||
// The AUTHENTIC unaimed delivery: zone -1 (invalidDamageZone) with
|
||||
// the round's world position as the impact point -- the victim's
|
||||
// cylinder hit-location table resolves WHERE the round physically
|
||||
// struck ("For BattleTech, damage zones are only valid via reticle
|
||||
// based weapons" -- the surviving ENTITY3.HPP warning). Without a
|
||||
// table the base handler drops the hit, so victims with no
|
||||
// type-29 stream fall back to the interim random-zone pick.
|
||||
//
|
||||
int zone = -1;
|
||||
if (victim->damageZoneCount > 0)
|
||||
damageData.impactPoint = localOrigin.linearPosition;
|
||||
if (victim->IsDerivedFrom(Mech::ClassDerivations))
|
||||
{
|
||||
Mech *mech_victim = (Mech *)victim;
|
||||
if (mech_victim->GetDamageLookupTable() == NULL
|
||||
&& victim->damageZoneCount > 0)
|
||||
{
|
||||
zone = Random(victim->damageZoneCount);
|
||||
}
|
||||
}
|
||||
else if (victim->damageZoneCount > 0)
|
||||
{
|
||||
zone = Random(victim->damageZoneCount);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user