diff --git a/game/reconstructed/btl4gau2.cpp b/game/reconstructed/btl4gau2.cpp index 79f230b..6c81068 100644 --- a/game/reconstructed/btl4gau2.cpp +++ b/game/reconstructed/btl4gau2.cpp @@ -132,6 +132,16 @@ static void *AttributePointerOf(void *subsystem, const char *name) // static void *ResolveLink(void *plug) { (void)plug; return 0; } +// +// Bridge (defined in powersub.cpp) that reads the engineering-screen assignment +// off the real PoweredSubsystem type (auxScreenNumber/Placement/Label) -- the +// reconstructed heat-leaf branch is not byte-exact, so the raw sub+0x1dc/0x1e0/ +// 0x1e4 offsets read garbage; the named fields (populated by the PoweredSubsystem +// ctor from the resource) are correct. Returns False for non-PoweredSubsystem- +// derived subsystems (== the vehicleSubSystems type filter). +// +extern bool BTGetSubsystemAuxScreen(Subsystem *sub, int *screen, int *placement, char *label64); + // // FUN_0041bf44 -- fetch the subsystem linked at slot N (used by the cooling-loop // lamp to find its cooling master). Stubbed to match ResolveLink above. @@ -277,10 +287,44 @@ SeekVoltageGraph::SeekVoltageGraph( // // @004c68ac -- dtor. Release the destroyed bitmap, GraphicGauge::~GraphicGauge. +// +SeekVoltageGraph::~SeekVoltageGraph() +{ + L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer; + warehouse->bitMapBin.Release(destroyedImage); +} + +Logical SeekVoltageGraph::TestInstance() const +{ + return GraphicGauge::TestInstance(); +} + // // @004c6920 -- BecameActive: previousVoltage = 9999.0f (force first redraw). // -// @004c6934 -- Execute. If the subsystem is destroyed (subsys+0x40 == 1) draw +void SeekVoltageGraph::BecameActive() +{ + previousVoltage = RedrawSentinel; // @0xAC force first redraw + destroyedShown = 0; +} + +// +// @004c6934 -- Execute (the seek-voltage curve plot). BRING-UP: the full plot +// (SetupClip/DrawTicks/DrawCursor over a 0..1 voltage polyline) needs the live +// seek-voltage sampler (subsys vtbl+0x3c) + the four attribute indices; the +// attribute resolve is summarised (FindAttributeIndex returns -1), so the plot is +// deferred and Execute is inert until the sampler + attribute reads land. Kept as +// a defined no-op so the gauge's vtable is complete (no /FORCE crash when the +// Myomer/Energy cluster ticks its SeekVoltageGraph child). +// +void SeekVoltageGraph::Execute() +{ + // (seek-voltage plot deferred -- see note above.) +} + +// +// @004c6934 (original notes preserved) -- Execute. If the subsystem is destroyed +// (subsys+0x40 == 1) draw // the centred "destroyed" bitmap once. Otherwise, when the live seek voltage // (subsys vtbl+0x3c sampler) changes: clear (SetupClip @004c6be4), re-plot the // 0..1 voltage curve as a polyline (step _DAT_004c6bdc up to _DAT_004c6be0, @@ -751,8 +795,10 @@ SubsystemCluster::SubsystemCluster( mfd_port, x + 0x10e, y + 0xe, "btpbus.pcc", 5, 1, inputVoltage, "PowerSource"); - // Background: image_names[ subsystem damageZoneIndex@0x1e0 ] if present. - int dzIndex = *(int *)((char *)subsystem_in + 0x1e0); // BEST-EFFORT raw offset + // Background: image_names[ auxScreenPlacement ] if present. (Placement@0x1e0 is + // read via the PoweredSubsystem bridge, not the non-byte-exact raw offset.) + int dzIndex = -1; + BTGetSubsystemAuxScreen(subsystem_in, NULL, &dzIndex, NULL); // auxScreenPlacement background = NULL; if (dzIndex >= 0 && image_names[dzIndex] != NULL) background = new BackgroundBitmap(mfd_mode, renderer_in, owner_ID, // @00471d00 @@ -814,8 +860,9 @@ SubsystemCluster::SubsystemCluster( (Scalar *)linkTemp, (Scalar *)linkDegrade, (Scalar *)failTemp, "VertTwoPartBar"); leakGauge = new BitMapInverseWipe(ChildRate(), eng_mode, renderer_in, // @004c5b7c - engPort, 0x255, 0xe0, "eleak.pcc", 0, 0xff, 3, - *(int *)((char *)subsystem_in + 0x150), (Scalar *)coolantLeak, "LeakGauge"); // BEST-EFFORT raw + engPort, 0x255, 0xe0, "eleak.pcc", 0, 0xff, + *(int *)((char *)subsystem_in + 0x150) /*third*/, 3 /*frames*/, + (Scalar *)coolantLeak, "LeakGauge"); // BEST-EFFORT raw (subsys+0x150) operating = False; // @0xC4 this[0x31] subsystem = subsystem_in; // @0xC0 this[0x30] @@ -1380,13 +1427,19 @@ Logical Subsystem *sub = entity->GetSubsystem(i); // entity+0x128[i] if (sub == NULL) continue; - if (!SubsystemDisplayFilter(sub)) // FUN_0041a1a4 + + // Read the engineering-screen assignment via the PoweredSubsystem bridge + // (also the type filter -- False == not PoweredSubsystem-derived). + int auxScreen = 0, placement = -1; + char labelBuf[64]; + if (!BTGetSubsystemAuxScreen(sub, &auxScreen, &placement, labelBuf)) // FUN_0041a1a4 + sub[0x1dc]/[0x1e4] continue; - int group = *(int *)((char *)sub + 0x1dc) - 1; // aux-screen position 1..12 + int group = auxScreen - 1; // aux-screen position 1..12 if (getenv("BT_VSS_LOG")) DEBUG_STREAM << "[vss] i=" << i << " classID=" << (int)sub->GetClassID() - << " auxScreen[0x1dc]=" << (group + 1) << "\n" << std::flush; + << " auxScreen=" << auxScreen << " placement=" << placement + << " label='" << labelBuf << "'\n" << std::flush; if (group < 0) { DebugStream << "Auxiliary screen position = zero\n"; @@ -1400,7 +1453,7 @@ Logical const AuxScreenGeometry &g = kAuxGeom[group]; int mfdPort = renderer->FindGraphicsPort((char *)g.mfdPortName); // FUN_00447f1c - char *label = (char *)((char *)sub + 0x1e4); // the subsystem label + char *label = labelBuf; // the subsystem label (bridge) char *tile = (char *)"btwarn.pcc"; switch (sub->GetClassID()) // sub+4 diff --git a/game/reconstructed/btl4gaug.cpp b/game/reconstructed/btl4gaug.cpp index 3dca203..ff71efc 100644 --- a/game/reconstructed/btl4gaug.cpp +++ b/game/reconstructed/btl4gaug.cpp @@ -1501,6 +1501,228 @@ OneOfSeveralPixInt::~OneOfSeveralPixInt() } +//########################################################################### +// Additional composite-cluster child gauges (used by btl4gau2's +// SubsystemCluster family). HorizTwoPartBar mirrors VertTwoPartBar +// (horizontal); OneOfSeveralInt/States are OneOfSeveral subclasses; the +// LeakGauge base BitMapInverseWipe reveals a bitmap up to a level. +//########################################################################### + +// +// StateConnection -- @004c3324 ctor / @004c3390 Transfer. Copies a Subsystem's +// state word (@0x14) into the destination each frame (drives OneOfSeveralStates). +// +class StateConnection : public GaugeConnection +{ +public: + StateConnection(int *destination, Entity *source) + : GaugeConnection(0), source(source), destination(destination) {} // FUN_004c3324 + void Update() // @004c3390 + { + *destination = *(int *)((char *)source + 0x14); // Subsystem state @0x14 + } +protected: + Entity *source; // @0x10 + int *destination; // @0x14 +}; + +// +// @004c4170 -- HorizTwoPartBar ctor (vtable 0x518cbc). GraphicGauge base, intern +// the tile bitmap, SetExtent, store fill/bg + width/height, wire the value/low/ +// high Scalar connections. Grows left->right (mirror of VertTwoPartBar). +// +HorizTwoPartBar::HorizTwoPartBar( + GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in, + int graphics_port_number, int left, int bottom, int right, int top, + const char *tile_image, int fill_color, int background_color, + Scalar *value_pointer, Scalar *low_pointer, Scalar *high_pointer, + const char *identification_string +): + GraphicGauge(rate, mode_mask, renderer_in, 0, graphics_port_number, // FUN_00444818 + identification_string) +{ + tileImage = new char[strlen(tile_image) + 1]; + strcpy(tileImage, tile_image); + L4Warehouse *warehouse = (L4Warehouse *)renderer_in->warehousePointer; + warehouse->bitMapBin.Get(tileImage); + localView.SetPositionWithinPort(left, bottom, right, top); // vtbl+0x08 + fillColor = fill_color; + backgroundColor = background_color; + width = right - left; + height = (top - bottom) - 1; + AddConnection(new GaugeConnectionDirectOf(0, &value, value_pointer)); + AddConnection(new GaugeConnectionDirectOf(0, &low, low_pointer)); + AddConnection(new GaugeConnectionDirectOf(0, &high, high_pointer)); +} + +HorizTwoPartBar::~HorizTwoPartBar() +{ + L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer; + warehouse->bitMapBin.Release(tileImage); + delete[] tileImage; + tileImage = NULL; +} + +Logical HorizTwoPartBar::TestInstance() const { return GraphicGauge::TestInstance(); } + +void HorizTwoPartBar::BecameActive() // @004c4324 +{ + previousFull = 0; + previousFill = width; +} + +// +// @004c4340 -- Execute: clamp value to [0,high], map value/warn to bar pixels +// (half-up round of width*x/high), repaint on change (mirror of VertTwoPartBar +// but along X). +// +void HorizTwoPartBar::Execute() +{ + if (value < 0.0f) value = 0.0f; + else if (value > high) value = high; + + int warnPix = (int)((Scalar)width * low / high + 0.5f); + int valPix = (int)((Scalar)width * value / high + 0.5f); + if (warnPix < 0) warnPix = 0; else if (warnPix > width) warnPix = width; + if (valPix < 0) valPix = 0; else if (valPix > width) valPix = width; + + if (warnPix != previousFull || valPix != previousFill) + { + if (warnPix < valPix) + { + localView.SetColor(fillColor); + localView.MoveToAbsolute(warnPix, 0); + localView.DrawFilledRectangleToAbsolute(valPix - 1, height); + } + if (valPix < width) + { + localView.SetColor(backgroundColor); + localView.MoveToAbsolute(valPix, 0); + localView.DrawFilledRectangleToAbsolute(width, height); + } + previousFull = warnPix; + previousFill = valPix; + } +} + +// +// @004c5148 -- OneOfSeveralInt ctor (vtable 0x518bac): OneOfSeveral(fromStrip=1) + +// a GaugeConnectionDirectOf feeding the selected frame. dtor @004c51d8. +// +OneOfSeveralInt::OneOfSeveralInt( + GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in, + int graphics_port_number, int x, int y, const char *image, + int columns, int rows, int *value_pointer, const char *identification_string +): + OneOfSeveral(rate, mode_mask, renderer_in, graphics_port_number, x, y, + True, image, 0, 0, columns, rows, identification_string) +{ + selected = 0; + AddConnection(new GaugeConnectionDirectOf(0, &selected, value_pointer)); // FUN_004749de +} + +OneOfSeveralInt::~OneOfSeveralInt() {} // @004c51d8 + +// +// @004c5470 -- OneOfSeveralStates ctor (vtable 0x518b24): OneOfSeveral + a +// StateConnection reading the subsystem state word @0x14. BecameActive @004c552c +// clamps the state >= 0 then chains OneOfSeveral::Execute. dtor @004c5500. +// +OneOfSeveralStates::OneOfSeveralStates( + GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in, + int graphics_port_number, int x, int y, const char *image, + int columns, int rows, Entity *subsystem_source, const char *identification_string +): + OneOfSeveral(rate, mode_mask, renderer_in, graphics_port_number, x, y, + True, image, 0, 0, columns, rows, identification_string) +{ + selected = 0; + AddConnection(new StateConnection(&selected, subsystem_source)); // FUN_004c3324 +} + +OneOfSeveralStates::~OneOfSeveralStates() {} // @004c5500 + +void OneOfSeveralStates::BecameActive() // @004c552c +{ + if (selected < 0) + selected = 0; + OneOfSeveral::BecameActive(); +} + +// +// @004c5b7c -- BitMapInverseWipe ctor (vtable 0x518a9c): GraphicGauge base; +// SetOrigin(x,y), intern the image, store colours + frame geometry (frameWidth = +// imageWidth/3, fullWidth = frames*2), wire a Scalar level connection. Used as +// the coolant LeakGauge. +// +BitMapInverseWipe::BitMapInverseWipe( + GaugeRate rate, ModeMask mode_mask, L4GaugeRenderer *renderer_in, + int graphics_port_number, int x, int y, const char *image, + int color_a, int color_b, int third, int frames, + Scalar *value_pointer, const char *identification_string +): + GraphicGauge(rate, mode_mask, renderer_in, 0, graphics_port_number, + identification_string) +{ + localView.SetOrigin(x, y); // vtbl+0x10 + imageName = new char[strlen(image) + 1]; + strcpy(imageName, image); + colorA = color_a; + colorB = color_b; + this->frames = frames; + fullWidth = frames * 2; + this->third = third; + L4Warehouse *warehouse = (L4Warehouse *)renderer_in->warehousePointer; + BitMap *bmp = warehouse->bitMapBin.Get(imageName); + if (bmp == NULL) + { + frameWidth = 0; + frameHeight = 0; + } + else + { + frameWidth = bmp->Data.Size.x / 3; + frameHeight = bmp->Data.Size.y; + } + AddConnection(new GaugeConnectionDirectOf(0, &value, value_pointer)); // FUN_00474855 +} + +BitMapInverseWipe::~BitMapInverseWipe() // @004c5c80 +{ + L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer; + warehouse->bitMapBin.Release(imageName); + delete[] imageName; + imageName = NULL; +} + +Logical BitMapInverseWipe::TestInstance() const { return GraphicGauge::TestInstance(); } + +void BitMapInverseWipe::BecameActive() // @004c5cf4 +{ + previousLevel = -1; +} + +// +// @004c5d08 -- Execute. BRING-UP: the authentic wipe (reveal the bitmap columns +// up to the level, coloured by colorA/colorB) needs the frame-strip blit math; +// a safe minimal repaint (blit the base frame on level change) keeps the vtable +// complete + non-crashing until the full wipe lands. +// +void BitMapInverseWipe::Execute() +{ + int level = (int)value; + if (level != previousLevel) + { + previousLevel = level; + L4Warehouse *warehouse = (L4Warehouse *)renderer->warehousePointer; + BitMap *bmp = warehouse->bitMapBin.Get(imageName); + if (bmp != NULL) + localView.DrawBitMapOpaque(0, 0, bmp); + warehouse->bitMapBin.Release(imageName); + } +} + + //########################################################################### //########################################################################### // HeadingPointer @004c554c Make / @004c562c ctor diff --git a/game/reconstructed/powersub.cpp b/game/reconstructed/powersub.cpp index cff28d3..e42b391 100644 --- a/game/reconstructed/powersub.cpp +++ b/game/reconstructed/powersub.cpp @@ -1258,3 +1258,26 @@ Subsystem *CreatePoweredSubsystem(Mech *owner, int id, void *seg) return (Subsystem *) new (Memory::Allocate(0x31c)) PoweredSubsystem(owner, id, (PoweredSubsystem::SubsystemResource *)seg, PoweredSubsystem::DefaultData); } + +// +// Bridge for the vehicleSubSystems gauge factory (btl4gau2.cpp). The engineering- +// screen assignment lives on PoweredSubsystem (auxScreenNumber / auxScreenPlacement +// / auxScreenLabel, resource +0x104/+0x108/+0x10C). Reading it from btl4gau2.cpp +// via a raw offset (sub+0x1dc) is wrong because the reconstructed heat-leaf branch +// is not byte-exact -- so the gauge factory calls this bridge, which casts through +// the real PoweredSubsystem type and returns the NAMED fields. Returns False for +// non-PoweredSubsystem-derived subsystems (== the FUN_0041a1a4 / 0x50f4bc type +// filter the vehicleSubSystems Make applies before dispatch). +// +bool BTGetSubsystemAuxScreen(Subsystem *sub, int *screen, int *placement, char *label64) +{ + if (sub == NULL) + return false; + if (!sub->IsDerivedFrom(*PoweredSubsystem::GetClassDerivations())) // FUN_0041a1a4(...,0x50f4bc) + return false; + PoweredSubsystem *ps = (PoweredSubsystem *)sub; + if (screen != NULL) *screen = ps->auxScreenNumber; // +0x104 + if (placement != NULL) *placement = ps->auxScreenPlacement; // +0x108 + if (label64 != NULL) strcpy(label64, ps->auxScreenLabel); // +0x10C + return true; +}