Experience levels WIRED: the egg field drives the simulation tier (Gitea #2)

BTPlayer's master ctor now seeds the flag block from
btMission->ExperienceLevel() + AdvancedDamageOn() (accessors from the
SURVIVED 1995 BTMSSN.HPP [T0]) instead of the NULL-stubbed scenario
role -- the egg experience knob works for the first time.  Both switch
slot errors fixed vs the binary @4c0bc8; phantom btMission duplicate
member removed (+0x1f8 IS Player::playerMission).

SEVEN gate stubs unified onto the real flags (two found empirically):
HeatModelActive, OwnerAdvancedDamage, LiveFireEnabled,
ControlsAllowLights, powersub @4b0efc, emitter's file-local FUN_004ad7d4
+ FUN_004ac9c8 stubs.  FUN_004ac9c8 family swept (incl. a live raw-offset
databinding trap in MechSubsystem::IsDamaged).  Flag block renamed to its
true semantics (simLive/heatModelOn/...), scoring-era names kept as
comments.  Includes the #5 [aud-tail] diag in emitter.cpp.

ALL 19 shipped eggs say experience=expert -> default behavior UNCHANGED
(per-tier verified: novice/standard = no heat/jams, authentic; veteran/
expert = today's exact behavior).  Real delta: egg advancedDamage=1 now
reaches player+0x264/0x268 (was hardwired 0).  Follow-up flagged: the
glass FE defaults empty experience to veteran (one tier below shipped).

Awaiting human verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-20 14:51:35 -05:00
co-authored by Claude Fable 5
parent a0cec48e3f
commit d7158f1f82
14 changed files with 294 additions and 130 deletions
+34 -15
View File
@@ -86,22 +86,41 @@ the `Tesla40_BT_manual.pdf` scan along with p3):
- **Movement heat is veteran+expert in 4.10** (the +0x260 gate), not expert-only as the 4.0 manual
states. Whether a coefficient still differentiates them is open.
## Port state (as of 2026-07-18) — wiring needed [T3]
## Port state (as of 2026-07-20) — WIRED (issue #2) [T2]
The reconstruction met this system piecemeal and stubbed the same gates inconsistently:
- `btplayer.cpp` ctor seeds `roleClassIndex` from `scenarioRole->GetReturnFromDeath()` (default 2)
— the binary seeds it from `btMission->ExperienceLevel()`. Bring-up therefore runs as VETERAN.
Two slot errors in the port's switch vs the binary: the binary's switch never touches +0x264
(that's advancedDamage, set after), and expert's `0` lands on +0x26c, not +0x270.
- `HeatModelActive()` stubbed permissive `return 1` (emitter/heat/mislanch/projweap) while
`Myomers::OwnerAdvancedDamage()` returns `False` — the SAME binary gate (+0x260) answered both
ways: heat accrues from weapons but never from movement, and no experience below veteran can
switch it off.
- `LiveFireEnabled` / `ControlsAllowLights` / powersub's short-event flag = the +0x25c not-novice
flag under three names.
- **The fix**: one accessor family on BTPlayer named members (databinding trap — never raw-read
`player+0x260`), seeded from `BTMission::ExperienceLevel()` + `AdvancedDamageOn()`, with all five
stub sites pointed at it; then an egg `experience` line actually selects the sim tier as shipped.
The wiring landed (uncommitted during the coordination pause; issue #2):
- **Seed**: the `BTPlayer` master ctor (`btplayer.cpp` ~1305) now reads
`((BTMission *)playerMission)->ExperienceLevel()` + `->AdvancedDamageOn()`. The binary's +0x1f8
mission pointer IS the inherited `Player::playerMission` (master: set by the base ctor;
replicant: back-filled from `Application::GetCurrentMission()` = app+0xc8) — the old separate
`btMission` member was a phantom duplicate, never set on the master branch; removed. The two
switch slot errors are fixed (binary rows verified against part_013.c:10843-10870). A `[exp]`
sentinel logs the seeded flags per master player.
- **Renames** (btplayer.hpp): showDamageReceived→`simLive` (0x25c), showKills→`heatModelOn`
(0x260), showDamageInflicted→`advancedDamageOn` (0x264), roleReturnDelay→`advancedDamageOn2`
(0x268, was mistyped Scalar), roleReturnDelay2→`levelFlag26c` (0x26c), showScore→`levelFlag270`
(0x270), roleClassIndex→`experienceLevel` (0x274).
- **Bridges** (btplayer.cpp, complete-type TU): `BTPlayerExperienceSimLive` (+0x25c) and
`BTPlayerExperienceHeatModelOn` (+0x260) join `BTPlayerRoleLocksAdvanced` (+0x274==0). All
consumers route mech→`GetPlayerLink()`→named member; NULL player reads permissive [T3].
- **Gates unified**: `HeatSink::HeatModelActive()` (heat.hpp; also serves mislanch/projweap/
heatfamily call sites) + `Myomers::OwnerAdvancedDamage()` (myomers.hpp) → the +0x260 bridge;
`ProjectileWeapon::LiveFireEnabled()` (projweap.cpp) + `Searchlight::ControlsAllowLights()`
(searchlight.hpp) → the +0x25c bridge; `PoweredSubsystem::HandleMessage` @4b0efc (powersub.cpp)
→ not-novice + the +0x25c bridge (the old "message-manager short-event flag" misattribution and
its dead `BT_GetMessageManager` guard are gone).
- **FUN_004ac9c8 family corrected**: `MechSubsystem::IsDamaged` (mechsub.cpp — was a live
raw-offset databinding trap `*(*(*(owner+0xd0)+0x190)+0x274)`), `BT_IsBusLive` (btstubs.cpp —
was `return True`), and powersub's `IsDamaged()` call sites @4b11bc/@4b21d0/@4b179c now all
route through `BTPlayerRoleLocksAdvanced`. `HeatSink::IsDamaged` (heat.hpp) deliberately keeps
the simulationState shadow for the gauge tint (btl4gaug.cpp:172).
- **SHIPPED EGGS ARE ALL `expert`** (every pilot page in all 19 content/*.EGG, camera pages
included) — so the default felt behavior is unchanged by the wiring (expert and the old
hardwired veteran agree on every LOCATED consumer: sim live, heat on, controls unlocked; they
differ only in the unlocated +0x26c flag). The glass front-end writes `veteran` when a pilot's
experience is empty (btl4fe.cpp:307).
- Myomers movement heat remains arithmetically inert (the Owner* motion accessors still return
neutral 0s — the gate is wired, the couplings are the reconciled follow-up).
## Corrections (swept 2026-07-18)