BT410 5.3.120: the mech ducks -- the master perf's duck driver landed whole, and the collision cylinder breathes with it
The first block reconstructed from the isolated master performance (@004a9f61..@004aa155, instruction-level read of the ndisasm listing), and with it the Mech's authentic message roster straight from the binary's own handler table: RealMaxSpeed 0x15, BalanceCoolant 0x16, SetBurningState 0x17, ClearBurningState 0x18, EjectPilot 0x19, DuckRequest 0x1A -- the enum now exists in MECH.HPP with those names. The system end to end: a DuckRequest message (@0049fa00) latches ONE evaluation -- the latch is consumed every master frame and cleared by Reset. The phase machine reads the raw simulation state, the mapper's duck button cell and the analog demand (deadzone 1e-4): from gait 0/4 it arms the sqd clip on the LEG channel only (slot 2 -- the slot map, not the enum's walk names), from the ducked hold it arms squ (slot 3), and a mech that starts limping while ducked is stood up by force. The collision-volume alarm (level 1 standing / 0 ducked, guarded by the authentic "Whoa! Bad Collision Volume State!") swaps the collision template's maxY -- the box TOP; the ground probe's minY is untouched -- between the ctor-captured height and 0.6x it (the double at @004a2d38). duckState publishes for the cockpit's crouch selector. Live-verified end to end: BT_FORCE_DUCK (an authentic press every 4s on its own clock) cycles press -> sqd -> volume 9.91->5.94 -> ducked hold -> press -> squ -> standing, continuously, no faults. En route the test machine-checked three neighbors for free: the squat clips resolve and play at the idle rate, the clip-finish transitions for slots 2/3 fire, and the machine correctly REFUSES to duck while walking (legState 5). Deferred, recorded: the per-arm MarkUpdate masks (8, 1) ride the still-empty ForceUpdate (the MP update-record feed), and the mapper duck cell's streamed binding id awaits a BT_MAP_LOG audit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# pod_render_norio.conf -- pod_render_rec with the RIO serial port OFF.
|
||||
#
|
||||
# The emulator log shows a steady stream of serial1 RX OVERRUN errors on
|
||||
# the RIO pipe, and controls post their events at HighEventPriority
|
||||
# UNCONDITIONALLY (CONTROLS.HPP:250) -- so a chattering RIO port would
|
||||
# flood a priority the background pump always serves first, starving the
|
||||
# priority-0 renderer events the load gate waits on. This conf tests that
|
||||
# by removing the port entirely. Everything else is identical to the rec
|
||||
# conf, so a launch here and a hang there isolates the RIO.
|
||||
#
|
||||
[sdl]
|
||||
output=opengl
|
||||
# higher,higher not highest: HIGH_PRIORITY_CLASS starved the host desktop;
|
||||
# with the retry patches a rare dropout self-recovers (see gauge_rio.conf).
|
||||
priority=higher,higher
|
||||
[dosbox]
|
||||
memsize=32
|
||||
machine=svga_s3
|
||||
[cpu]
|
||||
core=dynamic
|
||||
cputype=pentium
|
||||
cycles=max
|
||||
[sblaster]
|
||||
sbtype=sb16
|
||||
sbbase=220
|
||||
irq=5
|
||||
dma=1
|
||||
hdma=5
|
||||
[mixer]
|
||||
# match the EMU8000s' native rate (no resample) and buffer ~60ms so brief
|
||||
# emulation-thread stalls (RIO retry recovery) don't audibly chop
|
||||
rate=44100
|
||||
blocksize=1024
|
||||
prebuffer=60
|
||||
[serial]
|
||||
# RIO on COM1 with the low-latency options (rxpollus/rxburst) so the board's
|
||||
# few-ms ACK deadline is met; plasma display on COM2 (real pod has both).
|
||||
# VWE fork namedpipe backend (com0com/realport retired -- COM1/COM2 gone):
|
||||
# DOSBox = pipe client (retry), vRIO/vPLASMA apps = servers; an unconnected
|
||||
# pipe behaves as an unplugged cable so the mission still runs. serialnamedpipe.h
|
||||
serial1=disabled
|
||||
serial2=namedpipe pipe:vplasma
|
||||
# live UNBUFFERED game output: DOS char devices are not buffered, so
|
||||
# redirecting stdout to COM3 lands every line immediately. A normal
|
||||
# '> file' redirect stays 0 bytes until the process exits, which hides
|
||||
# all progress on a run that does NOT crash.
|
||||
serial3=file file:C:\VWE\TeslaRel410\emulator\render-bridge\podlog.txt
|
||||
[autoexec]
|
||||
mount c "C:\VWE\TeslaRel410\ALPHA_1"
|
||||
c:
|
||||
cd \REL410\BT
|
||||
set VIDEOFORMAT=svga
|
||||
rem production pod card init (PARAMETR.BAT:181-186): DIAGNOSE + AWEUTIL per
|
||||
rem card -- AWEUTIL /S does the EMU8000 bring-up and DRAM detect the HMI SOS
|
||||
rem driver relies on; skipping it left the cards uninitialized (silent).
|
||||
rem aweutil /s SKIPPED for now: it verifies the AWE32 GM ROM, which the
|
||||
rem emulated cards lack (hangs in a retry loop) -- restore once the ROM is
|
||||
rem dumped from a real card. diagnose /s kept (passes, sets mixer config).
|
||||
set BLASTER=A220 I5 D1 H5 P330 T6
|
||||
c:\sb16\diagnose /s
|
||||
set BLASTER=A240 I7 D3 H6 P300 T6
|
||||
c:\sb16\diagnose /s
|
||||
set BLASTER=A220 I5 D1 H5 P330 T6
|
||||
set TEMP=c:\
|
||||
rem arena1 city mission (TESTARN.EGG: map=arena1, time=day) with the RIO
|
||||
rem attached; stdout redirected so mission-load progress survives kills.
|
||||
set BT_JOINTS=1
|
||||
set L4VIEWEXT=1
|
||||
set BT_STACK_LOG=1
|
||||
set BT_MECH_LOG=1
|
||||
set BT_FORCE_DUCK=1
|
||||
set BT_GAUGE_LOG=1
|
||||
set BT_FORCE_THROTTLE=0
|
||||
set BT_FORCE_TURN=0
|
||||
set HEAPSIZE=15000000
|
||||
set L4GAUGE=640x480x16
|
||||
call setenv.bat r s n p
|
||||
32rtm.exe -x
|
||||
BTL4REC.EXE -egg testarn.egg > COM3
|
||||
|
||||
echo GAME-RC=%errorlevel% >> RC.TXT
|
||||
32rtm.exe -u
|
||||
echo ALPHA1-RUN-DONE
|
||||
pause
|
||||
|
||||
@@ -86,7 +86,8 @@ Derivation
|
||||
const Receiver::HandlerEntry
|
||||
Mech::MessageHandlerEntries[] =
|
||||
{
|
||||
MESSAGE_ENTRY(Mech, TakeDamage)
|
||||
MESSAGE_ENTRY(Mech, TakeDamage),
|
||||
MESSAGE_ENTRY(Mech, DuckRequest)
|
||||
};
|
||||
|
||||
Mech::MessageHandlerSet
|
||||
@@ -292,6 +293,25 @@ Mech::Mech(
|
||||
radarLinearPosition = &localOrigin.linearPosition;
|
||||
radarAngularPosition = &localOrigin.angularPosition;
|
||||
duckState = 0;
|
||||
|
||||
//
|
||||
// The duck system's rest state (binary ctor tail @004a1674: the volume
|
||||
// alarm at +0x4c4 initialises to level 1 = STANDING, and the two
|
||||
// cylinder heights capture template maxY and 0.6 x it -- the heights
|
||||
// are seeded after the collision template resolves, below).
|
||||
//
|
||||
duckRequestLatch = 0;
|
||||
duckPhaseRequest = 0;
|
||||
duckDemand = 0.0f;
|
||||
collisionVolumeState.Initialize(2);
|
||||
collisionVolumeState.SetLevel(1);
|
||||
standingVolumeHeight = 0.0f;
|
||||
duckedVolumeHeight = 0.0f;
|
||||
if (collisionTemplate != NULL)
|
||||
{
|
||||
standingVolumeHeight = collisionTemplate->maxY;
|
||||
duckedVolumeHeight = 0.6f * standingVolumeHeight;
|
||||
}
|
||||
unstablePercentage = 0.0f; // staged: no instability model yet
|
||||
collisionSpeed = 0.0f; // staged: audio watcher set (see MECH.HPP)
|
||||
distanceToMissile = 0.0f;
|
||||
@@ -810,6 +830,50 @@ Logical
|
||||
vehicle->GetSimulationState() == 9;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// DuckRequestMessageHandler -- binary @0049fa00 (authentic name "DuckRequest"
|
||||
// from the handler table @0x10bc7c). A positive request latches one duck
|
||||
// evaluation for the next master frame; the frame consumes the latch either
|
||||
// way.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Mech::DuckRequestMessageHandler(Receiver::Message *message)
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(message);
|
||||
|
||||
if (((int *)(message + 1))[0] > 0)
|
||||
{
|
||||
duckRequestLatch = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The collision-volume swap (binaries @004ac04c / @004ac064): write the
|
||||
// template's maxY -- the box TOP; the ground probe's minY is untouched.
|
||||
// Level 1 (standing) restores the captured height; level 0 (ducked) the
|
||||
// 0.6x height.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Mech::SetStandingCollisionVolume()
|
||||
{
|
||||
Check(this);
|
||||
Check(collisionTemplate);
|
||||
collisionTemplate->maxY = standingVolumeHeight;
|
||||
}
|
||||
|
||||
void
|
||||
Mech::SetDuckedCollisionVolume()
|
||||
{
|
||||
Check(this);
|
||||
Check(collisionTemplate);
|
||||
collisionTemplate->maxY = duckedVolumeHeight;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// TakeDamageMessageHandler -- the Mech override of the Entity damage entry
|
||||
@@ -1177,6 +1241,12 @@ void
|
||||
void
|
||||
Mech::Reset(const Origin &origin, Logical full_reset)
|
||||
{
|
||||
//
|
||||
// The binary Reset (@0049fb74) drops any pending duck latch with the
|
||||
// rest of the per-life state.
|
||||
//
|
||||
duckRequestLatch = 0;
|
||||
|
||||
Check(this);
|
||||
|
||||
localOrigin = origin;
|
||||
@@ -1619,6 +1689,44 @@ void
|
||||
//
|
||||
legResetLatch = 0;
|
||||
|
||||
//
|
||||
// BT_FORCE_DUCK=1: one authentic duck press every ~4s on its own
|
||||
// clock (independent of the one-shot button harness below) -- the
|
||||
// DuckRequest message, plus the held cell and analog demand every
|
||||
// frame. Press one ducks, the next (from the hold) stands: the
|
||||
// full-machine cycle soak.
|
||||
//
|
||||
{
|
||||
static int s_forceDuck = -1;
|
||||
static Scalar s_duckClock = 0.0f;
|
||||
if (s_forceDuck < 0)
|
||||
{
|
||||
s_forceDuck = (getenv("BT_FORCE_DUCK") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (s_forceDuck)
|
||||
{
|
||||
MechControlsMapper
|
||||
*pulse_mapper = (MechControlsMapper *)subsystemArray[0];
|
||||
if (pulse_mapper != NULL)
|
||||
{
|
||||
pulse_mapper->duckCommand = 1;
|
||||
}
|
||||
duckDemand = 1.0f;
|
||||
s_duckClock += time_slice;
|
||||
if (s_duckClock >= 4.0f)
|
||||
{
|
||||
s_duckClock = 0.0f;
|
||||
ReceiverDataMessageOf<int>
|
||||
duck_press(
|
||||
Mech::DuckRequestMessageID,
|
||||
sizeof(ReceiverDataMessageOf<int>),
|
||||
1);
|
||||
Dispatch(&duck_press);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (limping)
|
||||
{
|
||||
AdvanceLegAnimationGimp(time_slice);
|
||||
@@ -1641,6 +1749,150 @@ void
|
||||
((Gyroscope *)gyroSubsystem)->WriteMechJoint();
|
||||
}
|
||||
|
||||
//
|
||||
// THE DUCK DRIVER (master perf @004a9f61..@004aa155, disasm-decoded).
|
||||
//
|
||||
// Phase pick, every frame: not standing (raw state != 0) or duck button
|
||||
// released -> no request. Standing with the button held: from gait
|
||||
// state 0/4 (stand / turn-in-place), request duck-DOWN while the analog
|
||||
// demand clears the 1e-4 deadzone; from gait state 1 (ducked hold),
|
||||
// request duck-UP; anything else, none.
|
||||
//
|
||||
// The arms fire only on a DuckRequest-latched frame of a squat-capable
|
||||
// mech, bind the LEG channel only (squat is a leg figure), and set the
|
||||
// volume alarm: down -> level 0 (ducked), up -> level 1 (standing).
|
||||
// The latch is consumed unconditionally. A mech that STARTS LIMPING
|
||||
// while ducked is stood up by force (modes 3/4 with gait state 1).
|
||||
// On a volume-alarm edge the collision template's top swaps between
|
||||
// the captured heights; any other level is the authentic
|
||||
// "Whoa! Bad Collision Volume State!" Fail. The dirty marks (binary
|
||||
// MarkUpdate(8)+MarkUpdate(1) per arm) ride ForceUpdate -- still a
|
||||
// no-op here until the update-record feed lands.
|
||||
//
|
||||
{
|
||||
int
|
||||
raw_mode = (int)GetSimulationState();
|
||||
//
|
||||
// The mapper lives in roster slot 0 (the binary caches it at
|
||||
// mech+0x190; our tree resolves the same object through the roster).
|
||||
//
|
||||
MechControlsMapper
|
||||
*duck_mapper = (MechControlsMapper *)subsystemArray[0];
|
||||
int
|
||||
duck_held =
|
||||
(duck_mapper != NULL && duck_mapper->DuckCommand() != 0);
|
||||
|
||||
if (raw_mode != 0 || !duck_held)
|
||||
{
|
||||
duckPhaseRequest = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int
|
||||
leg_state = (int)legStateAlarm.GetLevel();
|
||||
if (leg_state == 0 || leg_state == 4)
|
||||
{
|
||||
duckPhaseRequest =
|
||||
(duckDemand <= 0.0001f && duckDemand >= -0.0001f) ? 0 : 1;
|
||||
}
|
||||
else if (leg_state == 1)
|
||||
{
|
||||
duckPhaseRequest = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
duckPhaseRequest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
static int s_duckEvalLog = -1;
|
||||
if (s_duckEvalLog < 0)
|
||||
{
|
||||
s_duckEvalLog = (getenv("BT_MECH_LOG") != NULL);
|
||||
}
|
||||
if (s_duckEvalLog && duckRequestLatch != 0)
|
||||
{
|
||||
DEBUG_STREAM << "[duck] eval mode=" << raw_mode
|
||||
<< " held=" << duck_held
|
||||
<< " leg=" << legStateAlarm.GetLevel()
|
||||
<< " phase=" << duckPhaseRequest
|
||||
<< " capable=" << squatCapable << endl;
|
||||
}
|
||||
}
|
||||
if (duckRequestLatch != 0 && squatCapable != 0)
|
||||
{
|
||||
if (duckPhaseRequest == 1)
|
||||
{
|
||||
SetLegAnimation(2); // the sqd (squat-down) clip slot -- the
|
||||
// SLOT map diverges from the enum's walk
|
||||
// names here (MECH2.NOTES.md)
|
||||
ForceUpdate(8);
|
||||
ForceUpdate(1);
|
||||
collisionVolumeState.SetLevel(0);
|
||||
}
|
||||
else if (duckPhaseRequest == 2)
|
||||
{
|
||||
SetLegAnimation(3); // the squ (squat-up) clip slot
|
||||
ForceUpdate(8);
|
||||
ForceUpdate(1);
|
||||
collisionVolumeState.SetLevel(1);
|
||||
}
|
||||
}
|
||||
duckRequestLatch = 0;
|
||||
|
||||
//
|
||||
// Limping stands you up (modes 3/4 with the legs still in the
|
||||
// ducked hold).
|
||||
//
|
||||
if (
|
||||
(raw_mode == 3 || raw_mode == 4) &&
|
||||
(int)legStateAlarm.GetLevel() == 1
|
||||
)
|
||||
{
|
||||
SetLegAnimation(3); // squ -- forced stand-up
|
||||
ForceUpdate(8);
|
||||
ForceUpdate(1);
|
||||
collisionVolumeState.SetLevel(1);
|
||||
}
|
||||
|
||||
//
|
||||
// The volume swap on an alarm edge, and the DuckState publish.
|
||||
//
|
||||
if (collisionVolumeState.GetState() != collisionVolumeState.GetOldState())
|
||||
{
|
||||
static int s_duckLog = -1;
|
||||
if (s_duckLog < 0)
|
||||
{
|
||||
s_duckLog = (getenv("BT_MECH_LOG") != NULL);
|
||||
}
|
||||
if (s_duckLog)
|
||||
{
|
||||
DEBUG_STREAM << "[duck] volume level "
|
||||
<< collisionVolumeState.GetOldState() << " -> "
|
||||
<< collisionVolumeState.GetState()
|
||||
<< " legState=" << legStateAlarm.GetLevel()
|
||||
<< " maxY " << standingVolumeHeight
|
||||
<< "/" << duckedVolumeHeight << endl;
|
||||
}
|
||||
switch (collisionVolumeState.GetLevel())
|
||||
{
|
||||
case 1:
|
||||
SetStandingCollisionVolume();
|
||||
duckState = 0;
|
||||
break;
|
||||
case 0:
|
||||
SetDuckedCollisionVolume();
|
||||
duckState = 1;
|
||||
break;
|
||||
default:
|
||||
Fail("Whoa! Bad Collision Volume State!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
collisionVolumeState.SetLevel(collisionVolumeState.GetLevel());
|
||||
}
|
||||
|
||||
//
|
||||
// THE TURN-IN-PLACE DISPATCHER (master perf @0x4aa505-0x4aa588, decoded
|
||||
// from raw disasm -- Ghidra never decompiled the function). From
|
||||
|
||||
@@ -221,6 +221,23 @@
|
||||
class Mech:
|
||||
public JointedMover
|
||||
{
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Message Support -- the authentic roster, read from the binary's own
|
||||
// handler table (ids and NAME STRINGS live at 0x10bbf0../0x50c146..):
|
||||
// 0x12 TakeDamage (Entity overlay), 0x14 PlayerLink (Entity overlay),
|
||||
// then the mech's own block from Entity::NextMessageID.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
RealMaxSpeedMessageID = Entity::NextMessageID, // 0x15 @0049f604
|
||||
BalanceCoolantMessageID, // 0x16 @0049f728
|
||||
SetBurningStateMessageID, // 0x17 @0049f674
|
||||
ClearBurningStateMessageID, // 0x18 @0049f700
|
||||
EjectPilotMessageID, // 0x19 @0049f854
|
||||
DuckRequestMessageID, // 0x1A @0049fa00
|
||||
NextMessageID
|
||||
};
|
||||
|
||||
//
|
||||
// The per-zone damage code walks the roster / segment table / alarms
|
||||
// directly (the 1995 arrangement, mirrored by the reconstruction).
|
||||
@@ -885,6 +902,41 @@
|
||||
Point3D *radarLinearPosition;
|
||||
Quaternion *radarAngularPosition;
|
||||
int duckState;
|
||||
|
||||
//
|
||||
// THE DUCK SYSTEM (master perf @004a9f61..@004aa155, disasm-decoded).
|
||||
//
|
||||
// duckRequestLatch (+0x398) ONE-SHOT: set by the DuckRequest
|
||||
// message handler (@0049fa00, request > 0), consumed -- and
|
||||
// CLEARED -- every master frame after the arm check; also
|
||||
// cleared by Reset. Only a latched frame may bind squat clips.
|
||||
// duckPhaseRequest (+0x3f8) 0 none / 1 duck-down / 2 duck-up,
|
||||
// recomputed every frame from the raw simulation state, the
|
||||
// mapper's duck button cell, the leg gait state and the duck
|
||||
// analog demand (deadzone 1e-4 @0x4ab16c).
|
||||
// duckDemand (+0x79c) the analog crouch input.
|
||||
// collisionVolumeState (+0x4c4) the volume alarm: level 1 =
|
||||
// STANDING, 0 = DUCKED. On an edge the collision template's
|
||||
// maxY (BoxedSolid +0xc -- the box TOP; the ground probe's
|
||||
// minY is untouched) swaps between the two ctor-captured
|
||||
// heights; any other level Fails ("Whoa! Bad Collision
|
||||
// Volume State!", MECH4.CPP line 417).
|
||||
// standing/duckedVolumeHeight (+0x518/+0x51c) captured at ctor
|
||||
// time: template maxY, and 0.6 x it (the double @004a2d38).
|
||||
//
|
||||
int duckRequestLatch;
|
||||
int duckPhaseRequest;
|
||||
Scalar duckDemand;
|
||||
AlarmIndicator collisionVolumeState;
|
||||
Scalar standingVolumeHeight;
|
||||
Scalar duckedVolumeHeight;
|
||||
|
||||
void
|
||||
DuckRequestMessageHandler(Receiver::Message *message);
|
||||
void
|
||||
SetStandingCollisionVolume(); // binary @004ac04c... see .CPP
|
||||
void
|
||||
SetDuckedCollisionVolume();
|
||||
//
|
||||
// STAGED VALUE (the instability model is not reconstructed).
|
||||
// The mech streams the authored unstable* effect constants in its
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
void
|
||||
Mech::SetLegAnimation(int state)
|
||||
{
|
||||
if ((state == 2 || state == 3) && getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[duck] arm slot " << state
|
||||
<< " clip=" << animationClips[state] << endl;
|
||||
}
|
||||
Check(this);
|
||||
//
|
||||
// Bounded by the SLOT count, not the name count: slot 0x20 is the
|
||||
@@ -192,6 +197,10 @@ Scalar
|
||||
return 0.0f;
|
||||
|
||||
case 2:
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[duck] sqd finished -> ducked hold" << endl;
|
||||
}
|
||||
mech->legStateAlarm.SetLevel(1);
|
||||
return 0.0f;
|
||||
|
||||
|
||||
@@ -86,8 +86,24 @@ per contact by `Mover::ProcessCollisionList`. The decomp
|
||||
matching our Simulate's structure and locating every staged gap:
|
||||
1. controls/demand intake (mech.cpp helpers 0x4a4eab/0x4a4ee9/0x4a4f9e)
|
||||
2. three cerr+SetLevel triplets — illegal-state alarm guards
|
||||
3. **THE DUCK DRIVER**: three arms of {mech2 SetAnim 0x4a7fc4,
|
||||
MarkUpdate ×2, AlarmIndicator::SetLevel} — duck down/hold/up
|
||||
3. **THE DUCK DRIVER** — DECODED + LANDED 5.3.120, live-verified
|
||||
(BT_FORCE_DUCK cycles down/hold/up continuously on the rig). The
|
||||
full system: DuckRequest message 0x1A (authentic name from the
|
||||
binary's handler table; roster = RealMaxSpeed 0x15, BalanceCoolant
|
||||
0x16, SetBurningState 0x17, ClearBurningState 0x18, EjectPilot 0x19,
|
||||
DuckRequest 0x1A) latches one evaluation (+0x398, one-shot, Reset
|
||||
clears); the phase machine reads the RAW simulation state, the
|
||||
mapper's duck cell (+0x25c) and the analog demand (+0x79c, deadzone
|
||||
1e-4 @0x4ab16c): gait 0/4 + input -> down (LEG slot 2, sqd), gait 1
|
||||
-> up (slot 3, squ); limp modes 3/4 force stand-up; the
|
||||
collisionVolumeState alarm (+0x4c4; 1=standing, 0=ducked; "Whoa! Bad
|
||||
Collision Volume State!" guard, line 417) swaps the collision
|
||||
template's maxY (ExtentBox +0xc == the box TOP; the ground probe's
|
||||
minY untouched) between the ctor-captured height and 0.6x it (the
|
||||
double @004a2d38); duckState publishes for the cockpit selector.
|
||||
Still open here: the two MarkUpdate masks (8 then 1) ride our
|
||||
ForceUpdate no-op, and the mapper duck cell's streamed binding id
|
||||
awaits a BT_MAP_LOG audit on a duck-bound RES.
|
||||
4. verify-guarded state read (mech4 accessors + Fail 0x40385c)
|
||||
5. gyro feed a (vec pair → gyro_b 0x4b2de4, gyro_a 0x4b2d8c)
|
||||
6. death arm: InDeathTransition 0x49fb54 → the 1.9KB mech2 advancer
|
||||
|
||||
@@ -95,6 +95,7 @@ MechControlsMapper::MechControlsMapper(
|
||||
shared_data
|
||||
)
|
||||
{
|
||||
duckCommand = 0;
|
||||
Check(owner);
|
||||
//
|
||||
// Prime the published control inputs to neutral. Per-frame interpretation
|
||||
|
||||
@@ -1,210 +1,223 @@
|
||||
//===========================================================================//
|
||||
// File: mechmppr.hpp //
|
||||
// Project: BattleTech //
|
||||
// Contents: Implementation details for the mech controls mapper //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(MECHMPPR_HPP)
|
||||
# define MECHMPPR_HPP
|
||||
|
||||
# if !defined(SUBSYSTM_HPP)
|
||||
# include <subsystm.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CSTR_HPP)
|
||||
# include <cstr.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CONTROLS_HPP)
|
||||
# include <controls.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(VECTOR2D_HPP)
|
||||
# include <vector2d.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
|
||||
//###########################################################################
|
||||
//######################## MechControlsMapper ###########################
|
||||
//###########################################################################
|
||||
|
||||
class MechControlsMapper:
|
||||
public Subsystem
|
||||
{
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute IDs -- the control inputs the streamed mappings bind to.
|
||||
// The chain ends at Subsystem::NextAttributeID == 2.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
MechControlsMapperPadFirstAttributeID = 2,
|
||||
StickPositionAttributeID, // 3
|
||||
ThrottlePositionAttributeID, // 4
|
||||
PedalsPositionAttributeID, // 5
|
||||
ReverseThrustAttributeID, // 6
|
||||
SpeedDemandAttributeID, // 7
|
||||
TurnDemandAttributeID, // 8
|
||||
LookForwardAttributeID, // 9
|
||||
LookLeftAttributeID, // a
|
||||
LookRightAttributeID, // b
|
||||
LookBehindAttributeID, // c
|
||||
LookDownAttributeID, // d
|
||||
TorsoUpAttributeID, // e
|
||||
TorsoDownAttributeID, // f
|
||||
TorsoLeftAttributeID, // 10
|
||||
TorsoRightAttributeID, // 11
|
||||
TorsoCenterAttributeID, // 12
|
||||
ControlModeAttributeID, // 13
|
||||
DisplayModeAttributeID, // 14
|
||||
PilotArrayPageAttributeID, // 15
|
||||
PilotArrayAttributeID, // 16
|
||||
TargetRangeExponentAttributeID, // authored watcher
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Control modes
|
||||
//
|
||||
public:
|
||||
enum ControlMode {
|
||||
BasicMode = 0,
|
||||
StandardMode,
|
||||
VeteranMode
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Look states. A five-state machine off the pod's look buttons; on a state
|
||||
// change InterpretControls calls Mech::CommitLookState, which re-aims the
|
||||
// eyepoint from the model's authored look angles (and, once the weapon wave
|
||||
// lands, re-arms the per-view weapon fire enables).
|
||||
//
|
||||
public:
|
||||
enum LookState {
|
||||
LookNone = 0,
|
||||
LookLeftState,
|
||||
LookRightState,
|
||||
LookBehindState,
|
||||
LookDownState
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
MechControlsMapper(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
CString subsystem_name,
|
||||
RegisteredClass::ClassID class_ID,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
|
||||
~MechControlsMapper();
|
||||
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame control interpretation (the mapper's Performance): reads the
|
||||
// engine-pushed raw input attributes (throttle / stick / pedals / buttons)
|
||||
// and publishes the locomotion + aim demands the mech drive consumes.
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
(MechControlsMapper::*Performance)(Scalar time_slice);
|
||||
void
|
||||
SetPerformance(Performance performance)
|
||||
{
|
||||
Check(this);
|
||||
activePerformance = (Simulation::Performance)performance;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretControls(Scalar time_slice);
|
||||
|
||||
Mech*
|
||||
GetMech() { Check(this); return (Mech *)GetEntity(); }
|
||||
Scalar
|
||||
GetSpeedDemand() const { Check(this); return speedDemand; }
|
||||
Scalar
|
||||
GetTurnDemand() const { Check(this); return turnDemand; }
|
||||
|
||||
//
|
||||
// The LIMP machines write the demand cell DOWN to the damaged side's
|
||||
// speed cap (binary GimpLegClipFinished @004a7970 preamble writes
|
||||
// mapper+0x128 directly) -- a limping mech cannot command more speed
|
||||
// than its limp figure carries.
|
||||
//
|
||||
void
|
||||
SetSpeedDemand(Scalar demand)
|
||||
{ Check(this); speedDemand = demand; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data -- the published control inputs (the streamed mappings write
|
||||
// these; InterpretControls, phase 5.3, reads them to drive the mech).
|
||||
//
|
||||
public:
|
||||
//
|
||||
// The POD ROSTER (binary mapper attributes PilotArrayPage/PilotArray,
|
||||
// ids 15/16): the comm scoreboard's row source. Zero = no roster
|
||||
// authored (a solo mission), which is why the shipped cockpit leaves
|
||||
// that page blank.
|
||||
//
|
||||
int
|
||||
GetPilotArray() const { Check(this); return pilotArray; }
|
||||
int
|
||||
GetPilotArrayPage() const { Check(this); return pilotArrayPage; }
|
||||
|
||||
|
||||
protected:
|
||||
ControlsJoystick stickPosition;
|
||||
Scalar throttlePosition;
|
||||
Scalar pedalsPosition;
|
||||
ControlsButton reverseThrust;
|
||||
Scalar speedDemand;
|
||||
Scalar turnDemand;
|
||||
ControlsButton lookForward;
|
||||
ControlsButton lookLeft;
|
||||
ControlsButton lookRight;
|
||||
ControlsButton lookBehind;
|
||||
ControlsButton lookDown;
|
||||
ControlsButton torsoUp;
|
||||
ControlsButton torsoDown;
|
||||
ControlsButton torsoLeft;
|
||||
ControlsButton torsoRight;
|
||||
ControlsButton torsoCenter;
|
||||
int controlMode;
|
||||
int displayMode;
|
||||
int pilotArrayPage;
|
||||
int pilotArray;
|
||||
int lookState;
|
||||
int previousLookState;
|
||||
//
|
||||
// Takes one reserved word (Scalar and int are both 4 bytes here), so
|
||||
// the class size and every existing offset stay exactly as they were.
|
||||
//
|
||||
Scalar targetRangeExponent; // authored watcher
|
||||
int reserved[21];
|
||||
};
|
||||
|
||||
#endif
|
||||
//===========================================================================//
|
||||
// File: mechmppr.hpp //
|
||||
// Project: BattleTech //
|
||||
// Contents: Implementation details for the mech controls mapper //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(MECHMPPR_HPP)
|
||||
# define MECHMPPR_HPP
|
||||
|
||||
# if !defined(SUBSYSTM_HPP)
|
||||
# include <subsystm.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CSTR_HPP)
|
||||
# include <cstr.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(CONTROLS_HPP)
|
||||
# include <controls.hpp>
|
||||
# endif
|
||||
|
||||
# if !defined(VECTOR2D_HPP)
|
||||
# include <vector2d.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
|
||||
//###########################################################################
|
||||
//######################## MechControlsMapper ###########################
|
||||
//###########################################################################
|
||||
|
||||
class MechControlsMapper:
|
||||
public Subsystem
|
||||
{
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// The duck button cell (binary mapper+0x25c) -- a device-poll write
|
||||
// destination like the torso command cells; the master perf's duck
|
||||
// driver reads it as the held-button gate. The streamed attribute
|
||||
// binding id is still to be audited (BT_MAP_LOG) -- until a RES table
|
||||
// binds it, BT_FORCE_DUCK drives it.
|
||||
//
|
||||
public:
|
||||
int
|
||||
DuckCommand()
|
||||
{ Check(this); return duckCommand; }
|
||||
int duckCommand;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const IndexEntry AttributePointers[];
|
||||
static AttributeIndexSet AttributeIndex;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Attribute IDs -- the control inputs the streamed mappings bind to.
|
||||
// The chain ends at Subsystem::NextAttributeID == 2.
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
MechControlsMapperPadFirstAttributeID = 2,
|
||||
StickPositionAttributeID, // 3
|
||||
ThrottlePositionAttributeID, // 4
|
||||
PedalsPositionAttributeID, // 5
|
||||
ReverseThrustAttributeID, // 6
|
||||
SpeedDemandAttributeID, // 7
|
||||
TurnDemandAttributeID, // 8
|
||||
LookForwardAttributeID, // 9
|
||||
LookLeftAttributeID, // a
|
||||
LookRightAttributeID, // b
|
||||
LookBehindAttributeID, // c
|
||||
LookDownAttributeID, // d
|
||||
TorsoUpAttributeID, // e
|
||||
TorsoDownAttributeID, // f
|
||||
TorsoLeftAttributeID, // 10
|
||||
TorsoRightAttributeID, // 11
|
||||
TorsoCenterAttributeID, // 12
|
||||
ControlModeAttributeID, // 13
|
||||
DisplayModeAttributeID, // 14
|
||||
PilotArrayPageAttributeID, // 15
|
||||
PilotArrayAttributeID, // 16
|
||||
TargetRangeExponentAttributeID, // authored watcher
|
||||
NextAttributeID
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Control modes
|
||||
//
|
||||
public:
|
||||
enum ControlMode {
|
||||
BasicMode = 0,
|
||||
StandardMode,
|
||||
VeteranMode
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Look states. A five-state machine off the pod's look buttons; on a state
|
||||
// change InterpretControls calls Mech::CommitLookState, which re-aims the
|
||||
// eyepoint from the model's authored look angles (and, once the weapon wave
|
||||
// lands, re-arms the per-view weapon fire enables).
|
||||
//
|
||||
public:
|
||||
enum LookState {
|
||||
LookNone = 0,
|
||||
LookLeftState,
|
||||
LookRightState,
|
||||
LookBehindState,
|
||||
LookDownState
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
MechControlsMapper(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
CString subsystem_name,
|
||||
RegisteredClass::ClassID class_ID,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
|
||||
~MechControlsMapper();
|
||||
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame control interpretation (the mapper's Performance): reads the
|
||||
// engine-pushed raw input attributes (throttle / stick / pedals / buttons)
|
||||
// and publishes the locomotion + aim demands the mech drive consumes.
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
(MechControlsMapper::*Performance)(Scalar time_slice);
|
||||
void
|
||||
SetPerformance(Performance performance)
|
||||
{
|
||||
Check(this);
|
||||
activePerformance = (Simulation::Performance)performance;
|
||||
}
|
||||
|
||||
void
|
||||
InterpretControls(Scalar time_slice);
|
||||
|
||||
Mech*
|
||||
GetMech() { Check(this); return (Mech *)GetEntity(); }
|
||||
Scalar
|
||||
GetSpeedDemand() const { Check(this); return speedDemand; }
|
||||
Scalar
|
||||
GetTurnDemand() const { Check(this); return turnDemand; }
|
||||
|
||||
//
|
||||
// The LIMP machines write the demand cell DOWN to the damaged side's
|
||||
// speed cap (binary GimpLegClipFinished @004a7970 preamble writes
|
||||
// mapper+0x128 directly) -- a limping mech cannot command more speed
|
||||
// than its limp figure carries.
|
||||
//
|
||||
void
|
||||
SetSpeedDemand(Scalar demand)
|
||||
{ Check(this); speedDemand = demand; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data -- the published control inputs (the streamed mappings write
|
||||
// these; InterpretControls, phase 5.3, reads them to drive the mech).
|
||||
//
|
||||
public:
|
||||
//
|
||||
// The POD ROSTER (binary mapper attributes PilotArrayPage/PilotArray,
|
||||
// ids 15/16): the comm scoreboard's row source. Zero = no roster
|
||||
// authored (a solo mission), which is why the shipped cockpit leaves
|
||||
// that page blank.
|
||||
//
|
||||
int
|
||||
GetPilotArray() const { Check(this); return pilotArray; }
|
||||
int
|
||||
GetPilotArrayPage() const { Check(this); return pilotArrayPage; }
|
||||
|
||||
|
||||
protected:
|
||||
ControlsJoystick stickPosition;
|
||||
Scalar throttlePosition;
|
||||
Scalar pedalsPosition;
|
||||
ControlsButton reverseThrust;
|
||||
Scalar speedDemand;
|
||||
Scalar turnDemand;
|
||||
ControlsButton lookForward;
|
||||
ControlsButton lookLeft;
|
||||
ControlsButton lookRight;
|
||||
ControlsButton lookBehind;
|
||||
ControlsButton lookDown;
|
||||
ControlsButton torsoUp;
|
||||
ControlsButton torsoDown;
|
||||
ControlsButton torsoLeft;
|
||||
ControlsButton torsoRight;
|
||||
ControlsButton torsoCenter;
|
||||
int controlMode;
|
||||
int displayMode;
|
||||
int pilotArrayPage;
|
||||
int pilotArray;
|
||||
int lookState;
|
||||
int previousLookState;
|
||||
//
|
||||
// Takes one reserved word (Scalar and int are both 4 bytes here), so
|
||||
// the class size and every existing offset stay exactly as they were.
|
||||
//
|
||||
Scalar targetRangeExponent; // authored watcher
|
||||
int reserved[21];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user