//===========================================================================// // File: dmgtable.hpp // // Project: BattleTech Brick: Entity Manager // // Contents: Damage lookup table -- layered / pie-slice hit-location selector // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // --/--/95 ?? Initial coding. // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved // // PROPRIETARY AND CONFIDENTIAL // //===========================================================================// // // RECONSTRUCTED from the shipped binary. No header survived for this module // (dmgtable.hpp/.cpp). The class cluster lives at @0x49de14..@0x49f5fb, was // found ADJACENT to (but separate from) the DamageZone code in mechdmg.cpp, // and is streamed from the ".tbl" notation file by the mech's // CreateDamageZoneStream (@0x4a474c, mech.cpp) -- caller sites @0x49ed28 is // invoked from the per-zone parser @line~11580 and the streamed form @0x49ea48 // from the object-stream reader @line~10423. // // Identifying strings (data @0x50bc00..0x50bd00): // "DamageZone" "video" "skeleton" "%s %f" "RotateWithTorso" "PieSlice" // "Layer" "TableEntryOf" "DamageZone Table Format!" "Not Found!" // "percentages do not sum to 100%(i.e., 1.0)!" "missing RotateWithTorso!" // "error in damage table format" " has bad format!" // vtables: 0x50bd84 (DamageLookupTable), 0x50bd90 (PieSlice), // 0x50bd9c (DamageZonePercentTable / leaf). // // The table is a three-level hit-location resolver: // // DamageLookupTable -- stack of armour LAYERS (penetration depth) // -> PieSlice -- an angular sector wheel (optionally torso-relative) // -> DamageZonePercentTable // -- a weighted list of {zone segment index, percentage} // whose percentages must sum to 1.0; a uniform random // roll then selects the final damage-zone index. // // Class names are reconstructed from the .tbl section keywords; member names // are inferred from usage and flagged best-effort. See dmgtable.cpp for the // per-method @ADDR evidence. // #if !defined(DMGTABLE_HPP) # define DMGTABLE_HPP #if !defined(PLUG_HPP) # include #endif #if !defined(TABLE_HPP) # include #endif #include "mechrecon.hpp" // reconstruction shim //##################### Forward Class Declarations ####################### class Mech; class NotationFile; class GenericObjectStream; class Point3D; //########################################################################### //##################### DamageZonePercentTable ########################## //########################################################################### // // Leaf node. Holds a sorted, cumulative-weighted list of damage-zone // segment indices and the uniform random "roll" that turns a [0,1) draw into // one of those zones. Built from a ".tbl" "DamageZone" page whose lines are // " " ("%s %f") and whose percentages must total 1.0. // (vtable @0x50bd9c, ctor @0x49deb0, dtor @0x49df80, object size 0x2c.) // class DamageZonePercentTable: public Plug { public: // // Stream constructor -- rebuilds the entry list from an object stream. // @0x49deb0 (FUN_004178cc base Plug ctor, then ReadEntries @0x49e5e4) // DamageZonePercentTable() {} // reconstruction: stack-temp default ctor DamageZonePercentTable( Mech *owner, GenericObjectStream *stream ); ~DamageZonePercentTable(); // @0x49df80 Logical TestInstance() const; // @0x49e000 (returns True) // // @0x49de14 -- uniform random weighted roll. Walks the entries in // ascending cumulative-percentage order, draws a [0,1) sample // (FUN_00408050) and returns the segment index (entry+0xc) of the first // entry whose cumulative threshold (entry+0x14) exceeds the draw. // Returns -1 if the list is empty. // int SelectZone() const; // // @0x49e00c -- parse a "DamageZone" page from the model .tbl: read the // "video"/"skeleton" model, then each " " line; resolve the // zone name to a skeleton segment index (Get_Segment_Index @0x4274f8), // accumulate the percentages and verify the total == 1.0. // Returns False (and logs) on a bad line, an unknown zone, or a total // that does not sum to 1.0. // static int BuildFromNotation( NotationFile *model_file, const char *model_name, const char *page_name, NotationFile *table_file, Mech *owner, GenericObjectStream *stream ); // // @0x49e524 WriteEntries (serialise count + {threshold,zone} pairs), // @0x49e5e4 ReadEntries (deserialise into the entry table). // void WriteEntries(GenericObjectStream *stream) const; // @0x49e524 void ReadEntries (GenericObjectStream *stream); // @0x49e5e4 protected: Mech *owner; // @0x0c param_1[3] void *refCount; // @0x10 param_1[4] (MemoryBlock/RefCount) ReconTable entries; // @0x14 param_1+5 sorted by cumulative % // each entry: zoneIndex@+0xc, cumulativePercent@+0x14 }; //########################################################################### //############################# PieSlice ################################ //########################################################################### // // An angular sector "wheel". N equal-angle slices (each 2*PI/N radians) wrap // a circle; the slice that contains the incoming hit direction is chosen and // its leaf DamageZonePercentTable is rolled. When "RotateWithTorso" is set // the incoming angle is measured relative to the live torso orientation // (orientationSource+0x1d8), so the slice layout turns with the mech's torso. // (vtable @0x50bd90, ctor @0x49e740, object size 0x30.) // class PieSlice: public Plug { public: // // @0x49e740 -- stream constructor. Reads owner@+0xc and sliceCount@+0x2c, // builds 'sliceCount' DamageZonePercentTable children (@0x49deb0) each // keyed to a cumulative angle i*(2*PI/sliceCount), and caches the torso // orientation source = owner+0x438. // PieSlice( Mech *owner, GenericObjectStream *stream ); ~PieSlice(); // // @0x49e678 -- pick a slice for incoming angle 'theta' (radians). When // rotateWithTorso is set, theta is shifted by the torso orientation // (orientationSource+0x1d8) and wrapped into [0, 2*PI). The slice index // = floor(theta * sliceCount / (2*PI)) feeds the child table lookup. // DamageZonePercentTable * SelectSlice(Scalar theta) const; // // @0x49e88c -- parse a "PieSlice" page: read the mandatory bool // "RotateWithTorso" (logged "missing RotateWithTorso!" if absent), then // each named sub-page as a DamageZonePercentTable (@0x49e00c). // Logs "error in damage table format" on a malformed entry. // static int BuildFromNotation( NotationFile *model_file, const char *model_name, const char *page_name, NotationFile *table_file, Mech *owner, GenericObjectStream *stream ); protected: Mech *owner; // @0x0c param_1[3] void *orientationSource; // @0x10 param_1[4] = owner+0x438 (torso) ReconTable slices; // @0x14 param_1+5 int rotateWithTorso; // @0x28 (read first in BuildFromNotation) int sliceCount; // @0x2c param_1[0xb] }; //########################################################################### //######################### DamageLookupTable ########################### //########################################################################### // // Top-level table streamed from the ".tbl" key for a mech. A stack of armour // LAYERS: the deeper a shot penetrates, the higher the layer index selected. // Each layer is a PieSlice wheel. (vtable @0x50bd84, ctor @0x49ea48, // dtor @0x49eadc, object size 0x30.) // class DamageLookupTable: public Plug { public: // // @0x49ea48 -- stream constructor. Reads layerCount@+0x28 and builds // that many PieSlice layers (@0x49e740). // DamageLookupTable( Mech *owner, GenericObjectStream *stream ); ~DamageLookupTable(); // @0x49eadc Logical TestInstance() const; // @0x49eb48 / @0x49e880 (returns True) // // @0x49eb54 -- resolve a hit to a damage zone. Transforms the impact // point into the mech frame (owner+0xd0), derives a penetration-depth // fraction (clamped against the armour reference at owner+0x2ec[+0xc]), // maps it to a layer index, fetches that layer's PieSlice and forwards // an incident angle (computed from the hit direction, or a random draw // when the direction is degenerate) to PieSlice::SelectSlice (@0x49e678). // void ResolveHit(const Point3D &impact) const; // // @0x49ed28 -- parse the layered table. Reads consecutive "Layer1", // "Layer2", ... pages (number suffix via FUN_004dd3ec), parsing each as // a PieSlice page (@0x49e88c). Logs " has bad format!" when no // layers are found. // static int BuildFromNotation( NotationFile *model_file, const char *model_name, NotationFile *table_file, Mech *owner, GenericObjectStream *stream ); protected: Mech *owner; // @0x0c param_1[3] ReconTable layers; // @0x10 param_1+4 int layerCount; // @0x28 param_1[10] }; //########################################################################### // Tool support -- entry comparator // // @0x49f301 -- orders table entries by their cumulative threshold (+0x14) // within an epsilon (_DAT_0049f369 ~= 1.0e-4). Used to keep the entry tables // sorted so the SelectZone / SelectSlice scans are monotone. // int CompareTableEntryThreshold(const void *a, const void *b); #endif