gauges: reconstruct + register segmentArcRatio -- the cockpit SPEED arc

SegmentArcRatio (config keyword "segmentArcRatio") was declared but undefined.
It is a thin subclass of the engine SegmentArc gauge: two Scalar connections
(numerator=value, denominator=max) and an Execute override that computes the
[0,1] fill fraction into the base's currentValue, then delegates drawing to
SegmentArc::Execute (which lights numerator/denominator of the segments).

Reconstruct Make (@004c62fc) / ctor (@004c6394) / Execute (@004c6488) / dtor +
the methodDescription (rate,modeMask,scalar inner,scalar outer,scalar deg0,
scalar deg1,integer segs,color bg,color fg,attribute numerator,attribute
denominator -- matching the engine SegmentArcNormalized param types; the config
gives inner/outer once and the Make duplicates them into inner0==inner1 /
outer0==outer1).  Execute recovered by disassembling the vtable override
@004c6488 (Ghidra dropped the x87):
  if (denominator < 1) currentValue = 0;
  else currentValue = clamp(|numerator/denominator * segmentSpan|, 0, 1);
  SegmentArc::Execute();
where FUN_004dcd00 = fabs and the ctor precomputes
  segmentSpan = (Scalar)(|n|/(|n|-1)) * 0.75f    // int division => 0.75 for n=36
(_DAT_004c6484 = 0.75f, read live from the binary).  Fixed the header field type
int segmentSpan -> Scalar (the ctor fstp's it and Execute fmul's it).

The config binds numerator=LinearSpeed, denominator=MaxRunSpeed -- both now
resolved by the Mech attribute table -- so the arc sweeps with speed.

Verified live (BT_DEV_GAUGES + drive): the radar-surface SPEED dial now shows a
green segmented arc filling with speed (was a plain ring).  No /FORCE unresolved
externals.  Combat un-regressed (TARGET DESTROYED after 8 hits), 0 crashes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-06 18:50:35 -05:00
co-authored by Claude Opus 4.8
parent 18d58f38f6
commit a2ee1181e6
3 changed files with 159 additions and 10 deletions
+143 -7
View File
@@ -1552,13 +1552,149 @@ void
// segmentSpan = (int)( (float)(|n|/(|n|-1)) * 0.75f ) // _DAT_004c62d4
// Deleting-dtor thunk @004c66b3 -> FUN_00474094 (arc base dtor).
//
// @004c62fc Make / @004c6394 ctor (vtable PTR_FUN_0051898c) -- SegmentArcRatio:
// derives from the second MUNGA L4 arc primitive (FUN_00473f44) and adds TWO
// GaugeConnectionDirectOf<Scalar> feeds (numerator @0x30, denominator @0x31,
// FUN_00474855 x2); precomputes segmentSpan with _DAT_004c6484 (0.75f).
// Make @004c62fc allocates 0xcc and supplies the arc geometry resources
// (DAT_00517eb0..00518158) and the name "SegmentArcRatio".
// Deleting-dtor thunk @004c668d -> FUN_00474094.
//###########################################################################
// SegmentArcRatio @004c62fc Make / @004c6394 ctor / @004c6488 Execute
// (vtable PTR_FUN_0051898c; base = engine SegmentArc)
//
// A segmented arc dial (config keyword "segmentArcRatio"). Two Scalar
// connections drive it -- numerator (value) and denominator (max) -- and Execute
// lights numerator/denominator of the arc. Used for the cockpit SPEED arc
// (config binds numerator=LinearSpeed, denominator=MaxRunSpeed), so the arc
// sweeps as the mech accelerates. Unlike VertTwoPartBar, the drawing is
// inherited from the engine SegmentArc base -- this class only computes the
// [0,1] fill fraction into the base's currentValue, then delegates the render.
//###########################################################################
MethodDescription
SegmentArcRatio::methodDescription =
{
"segmentArcRatio",
SegmentArcRatio::Make,
{
//
// CFG shape (L4GAUGE.CFG:4964):
// segmentArcRatio( rate, mode, inner,outer, deg0,deg1, segs,
// bg,fg, valueAttr, maxAttr )
// e.g. segmentArcRatio(C,ModeAlwaysActive,32,39,0,360,36,0,2,
// LinearSpeed, MaxRunSpeed);
//
{ ParameterDescription::typeRate, NULL }, // rate ID
{ ParameterDescription::typeModeMask, NULL }, // mode mask
{ ParameterDescription::typeScalar, NULL }, // inner radius
{ ParameterDescription::typeScalar, NULL }, // outer radius
{ ParameterDescription::typeScalar, NULL }, // start angle (deg)
{ ParameterDescription::typeScalar, NULL }, // end angle (deg)
{ ParameterDescription::typeInteger, NULL }, // segment count (+dir)
{ ParameterDescription::typeColor, NULL }, // background colour
{ ParameterDescription::typeColor, NULL }, // foreground colour
{ ParameterDescription::typeAttribute, NULL }, // numerator (value)
{ ParameterDescription::typeAttribute, NULL }, // denominator (max)
PARAMETER_DESCRIPTION_END
}
};
//
// @004c62fc -- Make. Allocate + construct; no resource to verify (returns True).
//
Logical
SegmentArcRatio::Make(
int display_port_index,
Vector2DOf<int> position,
Entity * /*entity -- unused*/,
GaugeRenderer *gauge_renderer
)
{
ParameterDescription *p = methodDescription.parameterList;
SegmentArcRatio *gauge = (SegmentArcRatio *)operator new(0xcc); // FUN_00402298(0xcc)
if (gauge != NULL)
{
new (gauge) SegmentArcRatio( // FUN_004c6394
p[0].data.rate, p[1].data.modeMask,
(L4GaugeRenderer *)gauge_renderer,
display_port_index, // graphics_port_number
position.x, position.y, // centre
p[2].data.scalar, p[3].data.scalar, // inner, outer radius
p[4].data.scalar, p[5].data.scalar, // start, end angle
p[6].data.integer, // segment count (+dir)
p[7].data.color, p[8].data.color, // bg, fg
(Scalar *)p[9].data.attributePointer, // numerator source
(Scalar *)p[10].data.attributePointer, // denominator source
"SegmentArcRatio");
}
return True; // binary returns 1 unconditionally
}
//
// @004c6394 -- ctor. Engine SegmentArc base (inner0==inner1, outer0==outer1 --
// the Make duplicates them, so the arc has a constant radius); precompute the
// span factor and wire the two Scalar connections.
//
SegmentArcRatio::SegmentArcRatio(
GaugeRate rate,
ModeMask mode_mask,
L4GaugeRenderer *renderer_in,
int graphics_port_number,
int center_x,
int center_y,
Scalar inner,
Scalar outer,
Scalar deg0,
Scalar deg1,
int number_of_segs,
int background_color,
int foreground_color,
Scalar *numerator_pointer,
Scalar *denominator_pointer,
const char *identification_string
):
SegmentArc(rate, mode_mask, renderer_in, 0, // FUN_00473f44 (owner_ID 0)
graphics_port_number, center_x, center_y,
inner, outer, inner, outer, // inner0/outer0 == inner1/outer1
deg0, deg1, number_of_segs,
background_color, foreground_color,
True, // use_thick_lines (binary param_20 = 1)
identification_string)
{
// segmentSpan = (Scalar)(|n| / (|n|-1)) * 0.75f -- |n|/(|n|-1) is INTEGER
// division (== 1 for n>=2), so this is 0.75 for the 36-segment speed arc.
int n = (number_of_segs < 0) ? -number_of_segs : number_of_segs; // @0xC8
segmentSpan = (Scalar)(n / (n - 1)) * 0.75f; // _DAT_004c6484 = 0.75f
AddConnection(new GaugeConnectionDirectOf<Scalar>(0, &numerator, numerator_pointer)); // @0xC0
AddConnection(new GaugeConnectionDirectOf<Scalar>(0, &denominator, denominator_pointer)); // @0xC4
}
//
// @004c668d -- deleting-dtor thunk. Connections + arc base are released by the
// implicit base-dtor chain (FUN_00474094); no own teardown.
//
SegmentArcRatio::~SegmentArcRatio()
{
}
//
// @004c6488 -- Execute. currentValue = clamp(|numerator/denominator * span|,0,1);
// then delegate to the engine SegmentArc::Execute (0x474300) which lights that
// fraction of the segments. (x87 recovered by disassembly: FUN_004dcd00 = fabs.)
//
void
SegmentArcRatio::Execute()
{
if (denominator < 1.0f) // guard div-by-tiny / unpowered
{
currentValue = 0.0f;
}
else
{
currentValue = numerator / denominator * segmentSpan;
if (currentValue < 0.0f) currentValue = -currentValue; // FUN_004dcd00 = fabs
if (currentValue < 0.0f)
currentValue = 0.0f;
else if (currentValue > 1.0f)
currentValue = 1.0f;
}
SegmentArc::Execute(); // 0x474300 -- the base draw
}
// === btl4gau2.cpp begins at @004c6798 (SeekVoltage gauge) -- not part of this TU.
+15 -3
View File
@@ -518,16 +518,28 @@
};
class SegmentArcRatio :
public SegmentArc // MUNGA L4 base (FUN_00473f44; engine class SegmentArc)
public SegmentArc // MUNGA L4 base (engine SegmentArc, L4GAUGE.cpp:4827)
{
public:
// Registered in BTL4MethodDescription[] (btl4grnd.cpp) as "segmentArcRatio".
static MethodDescription methodDescription;
static Logical Make(int, Vector2DOf<int>, Entity *, GaugeRenderer *); // @004c62fc
SegmentArcRatio(/* ...arc geometry... */); // @004c6394
SegmentArcRatio( // @004c6394
GaugeRate, ModeMask, L4GaugeRenderer *, int graphics_port_number,
int center_x, int center_y,
Scalar inner, Scalar outer, Scalar deg0, Scalar deg1,
int number_of_segs, int background_color, int foreground_color,
Scalar *numerator_pointer, Scalar *denominator_pointer,
const char *identification_string);
~SegmentArcRatio(); // dtor thunk @004c668d
void Execute(); // @004c6488 (ratio -> currentValue -> SegmentArc::Execute)
protected:
Scalar numerator; // @0xC0 this[0x30] (connection)
Scalar denominator; // @0xC4 this[0x31] (connection)
int segmentSpan; // @0xC8 this[0x32]
Scalar segmentSpan; // @0xC8 this[0x32] (float, read by Execute)
};
#endif
+1
View File
@@ -135,6 +135,7 @@ MethodDescription
&ColorMapperMultiArmor::methodDescription, // "colorMapperMultiArmor" -- worst-of-8-zones tint
&ColorMapperCritical::methodDescription, // "cmCrit" -- subsystem-critical-state tint
&VertTwoPartBar::methodDescription, // "vertBar" -- vertical fill bar (coolant/heat)
&SegmentArcRatio::methodDescription, // "segmentArcRatio" -- segmented arc dial (speed)
&BTL4ChainToPrevious
};