Mech milestone: reconstruct SequenceController (helper 2/3), struct+Init verified

The BT keyframe-animation player embedded in the Mech as legAnimation/
bodyAnimation. Struct field layout (from BT411's binary-offset map), default
ctor, dtor, and Init(Mech*) reconstructed and compile-verified; Init is
ctor-time (Mech ctor calls legAnimation.Init(this)/bodyAnimation.Init(this)) so
it's real (binds owner + caches owner->GetJointSubsystem()). The per-frame gait
engine (SelectSequence/Advance/Reset) is staged with Fail bodies -- it fires
only once the mech is ticking, past the current ctor frontier.

BT stage 23 ok; full tree still links clean. Evidence + real/staged split in
SEQCTL.NOTES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-19 20:53:54 -05:00
co-authored by Claude Fable 5
parent 9a4f1c293b
commit bc59e39f41
3 changed files with 224 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
//===========================================================================//
// File: seqctl.cpp //
// Project: BattleTech Brick: Mech animation //
// Contents: The Mech keyframe-animation player (leg / body gait clips) //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <bt.hpp>
#pragma hdrstop
#if !defined(SEQCTL_HPP)
# include <seqctl.hpp>
#endif
#if !defined(MECH_HPP)
# include <mech.hpp>
#endif
//
//#############################################################################
//#############################################################################
//
SequenceController::SequenceController()
{
keyframeCount = 0;
jointCount = 0;
footStepThreshold = NULL;
jointIndices = NULL;
keyframeTimes = NULL;
keyframeBase = NULL;
keyframeCursor = NULL;
keyframeData = NULL;
currentFrame = 0;
currentTime = 0.0f;
jointSubsystem = NULL;
owner = NULL;
clipResource = NULL;
finishedCallback = NULL;
callbackArg2 = 0;
callbackArg3 = 0;
}
//
//#############################################################################
// Init Bind the player to its owning mech and cache the joint subsystem the
// gait writes through. Called from the Mech ctor for legAnimation/bodyAnimation.
//#############################################################################
//
void
SequenceController::Init(Mech *owner_mech)
{
Check(owner_mech);
owner = owner_mech;
jointSubsystem = owner_mech->GetJointSubsystem();
clipResource = NULL;
}
//
//#############################################################################
//#############################################################################
//
SequenceController::~SequenceController()
{
//
// Releases the locked clip resource when reconstructed; nothing is locked
// while SelectSequence is staged.
//
}
//
//#############################################################################
// Playback -- the per-frame gait engine (clip parse, keyframe interpolation,
// joint writes). Exercised only once the mech is ticking; not yet
// reconstructed. See SEQCTL.NOTES.md.
//#############################################################################
//
void
SequenceController::SelectSequence(int, void *, unsigned, unsigned)
{
Fail("SequenceController::SelectSequence -- seqctl.cpp not yet reconstructed");
}
Scalar
SequenceController::Advance(Scalar, int)
{
Fail("SequenceController::Advance -- seqctl.cpp not yet reconstructed");
return 0.0f;
}
void
SequenceController::Reset(int)
{
Fail("SequenceController::Reset -- seqctl.cpp not yet reconstructed");
}
+86
View File
@@ -0,0 +1,86 @@
#if !defined(SEQCTL_HPP)
# define SEQCTL_HPP
//##################### Forward Class Declarations #######################
class Mech;
class JointSubsystem;
//##########################################################################
//###################### SequenceController ##########################
//##########################################################################
//
// The BT keyframe-animation player embedded in the Mech as legAnimation and
// bodyAnimation. It plays a gait clip: walks the clip's keyframes,
// interpolates each animated joint's rotation, writes it through the joint
// subsystem, and returns the root-translation distance advanced this step.
//
// Reconstructed from the shipped binary (ctor/Init @00427768, SelectSequence
// @004277a8, Advance @0042790c, Reset @004283b8). Not in the surviving 4.10
// archive nor the RP engine. See SEQCTL.NOTES.md.
//
class SequenceController
{
public:
//
// One root-translation entry (per keyframe); ".stride" is the forward
// (local Z) distance the gait reads.
//
struct Keyframe
{
Scalar x, y, stride;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction / Destruction
//
public:
SequenceController();
~SequenceController();
void
Init(Mech *owner_mech);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Playback
//
public:
void
SelectSequence(
int clip_id,
void *finished_callback,
unsigned callback_arg2,
unsigned callback_arg3
);
Scalar
Advance(Scalar time_slice, int move_joints);
void
Reset(int loop);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Clip data (parsed by SelectSequence; keyframe accessors read by the gait)
//
public:
int keyframeCount;
int jointCount;
Scalar *footStepThreshold;
int *jointIndices;
Scalar *keyframeTimes;
void *keyframeBase;
void *keyframeCursor;
Keyframe *keyframeData;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Runtime state / links
//
public:
int currentFrame;
Scalar currentTime;
JointSubsystem *jointSubsystem;
Mech *owner;
void *clipResource;
void *finishedCallback;
unsigned callbackArg2;
unsigned callbackArg3;
};
#endif
+41
View File
@@ -0,0 +1,41 @@
# SEQCTL.HPP / .CPP — reconstruction notes
**Status: struct + ctor/dtor + Init RECONSTRUCTED (compile-verified); the
per-frame playback methods (SelectSequence / Advance / Reset) staged. Helper
2/3 of the Mech embedded helpers ([[MECH-LAYOUT]] step 1).**
`SequenceController` — the BT keyframe-animation player embedded in the Mech as
`legAnimation` (@0x65c) and `bodyAnimation` (@0x6bc). Plays a gait clip: walks
keyframes, interpolates each animated joint's rotation, writes it through the
joint subsystem, returns the root-translation distance advanced. Absent from the
4.10 archive and the RP engine; reconstructed from the shipped binary + BT411's
seqctl.cpp decomp.
## What's real vs staged
- **Real (ctor-time):** default ctor (zeroes all fields) and `Init(Mech*)` — the
Mech ctor calls `legAnimation.Init(this)` / `bodyAnimation.Init(this)`
(binary; BT411 mech.cpp:685-686), so Init must run at construction. Init binds
`owner` and caches `jointSubsystem = owner->GetJointSubsystem()`
(JMOVER.HPP:104, returns `JointSubsystem*`), `clipResource = NULL`.
- **Staged (per-frame, after boot):** `SelectSequence` (@004277a8, clip parse +
resource lock), `Advance` (@0042790c, keyframe interpolation + joint writes —
the gait engine, ~145 lines in the decomp), `Reset` (@004283b8). These fire
only once the mech is ticking, well past the current ctor frontier, so they
Fail loudly until reconstructed.
## Field layout
Field set + order from BT411's binary-offset map (keyframeCount +0x14 …
callbackArg3 +0x50). Byte-exact offsets are not required for the functional
build (only wire/update-record layout needs that, and SequenceController is not
serialised) — the members are declared as named fields and BC4.52 lays them out;
the Mech's legAnimation/bodyAnimation slots size to `sizeof(SequenceController)`
self-consistently.
## Placement
1995 filename unknown (decomp-only; BT411 used seqctl.cpp). Filed as
BT/SEQCTL.HPP + SEQCTL.CPP. Not in the authentic BT.MAK member list, so the
build appends seqctl.obj to bt.lib via the extra-objs fallback (order is
immaterial — no static init).