1516 lines
130 KiB
Markdown
1516 lines
130 KiB
Markdown
# Control-Loop Atlas
|
||
|
||
This atlas is the high-level map for RT3 1.06. It is intentionally broader than the function map:
|
||
each section explains who owns a loop, where it starts, what dispatches it, which state blocks
|
||
anchor it, and where control is handed to neighboring subsystems.
|
||
|
||
## CRT and Process Startup
|
||
|
||
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and
|
||
`app_bootstrap_main` at `0x00484440`.
|
||
- Trigger/Cadence: single process startup path before shell or engine services exist.
|
||
- Key Dispatchers: `startup_init_tls_state`, `startup_init_file_handle_table`,
|
||
`startup_run_init_callback_table`, `__setenvp`, `startup_build_argv`,
|
||
`___crtGetEnvironmentStringsA`, then `app_bootstrap_main`.
|
||
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
|
||
- Subsystem Handoffs: exits the generic CRT path at `app_bootstrap_main`, which becomes the first
|
||
RT3-owned coordinator.
|
||
- Evidence: `artifacts/exports/rt3-1.06/startup-call-chain.md`,
|
||
`artifacts/exports/rt3-1.06/function-map.csv`.
|
||
- Open Questions: exact ownership boundary between compiler CRT shims and the first game-owned
|
||
bootstrap helper; whether any nontrivial startup callbacks seed game globals before
|
||
`app_bootstrap_main`.
|
||
|
||
## Bootstrap and Shell Service Bring-Up
|
||
|
||
- Roots: `app_bootstrap_main` at `0x00484440`, `bootstrap_init_shell_window_services` at
|
||
`0x004840e0`, and `shell_install_global_controller` at `0x0051ff90`.
|
||
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
|
||
- Key Dispatchers: `app_bootstrap_main`, `bootstrap_init_shell_window_services`,
|
||
`shell_install_global_controller`, `shell_service_pump_iteration`, graphics config load or
|
||
default-init helpers, runtime capability probes, and early shell service initializers under the
|
||
`0x004610..0x0053f0..` branch.
|
||
- State Anchors: global shell controller pointer `0x006d4024`, sibling display/runtime globals under
|
||
`0x006d402c` and `0x006d4030`, and host capability flags gathered by
|
||
`bootstrap_probe_system_profile`.
|
||
- Subsystem Handoffs: after the global shell controller has been installed the bootstrap path enters
|
||
the repeating `shell_service_pump_iteration` loop, which services shell-state work and hands each
|
||
iteration down into the controller frame path; when the shell lifetime ends this same bootstrap
|
||
path tears the shell bundle down and returns to `app_bootstrap_main`.
|
||
- Evidence: `startup-call-chain.md`, function-map rows for `app_bootstrap_main`,
|
||
`bootstrap_init_shell_window_services`, `shell_install_global_controller`,
|
||
`shell_service_pump_iteration`, `shell_load_graphics_config_or_init_defaults`,
|
||
`shell_reset_display_runtime_defaults`, and related graphics setup helpers.
|
||
- Open Questions: whether gameplay or in-engine world stepping later escapes this bootstrap-owned
|
||
shell loop entirely, or whether gameplay entry still rendezvous with the same outer coordinator
|
||
before the shell bundle is destroyed.
|
||
|
||
## Shell UI Command and Deferred Work Flow
|
||
|
||
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
|
||
- Trigger/Cadence: event-driven UI command dispatch plus deferred-message queue flushing during
|
||
shell activity.
|
||
- Key Dispatchers: `shell_dispatch_ui_command`, `shell_enqueue_deferred_work_message`,
|
||
`shell_post_deferred_message_type5`, `shell_post_deferred_message_type6`,
|
||
`shell_enqueue_deferred_message_type4`, `shell_enqueue_deferred_message_type1`.
|
||
- State Anchors: shell object at `0x0062be68`, queue roots around `[this+0x11369d]`,
|
||
`[this+0x1136a1]`, and `[this+0x1136a5]`, global routing gates at `0x006d4034`, and the separate
|
||
detail-panel controller rooted at `0x006d0818`.
|
||
- Subsystem Handoffs: routes into graphics config, scenario-text export, overlay generation,
|
||
multiplayer UI, shell detail windows such as `EditorPanel.win` and `TrainDetail.win`, and
|
||
presentation-facing deferred work later drained by `shell_service_frame_cycle`.
|
||
- Evidence: function-map shell rows around `0x00464410`, `0x004d4500`, `0x004ddbd0`, and
|
||
`0x0051f1d0..0x0051f460`.
|
||
- Open Questions: whether the shell command dispatcher is also used by hotkeys or only by UI
|
||
callback tables; the current `0x006d0818` detail-panel path looks shell-only and does not yet
|
||
explain gameplay world entry.
|
||
|
||
## Presentation, Overlay, and Frame Timing
|
||
|
||
- Roots: the bootstrap-owned `shell_service_pump_iteration` at `0x00483f70`, the shell-state service
|
||
pass `shell_state_service_active_mode_frame` at `0x00482160`, the installed global shell
|
||
controller at `0x006d4024`, the pending frame-cycle owner `shell_service_frame_cycle` at
|
||
`0x00520620`, and the frame-time history path under `shell_update_frame_time_history` at
|
||
`0x0051fd70`.
|
||
- Trigger/Cadence: recurring bootstrap-owned shell service work once the active mode, controller
|
||
window, and display runtime are live; special modal or content-building paths can also force
|
||
immediate frame servicing inside that broader cadence.
|
||
- Key Dispatchers: `shell_service_pump_iteration`, `shell_state_service_active_mode_frame`,
|
||
`shell_service_frame_cycle`, `shell_refresh_presentation_frame`,
|
||
`shell_update_frame_time_history`, `shell_get_smoothed_frame_scalar`,
|
||
`shell_set_gamma_ramp_scalar`, and overlay builders such as
|
||
`shell_queue_single_world_anchor_overlay`, `shell_queue_world_anchor_overlay_list`, and
|
||
`shell_queue_indexed_world_anchor_marker`.
|
||
- State Anchors: shell state at `0x006cec74`, active mode pointer `0x006cec78`, shell frame history
|
||
ring at `0x006d403c`, display/runtime state inside the controller block near `[this+0x114282]` and
|
||
`[this+0x11428a]`, presentation service pointer at `[this+0x0c]`, and the native controller window
|
||
handle at `[this+0x00]`.
|
||
- Subsystem Handoffs: the bootstrap-owned pump runs shell-bundle polling and shell-state maintenance
|
||
before the shell-state pass synchronizes active-mode helpers and modal-status work and dispatches
|
||
the controller frame cycle, which then consumes world/object state for overlays and presentation
|
||
refresh, uses the controller window handle for one-time `ShowWindow` and related UI interaction,
|
||
and drains the deferred work queues at the end of the cycle.
|
||
- Evidence: function-map rows for `shell_service_pump_iteration`,
|
||
`shell_state_service_active_mode_frame`, `shell_service_frame_cycle`,
|
||
`shell_flush_deferred_work_queues`, frame history, gamma ramp, world-anchor overlay builders, and
|
||
graphics runtime helpers.
|
||
- Open Questions: how simulation cadence rendezvous with the shell presentation cadence once
|
||
gameplay takes over and whether gameplay stepping exits this shell-owned controller path entirely.
|
||
|
||
## Map and Scenario Content Load
|
||
|
||
- Roots: `shell_map_file_entry_coordinator` at `0x00445ac0`, the first grounded world-entry branch
|
||
`world_entry_transition_and_runtime_bringup` at `0x00443a50`,
|
||
`shell_map_file_world_bundle_coordinator` at `0x00445de0`, reference-database setup via
|
||
`map_bundle_open_reference_databases` at `0x00444dd0`, and narrower loaders such as
|
||
`map_load_geographic_label_database` and `map_load_city_database`.
|
||
- Trigger/Cadence: shell tutorial launch, editor or detail-panel file actions through `fileopt.win`,
|
||
map-scenario open paths, and scenario-text export batch commands.
|
||
- Key Dispatchers: `shell_map_file_entry_coordinator`, `world_entry_transition_and_runtime_bringup`,
|
||
`world_runtime_release_global_services`, `shell_map_file_world_bundle_coordinator`,
|
||
`map_bundle_open_reference_databases`, `map_load_geographic_label_database`,
|
||
`map_load_city_database`, `scenario_text_export_build_language_file`,
|
||
`scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
|
||
- State Anchors: shell-side file staging buffers at `0x0062bee0` and `0x0062bec4`, shell and mode
|
||
globals at `0x006cec74` and `0x006cec78`, world object root `0x0062c120`, map bundle state
|
||
allocated through `0x00530c80`, and geography tables rooted at `0x0062b2fc` and `0x0062b268`.
|
||
- Subsystem Handoffs: the shared `fileopt.win` dialog rooted at `0x004dc670` now looks like the
|
||
shell-side selector above the two broad file coordinators. Its message handler sets `0x006d07f8`
|
||
for the load or restore side or `0x006d07ec` for the save or package side before the detail-panel
|
||
transition manager routes into `shell_map_file_entry_coordinator` or
|
||
`shell_map_file_world_bundle_coordinator`. The former unresolved third flag at `0x006d07f0` is now
|
||
accounted for too: it escapes into the standalone `SettingsWindow.win` path through
|
||
`shell_open_settings_window` rather than another map or save verb. The broad coordinators now hand
|
||
their interactive work through the shared `filerqst.win` helper at `0x004dd010`, and that helper
|
||
gives the extension split a firmer shape. The paired editor-map path is now grounded as `.gmp`
|
||
through load mode `4` and save mode `3`. The remaining non-editor families are no longer anonymous
|
||
either: `.gmc` is the campaign-scenario branch, backed both by the campaign-screen resignation
|
||
prompt on `0x006cec7c+0xc5` and by the numbered `%s%02d.gmc` save helper at `0x00517c70`; `.gmx`
|
||
is the sandbox branch, backed by the shell-side `The briefing is not available in sandbox games.`
|
||
restriction on `0x006cec7c+0x82`; and the default `.gms` branch is therefore the standalone
|
||
scenario family. When a live runtime world is already active the same helper bypasses those
|
||
non-runtime extensions and forces the `.smp` runtime-state branch instead. The auxiliary save-side
|
||
mode `11` is tighter now too: it still maps to `.gmt`, but instead of looking like another
|
||
gameplay save family it conditionally diverts into the same `.gmt` preview-surface pipeline owned
|
||
by the Multiplayer preview dataset object at `0x006cd8d8`, and only falls back to the normal
|
||
reference-bundle path when that dataset object is absent.
|
||
- Evidence: function-map map/scenario rows, analysis-context exports for `0x00445ac0`, `0x00445de0`,
|
||
`0x00443a50`, `0x00434300`, and `0x00444dd0`, plus objdump string and mode-table evidence for
|
||
`.gmp`, `.gmx`, `.gmc`, `.gms`, `.gmt`, `.smp`, `Quicksave`, the `0x004dd010` mode table at
|
||
`0x005f3d58`, the auxiliary-owner presence check at `0x00434050`, and the `.gmt` handoff through
|
||
`0x00469d30`, together with localized string evidence from ids `3018` and `3898`.
|
||
- Open Questions: bit `0x1` on both broad coordinators now grounds the Quicksave name seed and the
|
||
former third `fileopt.win` flag has been ruled out as a file-flow question because it just opens
|
||
`SettingsWindow.win`. The old broad extension question is mostly resolved: `.gmp` is the
|
||
editor-map pair, `.gms` is the standalone scenario family, `.gmc` is the campaign-scenario family,
|
||
`.gmx` is the sandbox family, and `.gmt` is at least bounded as an auxiliary preview-surface
|
||
branch rather than another gameplay save family. The higher-value global question is no longer
|
||
whether `0x00443a50` is world entry. It is where the long-lived simulation cadence takes over
|
||
after this bring-up and whether that cadence still rendezvous with the shell-owned frame path or
|
||
escapes into a separate gameplay loop.
|
||
|
||
## Multiplayer Session and Transport Flow
|
||
|
||
- Roots: `multiplayer_window_init_globals` at `0x004efe80`, `multiplayer_window_service_loop` at
|
||
`0x004f03f0`, the Multiplayer.win session-event callback table built by
|
||
`multiplayer_register_session_event_callbacks` at `0x0046a900`, the active session-event transport
|
||
object at `0x006cd970`, the Multiplayer preview dataset object at `0x006cd8d8`, and the lower
|
||
pending-template and text-stream helpers around `0x00597880..0x0059caf0`.
|
||
- Trigger/Cadence: shell-owned Multiplayer.win frame service plus event-driven session callbacks and
|
||
repeated transport pump steps.
|
||
- Key Dispatchers: session-event wrappers for actions `1`, `2`, `4`, `7`, `8`;
|
||
`multiplayer_register_session_event_callbacks`; `multiplayer_dispatch_requested_action`;
|
||
`multiplayer_reset_preview_dataset_and_request_action`;
|
||
`multiplayer_preview_dataset_service_frame`; `multiplayer_load_selected_map_preview_surface`;
|
||
`multiplayer_flush_session_event_transport`; `multiplayer_transport_service_frame`;
|
||
`multiplayer_transport_service_worker_once`;
|
||
`multiplayer_transport_service_route_callback_tables`;
|
||
`multiplayer_transport_service_status_and_live_routes`;
|
||
`multiplayer_transport_text_stream_service_io`;
|
||
`multiplayer_transport_dispatch_pending_template_node`;
|
||
`multiplayer_transport_service_pending_template_dispatch_store`.
|
||
- State Anchors: live session globals at `0x006d40d0`, active session-event transport object at
|
||
`0x006cd970`, status latch at `0x006cd974`, session-event mode latch at `0x006cd978`, retry
|
||
counter at `0x006cd984`, preview dataset object at `0x006cd8d8`, preview-valid flag at
|
||
`0x006ce9bc`, staged preview strings at `0x006ce670` and `[0x006cd8d8+0x8f48]`, Multiplayer.win
|
||
backing block at `0x006d1270`, selector-view store root at `[transport+0xab4]`, selector-view
|
||
generation counters at `[transport+0xab8]..[transport+0xac0]`, selector-view backing arrays around
|
||
`[transport+0xad0]..[transport+0xae4]`, selector-view entry probe-request id at `[entry+0x50]`,
|
||
live-state gate at `[entry+0x58]`, third-slot flag word at `[entry+0x64]`, probe-schedule tick at
|
||
`[entry+0x68]`, last-success tick at `[entry+0x6c]`, pending-probe latch at `[entry+0x74]`,
|
||
success generation at `[entry+0x78]`, consecutive-failure counter at `[entry+0x7c]`, rolling
|
||
average at `[entry+0x80]`, recent sample window at `[entry+0x84..]`, bounded sample-count at
|
||
`[entry+0x94]`, total-success count at `[entry+0x98]`, pending-template list at
|
||
`[transport+0x550]`, dispatch store at `[worker+0x54c]`, and text-stream buffers rooted under
|
||
`[worker+0x1c]`.
|
||
- Subsystem Handoffs: the Multiplayer.win initializer seeds the backing block at `0x006d1270`, later
|
||
reset paths construct the separate preview dataset at `0x006cd8d8`, and the shell-owned
|
||
active-mode frame services that dataset every frame through
|
||
`multiplayer_preview_dataset_service_frame`. That preview side publishes roster and status
|
||
controls through the Multiplayer window control paths, loads `.gmt` preview surfaces through
|
||
`multiplayer_load_selected_map_preview_surface`, and is even reused by the map/save coordinator’s
|
||
mode-`11` `.gmt` path when the dataset already exists. In parallel,
|
||
`multiplayer_register_session_event_callbacks` allocates and registers the separate session-event
|
||
transport object at `0x006cd970`. The shell-side bridge into that deeper transport cadence is now
|
||
tighter: `multiplayer_window_service_loop` and neighboring reset or initializer branches call
|
||
`multiplayer_flush_session_event_transport`, which forces one status flush and then drops into
|
||
`multiplayer_transport_flush_and_maybe_shutdown`. That wrapper in turn runs
|
||
`multiplayer_transport_service_frame`, the recurring pump that services one worker step through
|
||
`multiplayer_transport_service_worker_once`, sweeps the transport-owned callback tables and field
|
||
caches through `multiplayer_transport_service_route_callback_tables`, services both the auxiliary
|
||
status route and the current live route through
|
||
`multiplayer_transport_service_status_and_live_routes`, and then descends one layer farther into
|
||
the shared GameSpy route helper `multiplayer_gamespy_route_service_frame` at `0x58d040`. The
|
||
transport also owns a separate selector-view sidecar beneath that route cadence.
|
||
`multiplayer_transport_ensure_selector_view_store` allocates the keyed selector-view store at
|
||
`[transport+0xab4]`, `multiplayer_transport_find_selector_view_entry_by_name` resolves entries
|
||
from that store, `multiplayer_transport_upsert_selector_name_entry` marks per-slot activity and
|
||
flags inside each entry, and `multiplayer_transport_mark_selector_slot_views_dirty` plus
|
||
`multiplayer_transport_reset_selector_view_entry_runtime_state` manage the dirty or refresh fields
|
||
at `+0xa0`, `+0xa4`, and `+0xa8`. That selector-view maintenance path is now split more cleanly
|
||
too. The recurring owner is `multiplayer_transport_service_selector_view_refresh_cycle`, which
|
||
first runs the fast deferred-probe lane:
|
||
`multiplayer_transport_collect_refreshable_selector_view_entries` walks the store through
|
||
`multiplayer_transport_filter_insert_refreshable_selector_view_entry`, which now shows that
|
||
`[entry+0x64]` is not a generic flag bucket but the third selector-slot flag word, parallel to
|
||
`[entry+0x5c]` and `[entry+0x60]`. In that collector, the `g` and `a` mode-letter bits produced by
|
||
`multiplayer_transport_parse_selector_mode_letters` become mask `0x0c` in the slot-local flag
|
||
words, and any third-slot entry carrying those bits at `[entry+0x64]` is excluded from the
|
||
refreshable set. Eligible entries then pass slot-aware retry timing on `[entry+0x68]`,
|
||
`[entry+0x6c]`, `[entry+0x78]`, and `[entry+0x7c]`, after which the service loop schedules refresh
|
||
probes through `multiplayer_transport_schedule_selector_view_entry_refresh_probe`. That fast lane
|
||
is narrower now too: the entry-side match key `[entry+0x50]` is no longer just an opaque request
|
||
field. The profile-key callback lanes feed
|
||
`multiplayer_transport_parse_selector_view_probe_marker`, which decodes one local `X%sX|%d` marker
|
||
into a request id plus companion value, and
|
||
`multiplayer_transport_arm_selector_view_probe_tracking` stores those into `[entry+0x50]` and
|
||
`[entry+0x54]` before arming the live probe gate at `[entry+0x58]`. The current-selector callback
|
||
root at `0x59f8b0` is now bounded as well: it resolves and upserts the active selector name,
|
||
optionally reuses a cached `username` marker to arm probe tracking immediately, then submits the
|
||
same profile-key bundle with selector context and forwards that selector through callback slot
|
||
`17`, with the status-route side able to force route-mode transitions `2 -> 3 -> 4` afterward. One
|
||
layer lower, `multiplayer_transport_handle_profile_key_query_result` at `0x596970` now bounds the
|
||
per-key result path itself. It treats `username` as the probe-marker field, `b_flags` as the
|
||
selector mode-letter field, and `(END)` as a real sentinel that publishes a zeroed slot-`22`
|
||
payload instead of a marker pair. The same helper also hashes the selector-name, key-name, and
|
||
resolved value text back into the caller table, so the profile-key bundle now looks like a real
|
||
bounded handoff rather than an anonymous callback cloud. The deferred callback shim
|
||
`multiplayer_transport_dispatch_selector_view_refresh_probe_result` then walks the keyed store
|
||
through `multiplayer_transport_finish_selector_view_refresh_probe_if_matching`, which only
|
||
completes entries whose pending latch `[entry+0x74]` is still armed and whose parsed marker
|
||
request id `[entry+0x50]` matches the finished request. A failed result `-1` clears the pending
|
||
latch and increments the consecutive-failure counter at `[entry+0x7c]`. A nonfailure result clears
|
||
the pending latch, increments the success generation at `[entry+0x78]` and total-success count
|
||
`[entry+0x98]`, clears `[entry+0x7c]`, stamps the last-success tick at `[entry+0x6c]`, appends the
|
||
returned sample into the short rolling history at `[entry+0x84..]`, grows the bounded sample-count
|
||
`[entry+0x94]` up to four, computes the current average into `[entry+0x80]`, and then publishes
|
||
that averaged sample through `multiplayer_transport_enqueue_callback_slot24_record`. So the
|
||
publication boundary is explicit and the request-id ownership is explicit, even though the exact
|
||
user-facing meaning of the sample and the companion value at `[entry+0x54]` is still open. The
|
||
same service loop also owns a slower sidecar lane keyed off `[entry+0xa4]`:
|
||
`multiplayer_transport_select_stale_selector_view_progress_entry` walks the store through
|
||
`multiplayer_transport_pick_stale_selector_view_progress_entry`, picks one stale entry whose
|
||
progress latch `[entry+0x9c]` is clear and whose last progress tick `[entry+0x70]` is old enough,
|
||
and then hands it to `multiplayer_transport_stage_selector_view_progress_snapshot`. That helper
|
||
now looks more bounded too: it rebuilds the marker text from `[entry+0x50]` through
|
||
`multiplayer_transport_format_selector_view_probe_marker`, formats one `PNG %s %d` line around
|
||
that marker and the entry-local selector-view sample at `[entry+0x80]`, appends bounded
|
||
selector-slot `PNG` fragments for live overlapping slots, marks progress-snapshot state in flight
|
||
at `[entry+0x9c]`, and stamps both `[entry+0x70]` and the transport-wide throttle tick
|
||
`[transport+0xaec]`. So the selector-view sidecar no longer looks like one undifferentiated
|
||
refresh bucket: it has a faster deferred-probe lane plus a slower progress-snapshot lane, both
|
||
still under the shell-owned multiplayer transport cadence. The two descriptor lanes installed by
|
||
`multiplayer_transport_attach_callback_table_descriptor` are now tighter too. The first lane
|
||
rooted at `0x59f5c0` can arm deferred-close state on the owner transport and then forward through
|
||
callback slot `23`. The second lane is no longer just a loose selector-view bucket:
|
||
`multiplayer_transport_callback_dispatch_selector_name_payload_lane` at `0x59f650` classifies
|
||
selector payloads through `multiplayer_transport_is_selector_control_line`, routes `@@@NFO`
|
||
control lines into `multiplayer_transport_sync_selector_view_nfo_r_flag`, and otherwise publishes
|
||
either callback slot `13` or the split token-plus-tail callback slot `14` through
|
||
`multiplayer_transport_split_selector_payload_token_and_tail`. That local `@@@NFO` helper is now
|
||
bounded more tightly too: it only accepts lines ending in the literal `X\` tail, searches for the
|
||
field marker `\$flags$\`, and then sets or clears bit `0x2` in the third selector-slot flag word
|
||
at `[entry+0x64]` depending on whether that field contains the letter `r` before the next
|
||
backslash. The sibling `multiplayer_transport_callback_dispatch_current_selector_payload_lane` at
|
||
`0x59f720` first resolves the active selector through `0x5951a0`, then handles the
|
||
current-selector variants of the same control vocabulary: `@@@NFO` continues into the same local
|
||
`r`-flag sync helper, `@@@GML` plus mode-`3/4` payloads feed the shared control-token helper
|
||
`multiplayer_transport_handle_gml_or_png_selector_control`, and the remaining non-control payloads
|
||
publish callback slot `9`. That shared helper now narrows the `GML` and `PNG` split materially:
|
||
when the token is `GML` and the tail is `Disconnected`, it requires the active selector-view entry
|
||
to pass `multiplayer_transport_selector_view_entry_has_gml_disconnect_gate`, which currently means
|
||
the entry participates in the third selector slot and has bit `0x20` set in `[entry+0x64]`, before
|
||
it forces one status-pump pass, emits callback slot `16`, and re-enters route mode `5`. Its
|
||
sibling `PNG` branch resolves a named selector-view entry from the tail text and, when that entry
|
||
overlaps the active selector-slot ownership, refreshes selector-view runtime state through
|
||
`0x5948f0` and republishes callback slot `25`. Alongside those dispatchers, one grounded branch at
|
||
`0x59f560` still updates selector-view runtime state through `0x5948f0` and forwards the same
|
||
selector-name pair through callback slot `25`, while `0x59f850` resets selector text state through
|
||
`0x5954b0`, forwards through callback slot `19`, and when selector `2` is active in a nonterminal
|
||
route mode re-enters `multiplayer_transport_set_route_mode` with mode `1`. The low-level route
|
||
helper still looks like a two-part cycle:
|
||
`multiplayer_gamespy_route_service_retry_and_keepalive_timers` handles challenge or retry pressure
|
||
and periodic outbound control traffic around the `master.gamespy.com`, `PING`, `natneg`,
|
||
`localport`, `localip%d`, and `statechanged` strings, while
|
||
`multiplayer_gamespy_route_drain_inbound_packets` drains inbound datagrams and dispatches
|
||
semicolon lines, backslash-delimited key bundles, and `0xfe 0xfd` GameSpy control packets. The
|
||
transport-owned callback story is now narrower too. The shared route constructor
|
||
`multiplayer_gamespy_route_construct_and_seed_callback_vector` seeds `[route+0x88]` through
|
||
`[route+0x9c]` from the caller-supplied transport callback table, records the owner transport at
|
||
`[route+0x104]`, and explicitly zeroes `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]` before
|
||
any later patch-up. For the transport-owned status route,
|
||
`multiplayer_transport_try_connect_status_route` then patches `[route+0xa0]` through
|
||
`multiplayer_gamespy_route_set_extended_payload_callback` to point at
|
||
`multiplayer_transport_forward_validated_extended_route_payload` `0x00597330`, which simply
|
||
forwards the validated payload wrapper into the owner callback at `[transport+0x17f4]` with
|
||
context `[transport+0x17f8]`. The grounded live-route connect path at
|
||
`multiplayer_transport_try_connect_live_route` does not currently perform any matching
|
||
post-construction patch for `[route+0xa0]`, `[route+0xa4]`, or `[route+0xd4]`, and the higher
|
||
route-mode state machine now looks consistent with that: current grounded mode transitions switch
|
||
by releasing route objects through `multiplayer_gamespy_route_release_and_free` and rebuilding
|
||
them through `multiplayer_transport_try_connect_live_route`, not by mutating callback slots in
|
||
place. The parser behavior is now tighter as well: semicolon lines only dispatch when
|
||
`[route+0xd4]` is non-null, and the subtype-`6` raw fallback only dispatches when `[route+0xa4]`
|
||
is non-null. For the currently grounded transport-owned status and live routes, those two branches
|
||
therefore remain optional and can cleanly no-op instead of implying a hidden mandatory callback
|
||
path. Inside the packet parser, subtype `4` is no longer an unknown callback hop: after cookie
|
||
validation it dispatches through `[route+0x9c]`, which the transport-owned status and live routes
|
||
seed to `multiplayer_transport_handle_validated_route_cookie_event` `0x005972c0`. That helper
|
||
either marks route progress and re-enters `multiplayer_transport_set_route_mode`, or forwards the
|
||
event id plus payload into the owner callback at `[transport+0x17f0]` with context
|
||
`[transport+0x17f8]`. Subtype `6` still validates the same cookie, dedupes one 32-bit cookie or
|
||
packet id, and then dispatches the trailing payload through the natneg-or-raw callback layer
|
||
rooted at `[route+0xa0]` and `[route+0xa4]`. This separates the shell-frame preview refresh at
|
||
`0x006cd8d8` from the actual transport cadence at `0x006cd970`, and also separates that transport
|
||
cadence from the lower GameSpy route-service and packet-parser layer beneath it.
|
||
- Evidence: `function-map.csv`, `pending-template-store-management.md`,
|
||
`pending-template-store-functions.csv`, plus objdump caller traces showing
|
||
`multiplayer_window_service_loop` reaching `multiplayer_flush_session_event_transport` and the
|
||
transport pump chain `multiplayer_flush_session_event_transport ->
|
||
multiplayer_transport_flush_and_maybe_shutdown -> multiplayer_transport_service_frame ->
|
||
multiplayer_transport_service_worker_once -> multiplayer_transport_drain_request_text_queue`.
|
||
- Open Questions: unresolved request-id semantics for `1`, `2`, `4`, and `7`; whether any
|
||
non-mode-path helper outside the currently grounded release-and-rebuild flow ever patches
|
||
`[route+0xa4]` and `[route+0xd4]` for the transport-owned status/live routes, or whether those
|
||
packet families are simply unused in this stack; and how far the Multiplayer preview-dataset
|
||
machinery is reused outside `Multiplayer.win` beyond the currently grounded `.gmt` save-mode hook.
|
||
|
||
## Input, Save/Load, and Simulation
|
||
|
||
- Roots: the shell controller window-message ingress `shell_controller_window_message_dispatch` at
|
||
`0x0054e3a0`, the shell input-state object initialized at `0x006d4018` through
|
||
`shell_input_state_init` `0x0054e710`, the saved-world restore path
|
||
`world_load_saved_runtime_state_bundle` at `0x00446d40`, the live-world save path
|
||
`world_runtime_serialize_smp_bundle` at `0x00446240`, `world_entry_transition_and_runtime_bringup`
|
||
at `0x00443a50`, the frame-owned cadence `simulation_frame_accumulate_and_step_world` at
|
||
`0x00439140`, the recurring `GameMessage.win` service branch through
|
||
`game_message_window_service_if_present` `0x004e0720`, the world-facing `GameUppermost.win`
|
||
overlay branch ensured by `shell_ensure_game_uppermost_window` `0x004e0e40` and serviced through
|
||
`game_uppermost_window_service_world_hotspot_band` `0x004e0780`, and the lower step family rooted
|
||
at `simulation_advance_to_target_calendar_point` `0x0040ab50` with periodic branches through
|
||
`simulation_service_periodic_boundary_work` `0x0040a590`.
|
||
- Trigger/Cadence: shell-side input is event-driven by controller-window `WM_*` traffic while save
|
||
or load work is triggered either directly from shell commands or through the `fileopt.win` branch
|
||
flags into the `.smp` runtime-state family; post-bring-up world service becomes recurring once a
|
||
world root exists at `0x0062c120`, but the current grounded top-level cadence still remains the
|
||
shell-owned `shell_service_pump_iteration` path, which calls
|
||
`simulation_frame_accumulate_and_step_world` directly and lets it accumulate elapsed wall-clock
|
||
time into one or more simulation-step quanta. A second ingress is now bounded too:
|
||
`simulation_run_chunked_fast_forward_burst` at `0x00437b20` repeatedly calls the same lower
|
||
stepper `simulation_advance_to_target_calendar_point`, but current grounded callers put that
|
||
helper inside the larger post-load generation pipeline `world_run_post_load_generation_pipeline`
|
||
at `0x004384d0` under the `Seeding Economy...` phase rather than under the ordinary player-facing
|
||
speed buttons. That setup pipeline is now clearer at the progress-banner level too: localized id
|
||
`318` `Computing Transportation and Pricing...` stays visible while the pipeline runs
|
||
`world_compute_transport_and_pricing_grid` `0x0044fb70`, the early collection-owned staging pass
|
||
`world_setup_building_collection_phase` `0x0041ea50`, and the conditional region pair
|
||
`world_region_collection_seed_default_regions` `0x00421b60` plus
|
||
`world_region_border_overlay_rebuild` `0x004882e0`; only then does the code post id `319` `Setting
|
||
up Players and Companies...`. That `319` lane is no longer just a shell-state placeholder: its
|
||
primary grounded work is still the chairman-profile pair
|
||
`world_seed_default_chairman_profile_slots` `0x004377a0` plus
|
||
`world_build_chairman_profile_slot_records` `0x00437220`, which seed the 16 selector bytes at
|
||
`[0x006cec7c+0x87]`, materialize the per-slot chairman records from the scenario selectors,
|
||
campaign override flag `[0x006cec7c+0xc5]`, and the static persona table at `0x005f2d28`, and then
|
||
publish the selected chairman-profile and linked company summary pair through `[state+0x25]` and
|
||
`[state+0x21]`. The local slot-record shape is tighter too because the shell editor panel around
|
||
`0x004cc2d0` now surfaces it directly: `[slot+0x00]` is the staged chairman profile id,
|
||
`[slot+0x01]` is the Optional-versus-Mandatory byte with nonzero=`Optional` and zero=`Mandatory`,
|
||
`[slot+0x02]` combines with the separate per-slot gate at `[world+0x0bc3+slot*9]` to surface
|
||
`Human`, `Computer`, and `Human or Computer`, `[slot+0x03]` is the special occupied-seat byte, and
|
||
`[slot+0x04]` is the numeric tuning field. Both the selector seeder and the record materializer
|
||
treat either `[slot+0x02]` or `[slot+0x03]` as enough to keep a slot live, but current grounded
|
||
writes only seed `[slot+0x03]` on slot zero and later move it solely by whole-record compaction.
|
||
That makes `[slot+0x03]` the strongest current anchor for the distinguished primary-human-seat
|
||
flag rather than a generic role byte. The summary fields are tighter too: current direct accessors
|
||
now show `[state+0x25]` as the selected chairman profile id in `0x006ceb9c`, while `[state+0x21]`
|
||
is the linked owning company id copied from `[profile+0x1dd]` through
|
||
`scenario_state_set_selected_chairman_profile` `0x00434890`. The editor-side scenario setup
|
||
surface beside that chairman panel is clearer now too.
|
||
`map_editor_scenario_metadata_panel_refresh_controls` `0x004ca790` republishes the scenario
|
||
description from `[0x006cec78+0x672e]`, the start-year trio `[+0x66ca]`, `[+0x66d2]`, and
|
||
`[+0x66ce]`, and the paired boolean toggles `[+0x66de]` plus inverse `[+0x66f3]` across control
|
||
band `0x5b69..0x5b74`, while `map_editor_scenario_metadata_panel_refresh_briefing_mode`
|
||
`0x004ca670` now bounds the single-player versus multiplayer briefing switch by flipping selector
|
||
`0x621f50`, publishing localized headings `1491` and `3586`, and refreshing the two briefing texts
|
||
from `[state+0x4f30]` and `[+0x5ae9]`. The companion dispatcher
|
||
`map_editor_scenario_metadata_panel_handle_message` `0x004cb4a0` makes the year semantics tighter
|
||
too: it commits the description and both briefing texts from edit-control payloads, toggles the
|
||
same two booleans, and clamps the three year fields to `1829..2100` while maintaining `minimum <=
|
||
default <= maximum`, which now lines up directly with the editor strings `Description:` `Minimum
|
||
Start Year:` `Default Start Year:` `Maximum Start Year:` `Briefing` and `Multi-Player Briefing`.
|
||
The neighboring special-conditions page is clearer as well:
|
||
`map_editor_scenario_special_conditions_panel_construct` `0x004cb2b0` now grounds the live owner
|
||
for the `Setup_Options_Buttons.imb` list rooted at `0xa7fa`, walks the 36-entry static rule table
|
||
at `0x005f3ab0`, counts enabled flags from `[0x006cec78+0x4a7f]`, and publishes the `Special
|
||
Conditions In Effect` summary from localized id `1053`. The same page now has a bounded live
|
||
dispatcher too: `map_editor_scenario_special_conditions_panel_handle_message` `0x004cb8e0` handles
|
||
both bulk selection controls and direct row-state changes, commits them back into
|
||
`[0x006cec78+0x4a7f]`, and then re-enters the panel constructor to refresh the summary. That table
|
||
is no longer just a raw id run; it now clearly includes the finance and construction restrictions
|
||
`2535..2563`, plus later editor toggles such as `Use Bio-Accelerator Cars`, `Disable Cargo
|
||
Economy`, `Disable Train Crashes`, `Disable Train Crashes AND Breakdowns`, and `AI Ignore
|
||
Territories At Startup`. The neighboring available-chairman page is tighter too.
|
||
`map_editor_available_chairman_panel_construct` `0x004ca540` now bounds the shell-side owner for
|
||
the 40-entry persona-availability surface under `0x5aa0..0x5b03`: it walks the same persona table
|
||
family at `0x005f2d28`, publishes one localized chairman-name row per profile, counts enabled
|
||
availability bytes from `[0x006cec78+0x6987..]`, and emits the summary `%1 out of %2 are
|
||
selected.` from localized id `1035`. The live state owner beside it is now grounded as well:
|
||
`map_editor_available_chairman_panel_handle_message` `0x004cb6f0` handles three bulk-selection
|
||
controls `0x5aa1..0x5aa3` by rewriting that same availability-byte array from preset bytes
|
||
embedded in the persona table, and it also commits direct per-row toggle changes from
|
||
`0x5aaa..0x5b03` back into `[state+0x6987..]` before refreshing the page. So the editor lane now
|
||
has three distinct scenario-setup slices rather than one chairman-only blob: chairman slots,
|
||
scenario metadata and briefings, plus both a rule-toggle matrix and an available-chairman pool.
|
||
There is now one adjacent company-side lane too: current neighboring setup flow conditionally
|
||
enters `world_conditionally_seed_named_starting_railroad_companies` `0x0047d440` when the
|
||
Multiplayer preview owner `0x006cd8d8` is absent and either sandbox flag `[0x006cec7c+0x82]` is
|
||
set or shell-state flag `[0x006cec74+0x14c]` is set while editor-map mode `[0x006cec74+0x68]` is
|
||
clear. That helper no longer looks like a generic company refresh. It seeds exactly three fixed
|
||
named railroad-company records from `RT3.lng` ids `575..577`: `Missouri Pacific`, `New York
|
||
Central`, and `Grand Trunk Railroad`. The first seeded company is tied back to the selected
|
||
chairman-profile summary and becomes the selected company id. The second railroad is tighter now
|
||
too: it only receives a chairman link when `profile_collection_count_active_chairman_records`
|
||
finds at least two live chairman records, and the helper then binds the zero-based second active
|
||
chairman through `profile_collection_get_nth_active_chairman_record` with ordinal `1`. The third
|
||
railroad currently gets no matching chairman-link branch in the grounded setup flow and therefore
|
||
remains an unchaired named company in the live roster. The shell UI above that setup lane is now
|
||
tighter as well: `shell_company_list_window_construct` `0x004c7200` builds a live company-list
|
||
window over the company collection at `0x0062be10`, `shell_company_list_window_refresh_rows`
|
||
`0x004c6c30` formats the active companies with localized strings `267..270`, and only then
|
||
conditionally appends one synthetic row id `0x7fff` through
|
||
`shell_company_list_format_company_or_start_row` `0x004c6b40` so `<<Start New Company>>` appears
|
||
as a separate affordance rather than as part of the seeded trio itself. Current shell paging
|
||
around that same roster is tighter too because the live company collection now has a grounded
|
||
active-only ordinal helper family: `company_collection_count_active_companies` `0x00429a50`,
|
||
`company_collection_count_active_companies_before_company_id` `0x004299f0`, and
|
||
`company_collection_get_nth_active_company_id` `0x00429990`. Those helpers now anchor the “current
|
||
company among active companies” math used by shell-side detail and picker flows rather than
|
||
leaving it as anonymous collection glue. `shell_company_list_window_handle_message` `0x004c6f30`
|
||
then routes the synthetic row into `start_new_company_dialog_open` `0x0047d080`, whose commit path
|
||
now grounds through `start_new_company_dialog_commit_create_company` `0x0047d120`. That lower
|
||
helper unlinks any current chairman-owned company, allocates a fresh company id from the live
|
||
collection at `0x0062be10`, initializes it through `0x00428420`, and only then publishes the new
|
||
selected company. The neighboring compact request helper
|
||
`start_new_company_request_create_company` `0x0047d320` does the same fresh-company path from a
|
||
request block and is already reached from the startup-company branch at `0x00470e48`. The
|
||
immediate sibling shell branch below that roster is still station-oriented: current grounded
|
||
resource names and handlers put one branch on `shell_station_detail_window_construct`
|
||
`0x005068c0`, another on `shell_station_list_window_construct` `0x005074c0`, and the subordinate
|
||
selector helper on `shell_station_pick_window_construct` `0x00507620`. But the broader
|
||
company-detail ownership question is no longer open. There is now a separately grounded
|
||
`CompanyDetail.win` family rooted at `shell_company_detail_window_construct` `0x004c5540`, with
|
||
`shell_company_detail_window_handle_message` `0x004c56a0` as its main dispatcher and
|
||
`shell_company_detail_window_refresh_controls` `0x004c2ca0` as the shared repopulation pass for
|
||
the selected-company presentation and tabbed control bands around `0x9476..0x9490`. A grounded
|
||
shell detail-manager caller reaches that constructor at `0x004dde24`, the first finance-action
|
||
layer beneath it is now bounded through `shell_company_detail_issue_bond_offer_flow` `0x004c3890`,
|
||
`shell_company_detail_issue_stock_offer_flow` `0x004c3f30`,
|
||
`shell_company_detail_buyback_stock_flow` `0x004c46d0`, and
|
||
`shell_company_detail_change_dividend_rate_flow` `0x004c5360`, and the first non-finance layer is
|
||
now bounded too through `shell_company_detail_resign_chairmanship_flow` `0x004c5a0e`,
|
||
`shell_company_detail_bankruptcy_flow` `0x004c5b99`, the territory-access family rooted at
|
||
`shell_company_detail_refresh_selected_territory_access_summary` `0x004c1b60` plus
|
||
`shell_company_detail_buy_territory_access_rights_flow` `0x004c5fc9`, backed by
|
||
`company_clear_selected_chairman_if_current_profile` `0x00428a10`,
|
||
`company_declare_bankruptcy_and_halve_bond_debt` `0x00425a90`,
|
||
`company_has_territory_access_rights` `0x00424010`, `company_set_territory_access_rights_byte`
|
||
`0x00424030`, and `company_can_purchase_territory_access_rights` `0x00426be0`, plus the two
|
||
control-transfer lanes. `shell_company_detail_attempt_chairmanship_takeover_flow` `0x0050ccc0` now
|
||
grounds the special chairman's election path: it checks the caller's current stock ownership in
|
||
the target company, rejects insufficient holdings through localized id `623`, rejects recent
|
||
failed attempts through id `624`, and then opens the confirmation under id `625` before seeding
|
||
the local takeover-election state or packaging the same request through the multiplayer shell
|
||
transport. The follow-on resolver `shell_resolve_chairmanship_takeover_vote_and_commit_outcome`
|
||
`0x0050c940` now closes the single-player election loop too: it walks the active chairman profile
|
||
collection, computes weighted votes for and against the takeover, compares the affirmative total
|
||
against half the target-company value, presents the result through
|
||
`shell_present_chairmanship_takeover_vote_outcome_dialog` `0x0050c500` in single-player or
|
||
localized id `3082` in multiplayer, and then either transfers chairmanship through `0x00428a30` or
|
||
stamps the current year into `[company+0x289]` as the grounded takeover-cooldown field.
|
||
`shell_company_detail_attempt_merger_flow` `0x004ec640` now grounds the merger side too: it
|
||
rejects empty worlds through id `727`, rejects recent failed attempts through id `728`, checks the
|
||
proposed premium against company cash through localized id `3889`, and then commits into the
|
||
resolver family. That merger resolver is now bounded too:
|
||
`shell_resolve_merger_vote_and_commit_outcome` `0x004ebd10` walks the active chairman profile
|
||
collection, computes weighted votes for and against the proposed merger, compares the affirmative
|
||
total against half the target-company value, presents the result through
|
||
`shell_present_merger_vote_outcome_dialog` `0x004eb890` in single-player or localized id `3059` in
|
||
multiplayer, and then either commits the merger through `0x00427e20` or stamps the current year
|
||
into `[company+0x15f]` as the grounded merger-cooldown field. The vote-bias side beneath that
|
||
resolver is tighter now too: `scenario_state_compute_issue_opinion_multiplier` `0x00436590` is no
|
||
longer just an abstract issue table lookup because its merger callsite uses issue id `0x3a`, which
|
||
lines up directly with localized id `726` saying merger votes depend on public attitude toward the
|
||
management of the two companies. By contrast the public-support helper
|
||
`company_compute_public_support_vote_scalar` `0x00424fd0` uses the broader issue id `0x37`, which
|
||
currently looks like a more general company-management or public-sentiment slot rather than the
|
||
merger-only attitude term. The supporting stat layer is bounded more cleanly now too: the
|
||
surrounding `0x0b` setup in the merger and takeover offer builders is formatter mode rather than a
|
||
player-facing issue number, while the company-side stat wrapper
|
||
`company_read_year_or_control_transfer_metric_value` `0x0042a5d0` now reads as a generic
|
||
stat-family accessor over year-relative series or the bounded slot family in
|
||
`company_read_control_transfer_metric_slot` `0x0042a2e0`. Its recurring family token `0x2329` is
|
||
no longer treated as an issue id here either; it is the stat-family selector paired with localized
|
||
company-stat label id `2329`. That means the paired raw and scaled helpers at `0x004241e0` and
|
||
`0x00424200` now read as narrow control-transfer offer policy totals instead of direct
|
||
issue-argument consumers, and the same broader support-and-governance metric family now also feeds
|
||
the annual shareholder-revolt and creditor-liquidation lane surfaced by localized ids `300..304`.
|
||
The editor-side help text cluster around ids `2433..2437` is no longer floating either: current
|
||
grounded map-editor code now has a live economic tuning family beside the chairman-slot panel.
|
||
`map_editor_economic_cost_slider_panel_construct` `0x004cadf0` binds six slider controls through
|
||
`map_editor_economic_cost_slider_dispatch` `0x004ca980` into the scenario-state float block
|
||
`[0x006cec78+0x0be2..0x0bf6]`, while the surrounding descriptor table at `0x00611c70..0x00612220`
|
||
pairs that wider editor lane with localized fields `Prime Rate`, `Merger Premium`, and `Build
|
||
Stations Cost` through `Steam Engine Cost` plus the comparison or help texts `2433..2437`. The
|
||
broader shell-state master flag at `[0x006cec74+0x68]` still sits above the remaining post-load
|
||
phases, plus two narrower per-phase gates: `[0x006cec74+0x174]` directly fronts id `320` `Setting
|
||
Up Buildings...` and the later region-owned structure-demand and placement pass through
|
||
`world_region_collection_run_building_population_pass` `0x00421c20` plus the deeper per-region
|
||
worker `world_region_balance_structure_demand_and_place_candidates` `0x004235c0`, while
|
||
`[0x006cec74+0x178]` directly fronts id `321` `Seeding Economy...` and the chunked burst helper
|
||
`simulation_run_chunked_fast_forward_burst`; id `322` then fronts `Calculating Heights...`. The
|
||
master `+0x68` flag is no longer just structural: the shell load/save coordinators now use the
|
||
same flag to force the editor-map `.gmp` family, so current evidence treats it as the broader
|
||
editor-map mode above those later setup branches rather than an unnamed generic toggle. That
|
||
worker is no longer one opaque building bucket: current grounded categories split into `House`, a
|
||
weighted region-profile family surfaced through the `Industry Weightings` stats path, and a third
|
||
branch whose low-level fallback token is `Commercial` but whose aligned stats label is `City
|
||
Support`. The same lower helper also reappears later on a slower simulation-side cadence with
|
||
scale `1/12`, so it no longer looks like one-time setup glue only. The actual game-speed control
|
||
family remains separate, rooted at `world_set_game_speed_mode` `0x00434680`,
|
||
`world_adjust_game_speed_mode_delta` `0x00434850`, and `world_toggle_pause_or_restore_game_speed`
|
||
`0x00437a60`.
|
||
- CompanyDetail addendum: the shared helper `shell_company_detail_resolve_selected_company` at
|
||
`0x004c16f0` now bounds the common current-company accessor beneath the whole pane, and the
|
||
read-side owner also has a separate active-company navigation family through
|
||
`shell_company_detail_step_selected_active_company_delta` at `0x004c3470` plus the next or
|
||
previous wrappers at `0x004c3540` and `0x004c3550`. The section switch in
|
||
`shell_company_detail_window_refresh_controls` is now grounded too: section index `0x006cfe60`
|
||
selects a chairman/governance slice, a debt-and-capital slice, a per-share slice, or a
|
||
territory-access slice, published through the tab band around `0x9472..0x9479`, with adjacent
|
||
section-navigation controls around `0x947b..0x947c` and `0x948f..0x9490`.
|
||
- Key Dispatchers: `shell_controller_window_message_dispatch`,
|
||
`shell_input_apply_window_key_transition`, `shell_input_snapshot_dispatch_state`,
|
||
`shell_input_cursor_inside_active_view`, `world_load_saved_runtime_state_bundle`,
|
||
`world_runtime_serialize_smp_bundle`, `simulation_frame_accumulate_and_step_world`,
|
||
`game_message_window_service_if_present`, `game_message_window_service_frame`,
|
||
`game_uppermost_window_handle_message`, `game_uppermost_window_service_world_hotspot_band`,
|
||
`game_uppermost_window_refresh_controls`,
|
||
`world_view_service_keyboard_turn_pan_and_zoom_bindings`, `world_view_step_heading_quadrant`,
|
||
`world_view_step_zoom_bucket`, `world_seed_default_chairman_profile_slots`,
|
||
`world_build_chairman_profile_slot_records`,
|
||
`world_conditionally_seed_named_starting_railroad_companies`,
|
||
`scenario_state_set_selected_chairman_profile`,
|
||
`scenario_state_get_selected_chairman_profile_record`,
|
||
`scenario_state_get_selected_chairman_company_record`, `shell_company_list_window_construct`,
|
||
`shell_company_list_window_refresh_rows`, `shell_company_list_window_handle_message`,
|
||
`start_new_company_dialog_open`, `start_new_company_dialog_commit_create_company`,
|
||
`start_new_company_request_create_company`, `shell_station_detail_window_construct`,
|
||
`shell_station_list_window_construct`, `shell_station_list_window_handle_message`,
|
||
`shell_station_pick_window_construct`, `shell_station_pick_window_populate_station_rows`,
|
||
`map_editor_chairman_slot_panel_construct`, `map_editor_chairman_slot_panel_handle_message`,
|
||
`map_editor_chairman_slot_panel_refresh_selected_slot`,
|
||
`map_editor_chairman_slot_panel_cycle_selected_slot_profile`,
|
||
`map_editor_available_chairman_panel_construct`,
|
||
`map_editor_available_chairman_panel_handle_message`,
|
||
`map_editor_scenario_metadata_panel_refresh_briefing_mode`,
|
||
`map_editor_scenario_metadata_panel_refresh_controls`,
|
||
`map_editor_scenario_metadata_panel_handle_message`,
|
||
`map_editor_scenario_special_conditions_panel_construct`,
|
||
`map_editor_scenario_special_conditions_panel_handle_message`,
|
||
`map_editor_economic_cost_slider_panel_construct`, `map_editor_economic_cost_slider_dispatch`,
|
||
`station_place_world_surface_sync_and_dispatch`, `station_place_window_handle_message`,
|
||
`track_lay_window_refresh_controls`, `track_lay_window_service_frame`,
|
||
`track_lay_window_handle_message`, `simulation_advance_to_target_calendar_point`, the smaller
|
||
single-step helper at `0x00409e80`, `0x0040a9c0`, `0x0040a910`, and
|
||
`simulation_service_periodic_boundary_work`.
|
||
- State Anchors: shell input object `0x006d4018`, per-key state table starting at `[input+0x100]`,
|
||
packed shell input flags at `[input+0xa8c]` now grounded as Right Shift `0x1`, Left Shift `0x2`,
|
||
Control `0x4`, and Alt `0x20`, nested dispatch counter `[input+0xa90]`, global shell controller
|
||
`0x006d4024`, active world root `0x0062c120`, `GameMessage.win` object `0x006d081c`,
|
||
`GameUppermost.win` overlay object `0x006d0820`, `StationPlace.win` tool object `0x006d1720`,
|
||
`TrackLay.win` tool object `0x006d1a8c`, accumulated leftover simulation time at `[this+0x4c80]`,
|
||
shell and mode globals at `0x006cec74`, `0x006cec78`, and `0x006cec7c`, world manager collections
|
||
at `0x0062be10`, `0x006ceb9c`, `0x006cfcbc`, `0x006cec20`, `0x0062bae0`, and `0x006acd34`, plus
|
||
the packed calendar-like tuple fields around `[this+0x0d]`, `[this+0x0f]`, `[this+0x11]`, and
|
||
`[this+0x14]`.
|
||
- Subsystem Handoffs: the controller window dispatcher now looks like the first grounded input
|
||
ingress. It translates keyboard and mouse `WM_*` traffic into shell controller state and the
|
||
separate input object at `0x006d4018`; read-side cursor and camera helpers later snapshot that
|
||
object through `shell_input_snapshot_dispatch_state` and gate world-relative interaction through
|
||
`shell_input_cursor_inside_active_view`. Current grounded consumers around `0x00478cb0`,
|
||
`0x004e0780`, `0x0053f450`, and `0x0053fe90` still sit on the shell controller path and consult
|
||
`0x006d4024` or the world owner at `0x0062be68`, so there is still no evidence for a distinct
|
||
gameplay-only input object after world entry. Instead, the first deeper world-mode interaction
|
||
branch now looks like a shared world-view coordinator layered on top of the same shell-fed input
|
||
state. Shell_transition_mode ensures the `GameUppermost.win` object at `0x006d0820`; its message
|
||
dispatcher `game_uppermost_window_handle_message` owns the narrow action band `0x7918` through
|
||
`0x7921`; and the recurring service helper `game_uppermost_window_service_world_hotspot_band`
|
||
rate-limits those hotspot actions, rechecks `shell_input_cursor_inside_active_view`, and then pans
|
||
the live world view through `world_view_pan_relative_offset_in_camera_plane` `0x0043d130`. The
|
||
same lower setter family is also reached from the cursor-drag path through
|
||
`world_view_apply_screen_delta_to_focus_position` `0x0043d0c0`. Above both of those sits the
|
||
larger recurring service `world_view_service_shell_input_pan_and_hover` `0x0043db00`, which now
|
||
has one grounded keyboard branch beneath it:
|
||
`world_view_service_keyboard_turn_pan_and_zoom_bindings` `0x0043d740`. That helper resolves four
|
||
binding-pair families from the shell input table via `0x0054e7d0`, and the localized labels are
|
||
now grounded from `Data/Language/RT3.lng`: `Camera Forward` and `Camera Backward` feed the first
|
||
signed pan channel, `Camera Left` and `Camera Right` feed the second signed pan channel, `Camera
|
||
Zoom In` and `Camera Zoom Out` feed the signed zoom-step channel that `0x0043db00` smooths into
|
||
`world_view_step_zoom_bucket` `0x0043cc30`, and `Camera Rotate Left` plus `Camera Rotate Right`
|
||
feed the continuous heading-turn branch through `0x0043c810`. The setup side is now better bounded
|
||
too: `world_view_seed_keyboard_binding_slot_pairs` at `0x00439e40` seeds the eight slot pairs at
|
||
`[this+0x0a6]` through `[this+0x0e2]` from the global action-binding registry through `0x0045f370`
|
||
using the distinct registry keys `0x0043d2a0` through `0x0043d310`, and the registration block at
|
||
`0x00460769` through `0x004608e7` shows those roots are defaulted to the expected Up Down Left and
|
||
Right virtual keys before runtime polling begins. Beneath that camera stack, the enclosing frame
|
||
path now has one grounded non-view sidecar: after `0x0043db00` it reads the active controller-view
|
||
object pointer at `[0x006d4024+0x18]+0x366e`, compares it against the latched target at
|
||
`[frame_owner+0x66a2]`, fires exit and enter-style vtable callbacks on pointer changes through
|
||
slots `+0x64` and `+0x60`, and only latches the new object when the object passes its own slot
|
||
`+0x1c` availability test and shell detail control id `0x07d6` on `0x006d0818` has flag bit `0x4`.
|
||
That `0x07d6` gate is now more bounded than before: the dedicated `TrackLay.win` tool family
|
||
rooted at `0x006d1a8c` special-cases the same control id in both `track_lay_window_service_frame`
|
||
and `track_lay_window_handle_message`, uses world hit tests through `0x00448ac0` to arm and
|
||
release a drag latch on that surface, and routes the resulting command work through the shared
|
||
track-lay mode state at `0x00622b0c`. The surrounding `track_lay_window_refresh_controls` pass now
|
||
shows that this is not just one isolated drag handler: the same tool family owns three mutually
|
||
exclusive primary mode buttons at `0x985e` `0x985f` and `0x9860`, and current primary evidence now
|
||
bounds those values as `Lay single track.` `0x1`, `Lay double track.` `0x4`, and `Bulldoze` `0x40`
|
||
from the localized strings 2054 2055 and 1721 plus the matching control-routing branches in
|
||
`track_lay_window_handle_message`. The same family also owns a bridge-type preference selector
|
||
rooted at `0x006cec74+0x138`, two wrapped `Never` through `Common` frequency settings at
|
||
`0x006cec74+0x140` and `0x006cec74+0x13c`, two boolean track-lay preference toggles, and the
|
||
electrify-all-track action path. Those last two toggles are now tighter than before: current
|
||
evidence strongly aligns control `0x986e` and state `0x006cec74+0x144` with `Auto-Hide Trees
|
||
During Track Lay` from strings 1838 and 1839, while control `0x986d` and state `0x006cec78+0x4c74`
|
||
align with `Auto-Show Grade During Track Lay` from strings 3904 and 3905. That mapping is still
|
||
evidence-backed rather than absolutely direct from a recovered resource table, but the state
|
||
ownership and control order now make it the strongest current fit. That makes `0x07d6` look like
|
||
the shared main-world interaction surface inside a broader TrackLay world-command subsystem, not
|
||
an unrelated detail button. The neighboring `StationPlace.win` family is now grounded on that same
|
||
surface too: the shell detail-panel constructor family allocates it through
|
||
`station_place_window_construct` at `0x00509d80`, publishes it at `0x006d1720`, services it each
|
||
frame through `station_place_window_service_frame` at `0x0050a530`, and routes player-facing
|
||
commands through `station_place_window_handle_message` at `0x005091b0`. That dispatcher
|
||
special-cases the same `0x07d6` control, and the shared helper
|
||
`station_place_world_surface_sync_and_dispatch` at `0x00508bb0` either accepts that direct surface
|
||
traffic or falls back to the same detail-panel control looked up through `0x006d0818`, rechecks
|
||
flag bit `0x4`, hit-tests the world through `0x00448ac0`, stages world coordinates into
|
||
`0x006d1738` and `0x006d173c`, refreshes the selected-site summary through
|
||
`station_place_format_selected_site_summary`, and updates the live station-placement selection
|
||
state at `0x00622af0`, `0x00622aec`, and `0x006d1740`. Together with
|
||
`station_place_select_category_and_refresh` `0x00508880`,
|
||
`station_place_refresh_category_controls` `0x00507b90`, and `station_place_format_preview_panel`
|
||
`0x00508730`, that makes StationPlace a second grounded consumer of the shared main-world
|
||
interaction surface rather than only a sibling constructor plus frame hook. The StationPlace
|
||
control semantics are now tighter too: the top category strip at `0x697c` through `0x6981` now
|
||
grounds as small station, medium station, large station, service tower, maintenance facility, and
|
||
non-station building from `RT3.lng` strings 2197 through 2202. For the three station categories
|
||
only, the secondary strip at `0x6988` and `0x6989` plus display field `0x698c` no longer looks
|
||
like another placement mode family; it is a building-style scroller. The click handlers on
|
||
`0x6988` and `0x6989` cycle the style override in `0x00622aec`, and the display path builds the
|
||
active style token from `StationSml`, `StationMed`, or `StationLrg` plus the localized
|
||
architecture styles `Victorian`, `Tudor`, `Southwest`, `Persian`, `Kyoto`, and `Clapboard` from
|
||
`RT3.lng` ids 2672 through 2667. One layer lower, the remaining opaque controls are now much
|
||
tighter: `0x6985` and `0x6986` are no longer a generic assist toggle but the two station-rotation
|
||
policy choices from strings 2207 and 2206, switching between auto-orienting the building to track
|
||
or obstacles and strictly obeying the rotation specified by the circle above. The dedicated
|
||
control at `0x6987` is the station-rotation circle itself, wired through callback `0x00507a90` and
|
||
aligned with `Click to rotate the building. You can also use bracket keys [ and ] to rotate
|
||
buildings.` string 2208. That matches the lower behavior too: when the strict-rotation choice is
|
||
off, both preview and commit paths route staged coordinates through `0x00508040`, which performs
|
||
the extra orientation search before validation; when strict rotation is on, that pass is skipped
|
||
and the current angle in `0x006d172c` is used directly. The direct shell UI also exposes the same
|
||
discrete view-step family through `world_view_step_heading_quadrant` `0x0043cb00` and
|
||
`world_view_step_zoom_bucket` `0x0043cc30`. The neighboring gating predicates
|
||
`world_view_should_drive_primary_pan_channel` and `world_view_should_drive_secondary_pan_channel`
|
||
test packed shell input bits `0x3`, and `shell_input_apply_window_key_transition` now grounds
|
||
those bits as the left and right Shift modifiers from scan codes `0x2a` and `0x36`. That means
|
||
cursor drag, overlay hotspots, held Shift state, direct keyboard turn/pan/zoom bindings, the
|
||
TrackLay and StationPlace world-command surfaces, and at least one frame-owned hover or
|
||
focus-target branch all converge under the same shell-fed world-mode input path rather than a
|
||
separate gameplay-input stack.
|
||
- Evidence: function-map rows for `shell_controller_window_message_dispatch`,
|
||
`shell_drain_pending_window_messages`, `shell_input_state_init`,
|
||
`shell_input_apply_window_key_transition`, `shell_input_snapshot_dispatch_state`,
|
||
`shell_input_cursor_inside_active_view`, `world_load_saved_runtime_state_bundle`,
|
||
`world_runtime_serialize_smp_bundle`, `world_entry_transition_and_runtime_bringup`,
|
||
`simulation_frame_accumulate_and_step_world`, `game_message_window_service_if_present`,
|
||
`game_message_window_service_frame`, `shell_ensure_game_uppermost_window`,
|
||
`game_uppermost_window_construct`, `game_uppermost_window_handle_message`,
|
||
`game_uppermost_window_service_world_hotspot_band`, `world_view_set_focus_position_xyz`,
|
||
`world_view_apply_screen_delta_to_focus_position`,
|
||
`world_view_pan_relative_offset_in_camera_plane`, `world_view_seed_keyboard_binding_slot_pairs`,
|
||
`world_view_service_keyboard_turn_pan_and_zoom_bindings`, `world_view_step_heading_quadrant`,
|
||
`world_view_step_zoom_bucket`, `world_view_should_drive_primary_pan_channel`,
|
||
`world_view_should_drive_secondary_pan_channel`, `world_view_service_shell_input_pan_and_hover`,
|
||
`world_seed_default_chairman_profile_slots`, `world_build_chairman_profile_slot_records`,
|
||
`world_conditionally_seed_named_starting_railroad_companies`,
|
||
`profile_collection_count_active_chairman_records`,
|
||
`profile_collection_get_nth_active_chairman_record`,
|
||
`scenario_state_set_selected_chairman_profile`,
|
||
`scenario_state_get_selected_chairman_profile_record`,
|
||
`scenario_state_get_selected_chairman_company_record`, `start_new_company_dialog_open`,
|
||
`start_new_company_dialog_commit_create_company`, `start_new_company_request_create_company`,
|
||
`shell_company_list_format_company_or_start_row`,
|
||
`shell_company_list_activate_or_shift_center_company`, `shell_company_list_window_refresh_rows`,
|
||
`shell_company_list_window_handle_message`, `shell_company_list_window_construct`,
|
||
`shell_company_detail_window_refresh_controls`, `shell_company_detail_window_construct`,
|
||
`shell_company_detail_window_handle_message`, `shell_station_detail_window_construct`,
|
||
`shell_station_list_window_construct`, `shell_station_list_window_handle_message`,
|
||
`shell_station_pick_window_construct`, `shell_station_pick_window_populate_station_rows`,
|
||
`map_editor_chairman_slot_panel_construct`, `map_editor_chairman_slot_panel_refresh_slot_list`,
|
||
`map_editor_chairman_slot_panel_refresh_selected_slot`,
|
||
`map_editor_chairman_slot_panel_refresh_slot_counters`,
|
||
`map_editor_chairman_slot_panel_cycle_selected_slot_profile`,
|
||
`map_editor_chairman_slot_panel_handle_message`, `map_editor_available_chairman_panel_construct`,
|
||
`map_editor_available_chairman_panel_handle_message`,
|
||
`map_editor_scenario_metadata_panel_refresh_briefing_mode`,
|
||
`map_editor_scenario_metadata_panel_refresh_controls`,
|
||
`map_editor_scenario_metadata_panel_handle_message`,
|
||
`map_editor_scenario_special_conditions_panel_construct`,
|
||
`map_editor_scenario_special_conditions_panel_handle_message`,
|
||
`map_editor_economic_cost_slider_panel_construct`, `map_editor_economic_cost_slider_dispatch`,
|
||
`station_place_refresh_category_controls`, `station_place_format_selected_site_summary`,
|
||
`station_place_format_preview_panel`, `station_place_select_category_and_refresh`,
|
||
`station_place_world_surface_sync_and_dispatch`, `station_place_window_handle_message`,
|
||
`station_place_window_construct`, `station_place_window_service_frame`,
|
||
`track_lay_window_refresh_controls`, `track_lay_window_construct`,
|
||
`track_lay_window_service_frame`, `track_lay_window_handle_message`, and
|
||
`simulation_service_periodic_boundary_work`, plus the shell-input disassembly around `0x0054f290`,
|
||
the world-view setup and service branches around `0x00439e40`, `0x0043d740`, `0x0043db00`,
|
||
`0x0043cb00`, and `0x0043cc30`, the `319` setup-side branches around `0x004377a0`, `0x00437220`,
|
||
`0x00477820`, `0x00477860`, `0x0047d440`, `0x004c6c30`, `0x004c6f30`, `0x0047d080`, `0x0047d120`,
|
||
`0x0047d320`, `0x004c2ca0`, `0x004c5540`, `0x004c56a0`, `0x005068c0`, `0x005071e0`, `0x005074c0`,
|
||
and `0x005076c0`, the direct summary-field helpers around `0x00434870` `0x00434890` and
|
||
`0x004348c0`, the company-link writers at `0x00427c70` and `0x00427d74`, the company constructor
|
||
at `0x00428420`, the startup-company branch around `0x00470e48`, the shell company-list strings
|
||
`266` `<<Start New Company>>`, `267` `You are the chairman of the %1!`, `268` `The %1 has no
|
||
chairman at the moment.`, `269` `%1 is the chairman of the %2.`, `270` `Double-click for
|
||
details.`, and `2992` `Shift-Click to center on this company's primary station.`, the
|
||
`CompanyDetail.imb` and `CompanyDetail.win` resource strings in `.rdata`, the `StationDetail.imb`,
|
||
`StationDetail.win`, `StationList.win`, and `StationPick.win` resource strings in `.rdata`, the
|
||
shell editor window branches around `0x004c9da0`, `0x004ca010`, `0x004ca1c0`, `0x004ca540`,
|
||
`0x004ca670`, `0x004ca790`, `0x004ca980`, `0x004cb2b0`, `0x004cb4a0`, `0x004cb6f0`, `0x004cb8e0`,
|
||
`0x004cc250`, `0x004cc2d0`, `0x004ceb90`, and `0x004cecc0`, the localized chairman-slot strings
|
||
`2997` through `3001` `Optional` `Mandatory` `Human or Computer` `Computer` and `Human`, the
|
||
localized scenario-editor strings `1483..1492` `Description:` through `Type the briefing for this
|
||
map.`, the localized summary strings `1035` `%1 out of %2 are selected.` and `1053` `Special
|
||
Conditions In Effect`, the 40-entry persona availability page under `0x5aa0..0x5b03`, the 36-entry
|
||
special-condition table at `0x005f3ab0` covering ids `2535..2563`, `2874`, `3722`, `3835`, `3850`,
|
||
`3852`, and `3920`, the static persona table at `0x005f2d28`, the selector array at
|
||
`0x006cec7c+0x87`, the persona collection at `0x006ceb9c`, the localized persona strings in
|
||
`Data/Language/RT3.lng` including `2730` `Unassigned`, the named-chairman range `2731+`, and the
|
||
neighboring biography range `2495+`, plus the seeded railroad-name strings `575` `Missouri
|
||
Pacific`, `576` `New York Central`, and `577` `Grand Trunk Railroad`, the StationPlace.win
|
||
constructor plus category, dispatcher, rotation-circle, shared-world-surface, preview-build, and
|
||
recurring service branches around `0x00507a90`, `0x00507b90`, `0x00508550`, `0x00508730`,
|
||
`0x00508880`, `0x00508bb0`, `0x005091b0`, `0x00509d80`, and `0x0050a530`, the StationPlace string
|
||
cluster `Place a small station` 2197 `Place a medium station` 2198 `Place a large station` 2199
|
||
`Place a service tower` 2200 `Place a maintenance facility` 2201 `Place a non-station building`
|
||
2202 `Scroll through building styles.` 2203 `When placing the building, it will strictly adhere to
|
||
the rotation specified by the circle above.` 2206 `When placing the building, it will rotate
|
||
itself as needed to orient to track or avoid obstacles.` 2207 `Click to rotate the building. You
|
||
can also use bracket keys [ and ] to rotate buildings.` 2208 and the architecture-style labels
|
||
`Clapboard` 2667 `Kyoto` 2668 `Persian` 2669 `Southwest` 2670 `Tudor` 2671 and `Victorian` 2672,
|
||
the TrackLay.win constructor and dispatcher family around `0x0050d2d0`, `0x0050e400`,
|
||
`0x0050e1e0`, and `0x0050e5c0`, the localized `Never` through `Common` strings 615 through 618,
|
||
the track-lay strings `Bulldoze` 1721 `Lay single track.` 2054 `Lay double track.` 2055 and 3114,
|
||
the TrackLay preference strings `Auto-Hide Trees During Track Lay` 1838 `If 'Auto-Hide Trees
|
||
During Track Lay' is checked, trees will automatically be reduced to small stumps whenever you are
|
||
in track laying mode.` 1839 `Auto-Show Grade During Track Lay` 3904 and `If 'Auto-Show Grade
|
||
During Track Lay' is checked, you'll see the grade number over the track while laying track -
|
||
useful for trying to keep your slopes to a minimum.` 3905, the electrify-all confirmation and
|
||
failure strings 3083 3084 3837 and 3900, the binding-registry lookup path at `0x0045f370`, the
|
||
registration block at `0x00460769` through `0x004608e7`, and the localized labels in
|
||
`Data/Language/RT3.lng` ids `3466` through `3473`.
|
||
- Open Questions: no separate outer gameplay loop is grounded above
|
||
`simulation_frame_accumulate_and_step_world` yet and no deeper gameplay-only input object is
|
||
grounded either. The new setup-pipeline evidence narrows that question in one direction: the
|
||
currently grounded chunked burst path at `0x00437b20` now looks subordinate to
|
||
`world_run_post_load_generation_pipeline` rather than to the ordinary speed controls, and even
|
||
there it still reuses the same lower stepper and pumps shell work between chunks instead of
|
||
revealing a detached gameplay loop owner. The player-facing speed-control family is now cleaner
|
||
too: `world_set_game_speed_mode` owns the bounded `Paused` through `Very Fast` ladder plus the
|
||
hidden `Ultra Fast 6..9` extension, `world_toggle_pause_or_restore_game_speed` uses `[this+0x1d]`
|
||
as the resume slot, and the multiplayer host restriction now looks attached to that same setter
|
||
family rather than to the setup-side burst helper. The frame-owned shell coupling is tighter now
|
||
too: inside `simulation_frame_accumulate_and_step_world` itself, one direct branch opens or
|
||
focuses `LoadScreen.win` through `shell_open_or_focus_load_screen_page`, and the post-step
|
||
shell-window ladder also probes several sibling shell windows by the same presence-plus-dirty
|
||
pattern, including the live `LoadScreen` singleton, the shared callback-driven custom modal, the
|
||
shared file-options dialog, `SettingsWindow.win`, the now-grounded `Overview.win` and
|
||
`BuildingDetail.win` singletons, and now the shell-side `Trainbuy.win` singleton too. That
|
||
train-buy family is no longer just an unnamed probe pair either: the opener path is now grounded
|
||
under the same shell-owned cadence through `shell_can_open_trainbuy_window_or_warn` and
|
||
`shell_open_or_focus_trainbuy_window`, and its current family semantics already extend beyond a
|
||
bare locomotive picker into selected-train upgrade summary and route-edit affordances. The
|
||
selected-train side is tighter now too: we have explicit query helpers for the selected train
|
||
record, id, validity, and company ownership, plus one explicit ownership-mismatch warning modal.
|
||
That same pass also clarified one boundary we should not conflate: the neighboring shell family
|
||
rooted at `0x006d3b20` is now grounded separately as `TrainDetail.win`. It reuses the same
|
||
selected-train context and some of the same helpers, but it is not the same `Trainbuy.win`
|
||
singleton family. The `TrainDetail.win` side now has its own constructor, opener, refresh path,
|
||
and message owner above the broader train-command strip. Its inner `0xcb` strip is tighter now
|
||
too: one bounded branch is the selected-train engine-replacement or trainbuy handoff lane using
|
||
warning ids `593/594`, and another is the selected-train retirement lane using `595/596/597`
|
||
with either a local teardown path or multiplayer opcode `0x37`. The remaining train-command
|
||
family is narrower too: the shared `0x33`-stride helper trio at `0x004b2f00`, `0x004b3000`, and
|
||
`0x004b3160` now looks like a real train route-stop or waypoint list rather than a generic row
|
||
buffer, and route-entry flag byte `+0x28` now has one grounded top-level split: sign bit clear
|
||
entries are the live placed-structure-backed family, while sign bit set entries use the direct
|
||
route-node payload side. The helper `train_route_list_count_live_site_reference_entries`
|
||
`0x004b2b80` now counts the first family explicitly. One deeper lower-bit result is grounded too:
|
||
both `train_route_list_insert_staged_entry_at_index` and the auxiliary finalize helper
|
||
`train_finalize_aux_route_entry_buffer_preserving_subflags` `0x004a94b0` explicitly preserve
|
||
bits `0x40`, `0x20`, and `0x10` in the same flag byte during route-entry rewrites. Those bits are
|
||
also no longer copy-only: the neighboring shell helper
|
||
`shell_building_detail_refresh_flagged_service_capability_rows` `0x004b9a20` now consumes them to
|
||
restyle the `BuildingDetail.win` row bands `0x7d07..0x7d1c` and `0x7f58..0x801f`. The exact
|
||
player-facing labels for those rows are still open, but the subflags now have one real shell-side
|
||
consumer instead of only preservation logic. The broader `BuildingDetail.win` refresh family is
|
||
tighter too: `shell_building_detail_refresh_subject_cargo_and_service_rows` `0x004ba3d0` now
|
||
clearly owns the selected subject rows around `0x7d06`, `0x7d96..`, and `0x7d0e`, resolving
|
||
ordinary ids through the live candidate collection and the special express-side ids through the
|
||
embedded `AnyCargo.imb`, `AnyFreight.imb`, and `PassMail.imb` paths. The first fixed triplet is
|
||
now table-grounded instead of only inferred: `0x00621df8` seeds the short-label controls
|
||
`0x7dc8..0x7dca` with RT3.lng `494..496` `Any Cargo`, `Any Freight`, and `Any Express`, while
|
||
`0x00621e10` seeds the adjacent `0x7e90..0x7e92` icon-name triplet with `AnyCargo`,
|
||
`AnyFreight`, and `PassMail` before `%1.imb` formatting. RT3.lng also tightens the longer popup
|
||
side now: `0x00621e04` feeds the first clickable selector triplet `0x7f58..0x7f5a` with
|
||
`494/497/498`, so the help text there is `Any Cargo`, `Any Freight\n(Freight is everything but
|
||
Passengers, Mail, and Troops)`, and `Any Express\n(Express is Passengers, Mail, and Troops)`.
|
||
The sibling special service rows still align to `Dining Car` and `Caboose`. The extracted
|
||
`BuildingDetail.win` blob now sharpens the resource boundary too: its embedded text table is
|
||
currently sparse rather than rich, exposing the help line for `0x7d01`, repeated
|
||
`BuildingDetail.imb` asset strings, and one standalone caption entry `Cargo`. That makes
|
||
`Cargo` the strongest current resource-side anchor for the row header around `0x7d06`. The
|
||
ordinary deeper rows are tighter now too: they do not look like hidden caption-table entries,
|
||
but like live candidate-derived rows. The current path validates each ordinary id through
|
||
`indexed_collection_entry_id_is_live` `0x00517d40`, resolves the concrete candidate record
|
||
through `indexed_collection_resolve_live_entry_by_id` `0x00518140`, and then reuses candidate
|
||
field `[record+0x04]` as one shared stem for both the row asset `%s.imb` path and the
|
||
neighboring display-label lookup through
|
||
`localization_lookup_display_label_by_stem_or_fallback` `0x0051c920`. That lookup is now
|
||
bounded too: it scans the static stem table at `0x006243c8`, already grounding entries such as
|
||
`Alcohol`, `Aluminum Mill`, `Automobiles`, `Bauxite`, and `Big Boy` against RT3.lng
|
||
`3202..3220`, and only falls back to localized id `3866` when no table entry matches before the
|
||
fixed `0x384..0x386` express-side triplet takes over. One neighboring candidate-side helper is
|
||
tighter now too: `structure_candidate_query_route_style_or_local_availability_metric`
|
||
`0x0041e650` shares the same route-style byte at `[candidate+0x46]`, returning the cached local
|
||
float at `[candidate+0x5a]` for ordinary candidates but switching route-style rows to one
|
||
normalized count over the world-side route-link collection `0x006ada90` keyed by candidate class
|
||
`[candidate+0x3e]`. That collection is tighter now too: it is constructed during world bring-up
|
||
by `placed_structure_route_link_collection_construct` `0x00468110`, and current grounded
|
||
creation flow through `placed_structure_route_link_allocate_site_pair_for_candidate_class`
|
||
`0x00467f50` seeds class byte `+0x10`, a masked initial state template, and the strongest
|
||
current creation-side site-pair fields at `+0x0c` and `+0x0e` before
|
||
`placed_structure_route_link_attach_site_owner` `0x00467eb0` links the new route-link record
|
||
into the placed-structure-owned chain at `[site+0x272]`. The owner-side split is tighter now
|
||
too: route-link field `+0x08` is the separate route-node-style owner anchor used by
|
||
`placed_structure_route_link_attach_route_node_owner` `0x00467f20`, while `+0x0a/+0x0c/+0x0e`
|
||
now read as the site-reference triple matched by
|
||
`placed_structure_route_link_collection_remove_links_touching_site_id` `0x004681f0`. Creation
|
||
flow now sharpens that further: `+0x0c` is the strongest current candidate for the stable
|
||
first-site field seeded before owner attachment, `+0x0a` is the mutable owner-site anchor
|
||
installed by the attach helper, and `+0x0e` is the stable second-site field. One more split is
|
||
tighter now too: `placed_structure_route_link_recompute_endpoint_pair_state` `0x00467c30`
|
||
currently uses `+0x0a` and `+0x0e` as the active endpoint site pair while recomputing state
|
||
byte `+0x12`; `+0x0c` is still not directly read there. The family also has a clearer release
|
||
and refresh side now: `placed_structure_route_link_release_and_detach` `0x004680b0` rolls back
|
||
the class counters and then tails into `placed_structure_route_link_detach_current_owner_chain`
|
||
`0x00467df0`, while
|
||
`placed_structure_route_link_collection_recompute_all_endpoint_pair_state` `0x004682c0`
|
||
explicitly reruns the per-record endpoint-pair reconciler across the whole live route-link
|
||
collection. One layer above that,
|
||
`placed_structure_route_link_rebuild_route_style_grid_counters_and_endpoint_state`
|
||
`0x00468300` now looks like the full-family refresh owner: it clears three route-style class
|
||
lanes in the world-grid tables rooted at `[0x0062c120+0x2129]`, then clears bit `0x2` across
|
||
the live route-link records and reruns the endpoint-pair reconciler. That now lines up with the
|
||
visible shell split: non-route candidates keep the richer local metric and price lane, while
|
||
route-style candidates use one world-side route-link family instead. The emission side is
|
||
tighter now too: `placed_structure_try_emit_best_route_style_peer_link_for_candidate_class`
|
||
`0x0040fef0` scans the live placed-structure collection for one best peer site by
|
||
class-specific weight, distance window, and type gate, then only creates a new route-link
|
||
through `placed_structure_route_link_allocate_site_pair_for_candidate_class` `0x00467f50` when
|
||
`placed_structure_endpoint_pair_has_shared_route_entry_key` `0x0040fbe0` says the chosen site
|
||
pair does not already share a route-entry key. One layer above that,
|
||
`placed_structure_rebuild_route_style_candidate_scores_and_peer_links` `0x004101e0` now reads
|
||
as the broader per-site owner of this lane: it computes per-class route-style score scalars from
|
||
local site state and scenario multipliers, drives the first three route-style emission attempts,
|
||
and then continues into a larger descriptor-driven scoring phase. Inside that larger pass,
|
||
`placed_structure_accumulate_candidate_metric_or_emit_route_style_peer_link` `0x0042cab0`
|
||
now cleanly shows the split between ordinary candidates, which add directly into the local
|
||
route-style grid lane at `[site+candidate*4+0x103]`, and remapped route-style candidates, which
|
||
re-enter the peer-link emitter. One layer above that, the broader post-create or post-edit site
|
||
rebuild `placed_structure_finalize_creation_or_rebuild_local_runtime_state` `0x0040ef10`
|
||
conditionally re-enters `0x004101e0` with stack flag `1` when its local latch at `[site+0x29e]`
|
||
stays clear, so the route-style lane is no longer floating under an unnamed single-site caller.
|
||
The placement side is tighter now too: the direct constructor
|
||
`placed_structure_collection_allocate_and_construct_entry` `0x004134d0` is the shared allocator
|
||
immediately beneath the current placement-side callers before they hand the new site into
|
||
`0x0040ef10`, and the lower constructor
|
||
`placed_structure_construct_entry_from_candidate_and_world_args` `0x0040f6d0` now bounds the
|
||
actual field-seeding layer beneath that allocator. The old unresolved higher placement chooser is
|
||
bounded more cleanly now too: the `0x00403ed5` and `0x0040446b` direct-placement commits sit
|
||
inside one larger helper,
|
||
`city_connection_try_build_route_with_optional_direct_site_placement` `0x00402cb0`. That shared
|
||
heavy builder is now the common target of the compact wrapper `0x00404640`, the peer-route
|
||
candidate builder `0x004046a0`, the region-entry pair wrapper
|
||
`city_connection_try_build_route_between_region_entry_pair` `0x00404c60`, and the direct retry
|
||
paths inside `simulation_try_select_and_publish_company_start_or_city_connection_news`
|
||
`0x00404ce0`. Internally it now has three bounded lanes:
|
||
an early explicit route-store attempt through `0x004a01a0`,
|
||
a single-endpoint direct-placement lane that scans `Maintenance` and `ServiceTower` candidates
|
||
and commits through `0x00403ed5 -> 0x004134d0 -> 0x0040ef10`,
|
||
and a later paired-endpoint fallback lane that seeds two endpoint candidates from the same stem
|
||
family, walks a temporary route-entry list, and commits through
|
||
`0x0040446b -> 0x004134d0 -> 0x0040ef10`.
|
||
It can still unwind through route-state cleanup without committing new placed structures, so the
|
||
exact lower helper semantics are not fully closed, but the broader chooser is no longer
|
||
anonymous and its main policy split is now visible. The two lower helpers directly under those
|
||
commit lanes are bounded now too:
|
||
`placed_structure_project_candidate_grid_extent_offset_by_rotation` `0x00417840` is the shared
|
||
projected-footprint offset helper over candidate bytes `[candidate+0xb8]` and `[candidate+0xb9]`,
|
||
and `placed_structure_validate_projected_candidate_placement` `0x004197e0` is the shared
|
||
go-or-no-go validator that checks company, territory, world-tile, and footprint occupancy state
|
||
before either direct-placement commit is allowed to fire.
|
||
The recurring outer owner is tighter now too:
|
||
`placed_structure_collection_refresh_quarter_subset_route_style_state` `0x00413580` walks every
|
||
fourth live placed structure from a scenario-time-derived offset and re-enters the per-site
|
||
rebuild with stack flag `0`, giving the route-style lane a concrete recurring maintenance sweep
|
||
under `simulation_service_periodic_boundary_work` rather than only a floating caller note. One
|
||
neighboring helper is now bounded on the message side too:
|
||
`shell_building_detail_handle_subject_value_row_band_action` `0x004ba270` switches over the
|
||
clicked row family `0x7d07..0x7d14`, treats subject bytes `+0x21/+0x22/+0x23` as one current
|
||
selection plus one bounded low/high pair, increments the family dirty latch at `0x006cfe0c` on
|
||
change, and republishes the refreshed value through the shared shell control helper on code
|
||
`0x66`. One neighboring helper is now bounded on the side-list path too:
|
||
`shell_building_detail_propagate_selected_subject_state_into_side_list` `0x004b9ec0` walks the
|
||
sibling list owner at `0x006cfe08`, copies the active subject state into each side-list record,
|
||
mirrors the current service or capability id at `+0x24`, and preserves the `0x40/0x20/0x10`
|
||
subflags from the active subject. That means the value-row actions and later selector rows now
|
||
read as propagating one shared building-detail state across the sibling list rather than only
|
||
mutating one isolated subject record. One neighboring helper is
|
||
now bounded separately too: `shell_building_detail_refresh_subject_pair_value_rows` `0x004bad20`
|
||
owns the mutually exclusive value-row pairs `0x7d07/0x7d08`, `0x7d11/0x7d12`, and
|
||
`0x7d13/0x7d14`, choosing among them from the same selected-subject flag byte and payload fields
|
||
at `+0x22/+0x23`, while the larger cargo-or-service row owner also gates the extra visual lanes
|
||
`0x7d6a`, `0x7d6b`, and `0x7d9d` from that same subflag family. The asset-string block is tighter
|
||
too: that branch family now explicitly uses `AnyCargo.imb`, `AnyFreight.imb`, `PassMail.imb`,
|
||
`Caboose.imb`, and `Dining.imb`, with the bit-`0x20` special lane already aligned to
|
||
`Caboose.imb` and the sibling bit-`0x10` special lane now aligned to `Dining.imb`. The larger
|
||
`0x7f58..0x801f` band is no longer just a styled row family either:
|
||
`shell_building_detail_present_flagged_service_capability_popup` `0x004b9fd0` is now grounded as
|
||
its explanatory popup callback. It resolves either one fixed express-side row from `0x00621e04`
|
||
or one active candidate or service id from `[subject+0x24]`, then formats one popup through the
|
||
shell message-box path. The neighboring refresh helper `shell_building_detail_refresh_flagged_service_capability_rows`
|
||
`0x004b9a20` is tighter now too: `0x7d07..0x7d1c` is not one anonymous block, but a real mask
|
||
partition over subject bits `0x20` and `0x10`, with one zero-mask pair, one bit-`0x20`-only
|
||
pair, one exclusive-or pair, and later one-bit and two-bit indicator rows. RT3.lng now closes
|
||
the fixed popup text family too: the single-line branch uses
|
||
`3922` `%1\nLoads available at %2: %3`, while the richer ordinary-candidate branch uses
|
||
`2981` `%1\nLoads available at %2: %3 Current Price: %4` and can append
|
||
`2982` `Price at next station, %1: %2 (%3%4)`. The selector band is narrower too: current
|
||
refresh-side evidence shows `0x7f58..0x801f` as one selected-ordinal highlight family over the
|
||
three fixed express rows plus the ordinary active-candidate rows, not as a generic unstructured
|
||
list. The neighboring `0x8020..0x8051` band is tighter too: it is primarily the matching
|
||
remove-entry family for the selected subject's live id list, with two special row indices
|
||
re-routing into the `0x7d0f/0x7d10` subflag-clearing path instead of ordinary list compaction
|
||
when the current `0x20/0x10` service-bit combination demands it. That means the exact captions
|
||
are no longer broadly open across the whole selector family: the fixed express-side rows, the
|
||
mask partitions, and the add/remove structure are grounded, and the remaining caption gap is
|
||
mostly the ordinary candidate rows further down the same band. The top-level toggles are
|
||
tighter now too: the paired
|
||
`0x7d02/0x7d03` controls are the real mode switch over subject bit `0x40`, choosing between the
|
||
bounded pair-value branch and the current-selection/status branch around `0x7d0d/0x7d0e`, while
|
||
the smaller `0x7d0f/0x7d10` controls flip the same special-service subflags later rendered as
|
||
`Caboose` and `Dining Car`. One adjacent control is tighter now too: the extracted
|
||
`BuildingDetail.win` resource contains the plain-English line `Set the initial cargo amount for
|
||
'Disable Cargo Economy' scenarios.`, and the `0x7d01` plus `0x7d09/0x7d0a` message-side branch
|
||
can mirror the current subject or selection through
|
||
`shell_building_detail_submit_aux_owner_subject_sync_request` `0x004b9e10` into the auxiliary
|
||
owner queue at `[0x006cd8d8+0x8f48]`, with side-owner presence now explicitly grounded through
|
||
`shell_has_auxiliary_preview_owner` `0x00434050`. That queued request is tighter too: both
|
||
current callsites forward the same side-list mirror latch at `[0x006cfe08+0x0c]`, so the
|
||
auxiliary owner now clearly sees not just the staged subject but also whether the local
|
||
`BuildingDetail` side-list is in mirrored-subject mode. The same `0x7d01` lane also now has one
|
||
clear rejection note: localized id `3587` `This option is only available by following the
|
||
tutorial.` now sits behind the active tutorial flag at `0x006d3b4c` and the cached previous
|
||
tutorial expected-control id at `0x00622b38`, while the neighboring tutorial helper
|
||
`tutorial_advance_step_and_refresh_expected_control_ids` `0x00516be0` now grounds
|
||
`0x00622b34/0x00622b38/0x00622b3c` as the current and previous expected-control cache rather
|
||
than anonymous globals. The last field is tighter now too: `0x00622b3c` is currently best read
|
||
as the active tutorial step's alternate-accepted companion control id, because the generic shell
|
||
control path compares clicked control ids against it directly and suppresses the `3587` tutorial
|
||
rejection when they match. That does not fully close the player-facing caption bind for every
|
||
control, but it does bound the neighboring side-owner sync and tutorial-rejection lane instead
|
||
of leaving it as anonymous glue.
|
||
The extra pre-insert gate is narrower than it
|
||
first looked. It is now grounded as tutorial-only:
|
||
`tutorial_validate_train_route_station_indicator_step` `0x00516d00` checks the current tutorial
|
||
step from the shell tutorial descriptor table at `0x00622b48` before a live station-or-transit
|
||
site id can be committed into the staged route entry, and the currently accepted steps align with
|
||
localized prompts `3777` and `3778`, the two train-routing tutorial instructions that tell the
|
||
player to click the Milan and Turin station indicators. Outside that tutorial state family, the
|
||
route-entry insertion path is not gated there. The adjacent validator is tighter now too:
|
||
`train_route_list_validate_reachability_and_station_pair` `0x004b2c10` walks that same route list,
|
||
resolves placed-structure-backed entries through the live placed-structure and route-node
|
||
collections, uses the direct route-node payload branch for the remaining entry family, and fails
|
||
with `3089..3091` when the resulting route cannot be traversed or does not preserve a valid
|
||
terminal station pair. The post-validation follow-on is bounded too:
|
||
`train_current_route_context_uses_strict_reachability_mode` `0x004a9460` is now the small shared
|
||
gate above the stricter branch, keyed off the current linked route object's downstream class type,
|
||
and `train_set_route_operating_mode_and_scalar` `0x004ab980` now reads as the shared train mode
|
||
setter beneath route editing, with the local and multiplayer insertion paths choosing mode
|
||
`0x13` only when that stricter second validation succeeds and mode `0x0a` on the looser fallback
|
||
path. The first
|
||
deeper world-mode interaction branch is now better
|
||
bounded: `GameUppermost.win` hotspots, cursor drag, held Shift state, discrete shell view-step
|
||
commands, direct keyboard turn/pan/zoom bindings, the `TrackLay.win` and `StationPlace.win`
|
||
world-command surfaces, and a frame-owned hover or focus-target transition branch all feed the
|
||
same shell-controller-backed path. The remaining uncertainty has moved farther from basic
|
||
ownership: the hover-target branch clearly exists, and `0x07d6` now looks like the shared
|
||
main-world interaction surface rather than a generic detail button for one tool family only. The
|
||
next unresolved layer is narrower and more semantic: the setup side now has one grounded
|
||
owner, `world_run_post_load_generation_pipeline`, and its building-side branch is no longer just
|
||
one opaque block. We now have a region family, a region-border overlay rebuild, a region-owned
|
||
structure-demand and placement dispatcher, and a deeper per-region worker that computes category
|
||
demand, subtracts existing coverage, and tries candidate placements. The category map is tighter
|
||
too: category `0` falls back to `House`, category `2` is the year-gated weighted region-profile
|
||
family that also feeds the localized `Industry Weightings` stats panel, and category `3` now
|
||
reaches a separate pool-driven picker whose fallback label is `Commercial` but whose aligned
|
||
player-facing stats bucket is `City Support`. The remaining setup-side uncertainty has therefore
|
||
narrowed again: the region seed and border-overlay pair clearly complete before the `Setting up
|
||
Players and Companies...` banner is posted; `[0x006cec74+0x174]` now looks like the direct
|
||
building-population gate; `[0x006cec74+0x178]` now looks like the direct seeding-burst gate; and
|
||
`[0x006cec74+0x68]` now aligns with editor-map mode because the same flag forces the `.gmp` family
|
||
in the shell file coordinators while suppressing the later building and seeding branches and
|
||
diverting the deeper region worker into alternate logic. The `319` lane itself is no longer the
|
||
open structural gap; it now clearly owns chairman-profile slot seeding, profile-record
|
||
materialization, a shell editor surface over the same local record family, and a separate
|
||
live-company presentation path through the company-list window. The remaining gaps on this lane
|
||
are therefore narrower than before: the local slot records are rooted at `[world+0x69d8]`,
|
||
`[slot+0x01]` polarity and the external role gate at `[world+0x0bc3+slot*9]` are now grounded, and
|
||
`[slot+0x03]` now looks like the distinguished primary-human-seat marker because current grounded
|
||
writes seed it only on slot zero and later logic moves it solely by whole-record compaction. The
|
||
open question is no longer whether the seeded trio lands in the visible shell company roster;
|
||
current evidence says it does, and ordinary `Start New Company` now looks like a fresh-company
|
||
allocator through `start_new_company_dialog_commit_create_company` and
|
||
`start_new_company_request_create_company`, not like the path that claims one of the seeded named
|
||
railroads. The immediate post-roster station branch is now clearly separate: current grounded
|
||
resource names and handlers put mode `8` on `StationDetail.win`, mode `5` on `StationList.win`,
|
||
and the subordinate selector helper on `StationPick.win`. The company-side ownership question has
|
||
therefore moved down a layer rather than staying open. We now have a recovered `CompanyDetail.win`
|
||
owner family through `shell_company_detail_window_refresh_controls`,
|
||
`shell_company_detail_window_construct`, and `shell_company_detail_window_handle_message`; the
|
||
same owner now has one broader bounded read-side lane too, because control `0x9470` uses
|
||
`shell_company_detail_render_financial_history_panel` to draw the five-step Revenue or Expenses or
|
||
Interest or Profit or Lifetime strip, sibling control `0x9471` reuses
|
||
`shell_format_company_financial_summary_card` through
|
||
`shell_company_detail_render_company_summary_card`, controls `0x947d` and `0x947e` now ground a
|
||
bond maturity and repay panel through `shell_company_detail_render_bond_maturity_and_repay_panel`,
|
||
control `0x9488` now grounds the debt or capital or dividend summary block through
|
||
`shell_company_detail_render_capital_and_dividend_summary_panel`, control `0x948a` now grounds the
|
||
per-share metrics block through `shell_company_detail_render_per_share_metrics_panel`, and the
|
||
adjacent territory selector lane is bounded through
|
||
`shell_company_detail_select_territory_access_row`,
|
||
`shell_company_detail_render_territory_access_row`,
|
||
`shell_company_detail_sync_selected_territory_from_picker`, and
|
||
`shell_company_detail_refresh_selected_territory_access_summary`; the first finance-action layer
|
||
beneath it is bounded through the bond, stock-issue, stock-buyback, and dividend-rate helpers; the
|
||
territory-access side is bounded too through
|
||
`shell_company_detail_refresh_selected_territory_access_summary`,
|
||
`shell_company_detail_buy_territory_access_rights_flow`, and the underlying company access-rights
|
||
helpers; and the full takeover and merger vote-result lane is now grounded through
|
||
`shell_resolve_chairmanship_takeover_vote_and_commit_outcome`,
|
||
`shell_present_chairmanship_takeover_vote_outcome_dialog`,
|
||
`shell_resolve_merger_vote_and_commit_outcome`, and `shell_present_merger_vote_outcome_dialog`.
|
||
The remaining company-side uncertainty is therefore narrower than before: the public-support side
|
||
is now tighter too because `company_compute_cached_recent_performance_support_score` and
|
||
`company_compute_public_support_vote_scalar` bound the recent-performance and public-support blend
|
||
beneath those vote resolvers, `scenario_state_compute_issue_opinion_multiplier` now bounds the
|
||
next layer of optional company, chairman, and territory-specific opinion overrides on the active
|
||
scenario state, and the broader stat-reader family around
|
||
`company_read_control_transfer_metric_slot` and
|
||
`company_read_year_or_control_transfer_metric_value` is no longer just a merger-premium helper.
|
||
Current grounded callers show the same metric family feeding the annual shareholder-revolt and
|
||
creditor-liquidation lane surfaced by localized ids `300..304`, so the remaining gap is now mostly
|
||
semantic: the exact player-facing names of the support-and-governance metric slots behind issue
|
||
ids `0x37` and `0x3a`, plus any later chairmanship-control side effects beyond the already
|
||
grounded success or failure commit points. The packed simulation calendar tuple semantics also
|
||
remain open. The `TrackLay.win` family now clearly owns `Lay single track.` `Lay double track.`
|
||
and `Bulldoze` as its three primary modes, its bridge selector, its wrapped frequency preferences,
|
||
and a strongly aligned pair of `Auto-Hide Trees During Track Lay` and `Auto-Show Grade During
|
||
Track Lay` toggles; the `StationPlace.win` family now clearly owns its six top-level category
|
||
buttons, the station-style scroller, and the station-rotation controls. The older `Building
|
||
placement center` string 671 no longer looks like a live StationPlace control label in the current
|
||
recovered flow, because the active constructor, preview, refresh, and dispatcher paths all use
|
||
neighboring ids such as 669 and 2208 without a direct recovered lookup of 671. On save or load the
|
||
broad serialize-versus-restore split is now grounded, the non-Quicksave `.gmp/.gmx/.gmc/.gms`
|
||
families are separated, and the auxiliary `.gmt` path is at least bounded to the preview-surface
|
||
side owner. The higher-value shell-facing gap has therefore shifted upward to the remaining
|
||
semantics of the post-load generation phases, the later recurring structure-population cadence,
|
||
the deeper vote-weight formulas inside takeover and merger resolution, and the still-open meaning
|
||
of the packed simulation calendar tuple.
|
||
|
||
- CompanyDetail, tighter section ownership: the shell detail family now has an explicit
|
||
section-selector lane in addition to the read-side panels already mapped. Controls
|
||
`0x9472..0x9475` directly select the four visible CompanyDetail sections through `0x006cfe60`,
|
||
`0x9476..0x9479` are the companion visual controls for that same tab strip, and section `0` is now
|
||
bounded more tightly as the chairman or governance slice around the portrait-backed chairman band
|
||
on `0x9480` plus the dynamic overview widget `0x947f`. That widget is no longer just a vague
|
||
status line: the section-0 refresh binds it through a dedicated stack-built dynamic text path,
|
||
and the strongest current shared formatter candidate is
|
||
`shell_format_company_governance_and_economy_status_panel` at `0x004e5cf0`, which first renders a
|
||
five-line company-metric preamble through localized ids `1211..1215`: `Revenues` from slot
|
||
`0x2c`, `Profits` from slot `0x2b`, `Load miles hauled` from slot `0x17`, `Revenue per load
|
||
mile` from the derived `slot 0x2c / slot 0x17` branch, and `Average speed` from slot `0x26`
|
||
rendered through `1216` `%1 m.p.h.`. It then splits the governance summary more concretely: no
|
||
linked chairman emits `3045`, wholly owned companies emit `3046` for the scenario-selected
|
||
chairman or `3047` for another linked chairman, and investor-owned companies emit the
|
||
investor-attitude lines `3048/3049` with one adjective from the table at `0x00622170`. That
|
||
branch is no longer using unnamed helpers either: `company_get_linked_chairman_profile_record`
|
||
`0x00426ef0` resolves the linked chairman profile and `chairman_profile_owns_all_company_shares`
|
||
`0x004768c0` is the full-ownership test behind the `3046/3047` split. The salary side is tighter
|
||
too: the formatter computes one signed delta from `[company+0x14f]` and `[company+0x0d59]`, then
|
||
chooses `3050..3052` for the scenario-selected chairman or `3053..3055` for another linked
|
||
chairman depending on whether that delta is negative, positive, or zero. The bonus line is
|
||
narrower still: it only appears when the display year matches `[company+0x34f]`, using amount
|
||
`[company+0x353]` with `3056` or `3057`. It then appends the
|
||
`1218` `Economy status - %1.` tail caption and stages the adjacent selected-company report or
|
||
list help-title pairs `1219/1220` `Income Statement`, `1221/1222` `Balance Sheet`,
|
||
`1223/1224` `Haulage Report`, `1225/1226` `Stock Report`, `1227/1228` `Train List`,
|
||
`1229/1230` `Station List`, `1231/1232` `Industry List`, and `1233/1234` `Cargo List`.
|
||
Current evidence still does not recover separate `CompanyDetail.win` action controls for that
|
||
strip under `shell_company_detail_window_handle_message`, so it currently reads as staged
|
||
overview text or help content rather than as a closed launcher family. The direct `0x947f`
|
||
formatter call is still indirect, but the widget boundary is tighter too: the generic shell
|
||
helpers `shell_control_refresh_matching_dynamic_text_payload` `0x00540a47` and
|
||
`shell_control_release_dynamic_text_payload` `0x005639d2` now show that type `0x6f` controls
|
||
free or swap one heap-backed text payload and then short-circuit as a special dynamic-text case,
|
||
which strengthens the reading of `0x947f` as a display-only overview widget rather than a normal
|
||
callback control. One adjacent boundary is tighter now too: the broader overview wrapper at
|
||
`shell_render_company_overview_panel_header_and_optional_change_affordance` `0x004e5a80` owns
|
||
the fallback no-company texts `1210`, `3043`, and `3888`, styles controls `0x3f06` and `0x3f07`,
|
||
and on the narrower selected-company branch appends `3044` `Click to change company name and
|
||
logo.` plus the neighboring `1941` `Change` affordance before falling through into the shared
|
||
`0x004e5cf0` text body. That keeps the name/logo affordance outside the ordinary
|
||
`CompanyDetail.win` action dispatcher and makes the `0x947f` alignment cleaner. The message-side
|
||
action band is tighter too: `0x94b5` grounds
|
||
territory-access purchase, `0x94b6` grounds bankruptcy, `0x94cf..0x94d2` ground bond issue, stock
|
||
issue, stock buyback, and dividend-rate changes, `0x9493` routes into the destructive
|
||
company-clear helper that deactivates the selected company and clears chairman/share links,
|
||
`0x94d6` grounds bankruptcy, `0x94d7..0x94da` ground bond issue, stock issue, stock buyback, and
|
||
dividend-rate changes, `0x94db` grounds merger, `0x94dc` grounds resignation, and `0x9538` grounds
|
||
chairmanship takeover. The finance-side dialog family is tighter too: the bond-issue lane now has
|
||
the dedicated modal renderer `shell_company_detail_render_issue_bond_offer_dialog` `0x004c3560`
|
||
for underwriter terms `968..972`, the stock-issue lane has
|
||
`shell_company_detail_render_issue_stock_offer_dialog` `0x004c3b50` for the staged offer lines
|
||
`975..978`, and the buyback lane has
|
||
`shell_company_detail_render_stock_buyback_offer_dialog` `0x004c4300` for broker lines
|
||
`981..984`. The compact summary card on sibling control `0x9471` is tighter too:
|
||
`shell_format_company_financial_summary_card` at `0x004bfb30` now clearly renders `Cash:`,
|
||
`Revenue:`, and `Profits:` from company slots `0x0d`, `0x2c`, and `0x2b`, rather than one looser
|
||
unnamed finance block. The dividend lane is now split the same way:
|
||
`shell_company_detail_setup_dividend_rate_adjust_controls` `0x004c4c70` binds the paired adjust
|
||
controls `0x99e8` and `0xc0f9`,
|
||
`shell_company_detail_render_change_dividend_rate_dialog` `0x004c4e30` renders localized ids
|
||
`988..990`, and
|
||
`shell_company_detail_handle_change_dividend_rate_dialog_message` `0x004c5140` clamps the staged
|
||
dividend rate against `company_compute_board_approved_dividend_rate_ceiling` `0x00426260` and
|
||
raises localized id `991` when the board refuses a higher dividend, all before the existing
|
||
company commit path. All four finance verbs now converge through the shared
|
||
callback-driven modal opener `shell_open_custom_modal_dialog_with_callbacks` `0x004c98a0`, which
|
||
is also reused by the multiplayer staged text-entry lane. The front controls in section `0` are
|
||
tighter too: `0x948b` is a
|
||
tutorial-guarded escape or back control that shows localized id `3724` `This option is disabled in
|
||
the tutorial.` before falling back to the shell detail-manager escape path, `0x9491` and `0x9492`
|
||
only restyle the paired visuals `0x948f` and `0x9490`, and `0x9494` opens localized id `3635`
|
||
`Enter the amount that your company's cash should be` and writes the accepted value directly into
|
||
the selected company cash pair. The neighboring debt section is tighter now too: controls
|
||
`0x947d` and `0x947e` no longer read as one-off bond widgets, but as the owners of two repayable
|
||
bond-slot row bands, `0x94e8..0x950f` and `0x9510..0x9537`. Those row controls now clearly
|
||
converge on the same repayment path in the message dispatcher: they reject unaffordable repayment
|
||
through localized id `2990`, open the early-repayment confirmation rooted at `2991`, and then
|
||
either commit through `company_repay_bond_slot_and_compact_debt_table` `0x00423d70` or package
|
||
the request through the multiplayer shell transport. The render-side owner is tighter too:
|
||
`shell_company_detail_render_bond_maturity_and_repay_panel` formats `Due %1` and `Repay this
|
||
bond.` for the selected debt slot while the tiny binder
|
||
`shell_company_detail_bind_bond_row_band_for_active_panel` switches between the two row bands.
|
||
Current `0xcb` dispatch does not treat `0x948f`, `0x9490`, `0x94d4`, or `0x94d5` as standalone
|
||
action cases. The message-side jump table now makes that passive/action split explicit too: in the
|
||
dispatch byte map rooted at `0x004c6640`, controls `0x94d6..0x94dc` map to cases `0x06..0x0c`,
|
||
control `0x9538` maps to case `0x0d`, and the neighboring companion rows `0x94d4` and `0x94d5`
|
||
stay on the default path. The refresh-side ownership is tighter too: the helper now explicitly
|
||
loops over `0x94d4..0x9537` as the selected-company-owned band and over `0x9538..0x959b` as the
|
||
linked-chairman-owned band, and those two loops do not use the same style polarity.
|
||
`0x94d4..0x9537` take style `0x65` when the selected company matches the scenario-selected
|
||
company and `0x87` when it differs, while `0x9538..0x959b` take style `0x87` when the selected
|
||
company's linked chairman matches the scenario-selected chairman and `0x65` otherwise. The
|
||
selected-company action side
|
||
is tighter now too: scenario toggle `0x006cec78+0x4a8f` re-enables bankruptcy `0x94d6` together
|
||
with passive companion row `0x94d4`, `+0x4a8b` re-enables issue bonds `0x94d7` together with
|
||
passive companion row `0x94d5`, `+0x4a87` re-enables stock issue and stock buyback
|
||
`0x94d8..0x94d9`, `+0x4a93` re-enables dividend `0x94da` together with the same passive
|
||
companion row `0x94d5`, `+0x4adb` re-enables merger `0x94db`, `+0x4acb` re-enables resignation
|
||
`0x94dc`, and `+0x4acf` re-enables chairmanship takeover `0x9538`. That makes `0x94d4/0x94d5`
|
||
read more like passive companion or heading widgets than hidden verbs. The constructor boundary
|
||
is tighter too: current `CompanyDetail.win` setup still only binds explicit callbacks for
|
||
`0x9470`, `0x9471`, `0x947d`, `0x947e`, and `0x948c..0x948e`, not for the wider section-0 row
|
||
bands. That keeps the remaining `0x94d4..0x959b` content looking more like resource-defined
|
||
display rows that are gated and restyled by refresh than like individually code-rendered widgets.
|
||
That leaves the main remaining CompanyDetail-specific shell edge at the exact `0x947f` formatter
|
||
binding plus the still-unsplit render-side governance rows inside `0x94d4..0x959b`.
|
||
- Adjacent `LoadScreen.win` report family: the neighboring shell lane around controls
|
||
`0x3ef6..0x4073` is now separated from `CompanyDetail` instead of being treated as one more
|
||
extension of the `0x947f` overview path. The real outer owner is
|
||
`shell_load_screen_window_construct` `0x004ea620`, which binds `LoadScreen.win`, randomizes the
|
||
`LoadScreen%d.imb` background family, stores the singleton at `0x006d10b0`, and seeds the first
|
||
visible page-strip controls. Above the older page-specific work, the real message owner is now
|
||
`shell_load_screen_window_handle_message` `0x004e3a80`: it owns page id `[this+0x78]`,
|
||
page-local substate `[this+0x7c]`, page-kind `[this+0x80]`, current company `[this+0x88]`,
|
||
current chairman profile `[this+0x8c]`, display year `[this+0x9c]`, and the page-local report
|
||
row latch `[this+0x118]`, then fans back into the shared selector
|
||
`shell_load_screen_select_page_subject_and_refresh` `0x004e2c10`, the company-step helper
|
||
`0x004e3a00`, and narrower page branches such as `0x004e45d0`. The matching render-side owner is
|
||
now bounded too: `shell_load_screen_render_active_page_panel` at `0x004ea060` formats the common
|
||
heading and panel frame, then switches on page id `[this+0x78]` and hands control down into the
|
||
active page body. That older branch is now demoted to what it actually is:
|
||
`shell_load_screen_profile_stock_holdings_page_handle_message`, the page-specific handler beneath
|
||
the stock-holdings slice. Inside that same family,
|
||
`shell_load_screen_render_profile_stock_holdings_summary_panel` at `0x004e5300` grounds the
|
||
selected-profile holdings page: it resolves the current chairman profile from `[this+0x8c]`,
|
||
renders the top summary rows `1204` `Stock Value:`, `1205` `Total Assets:`, and
|
||
`1206` `Stock Holdings:`, then walks the active company roster and formats one row per positive
|
||
holding through `1201` `Click to view details on %1.`, `1207` `%1 Shares`, and `1208` `%1 Value`,
|
||
falling back to `1209` `None` when no positive holdings survive. It also appends `3029`
|
||
`Click to change player name and portrait.` plus the adjacent `1941` `Change` affordance only
|
||
when the rendered profile matches the scenario-selected chairman. The earlier pages are tighter
|
||
now too: `0x004e68e0` is the selected-company financial ranking page using the active company
|
||
roster plus `1235..1245` for revenue, profit, cash, track mileage, and report affordances; and
|
||
`0x004e6ef0` is the active-chairman wealth ranking page using the chairman profile collection
|
||
plus `1237`, `1241`, `1246..1250` for cash, stock, total, and purchasing-power style comparisons.
|
||
The later sibling renderers are broader than that one holdings page now too: `0x004e7670` is the
|
||
selected-company train list page using `1235..1267`, `0x004e8270` is the selected-company
|
||
building list page using `1268..1278`, `0x004e8bb0` is the selected-company station list page
|
||
using `1279..1288`, and `0x004e9460` is the map-wide cargo list page using `1289..1298` over the
|
||
live candidate collection rather than one company roster. The last broad early-page owner is
|
||
tighter now too: `0x004e9b20` is the shared multi-year company report-table renderer for page
|
||
`4` `Income Statement`, page `5` `Balance Sheet`, and page `6` `Haulage Report`, all driven from
|
||
`0x004ea060` with one caller-supplied mode byte and yearly company rows built through
|
||
`company_read_year_or_control_transfer_metric_value`. The row families are bounded too:
|
||
income-statement rows `1301..1315`, balance-sheet rows `2816` and `1317..1322`, and
|
||
haulage-report rows `1323..1335`. The only special rows inside that family are now tighter too:
|
||
`0x00425880` and `0x004258c0` provide the negative-cash and positive-cash interest-rate inserts
|
||
for the `%1/%2` placeholders in strings `2815` and `2816`, so they are no longer anonymous
|
||
private math blobs. The adjacent early siblings are tighter now too: `0x004e5130` is the
|
||
selected-company `Stock Data` page wrapper that falls back through `1299` and otherwise reuses
|
||
`0x004c0160` to render the `Largest Shareholders`, `Shares`, and `Per Share Data` family;
|
||
`0x004e6ef0` is the `Player List` page; `0x004e5300` is the `Player Detail` holdings page; and
|
||
`0x004e51ea` is the `Game Status` briefing panel that pulls current scenario briefing text from
|
||
the live scenario text store and appends the `1772/1773` `Briefing` affordance. The active-page
|
||
renderer at `0x004ea060` is now tighter too because its 13-byte page descriptor table at
|
||
`0x006220a0` has been decoded directly as `{ page kind, title string id, `0x3ef8` backlink
|
||
page, selected-company-header flag }`. The currently grounded rendered title order is:
|
||
`XXX`, `COMPANY OVERVIEW`, `COMPANY LIST`, `INCOME STATEMENT`, `BALANCE SHEET`,
|
||
`HAULAGE REPORT`, `STOCK DATA`, `PLAYER LIST`, `PLAYER DETAIL`, `GAME STATUS`,
|
||
`TRAIN LIST`, `TRAIN DETAIL`, `STATION LIST`, `STATION DETAIL`, `CARGO LIST`,
|
||
and `INDUSTRY LIST`. Its live body bindings are now bounded too: page `0` falls back to
|
||
`1203` `Unable to display page`, page `1` is the company overview wrapper, page `2` is the
|
||
company list page, pages `3..5` are income statement, balance sheet, and haulage report, page
|
||
`6` is stock data, page `7` is player list, page `8` is player detail, page `9` is game status,
|
||
page `0x0a` is train list, page `0x0b` currently falls back to `1203`, page `0x0c` is station
|
||
list, page `0x0d` currently falls back to `1203`, page `0x0e` is cargo list, and page `0x0f`
|
||
is industry list. The row-click path is tighter now too: player-list rows re-enter page `8`
|
||
directly, but train, station, and industry rows leave `LoadScreen.win` through the shell
|
||
detail-panel manager at `0x004ddbd0` instead of switching to title-table pages `0x0b` or
|
||
`0x0d`. Page `0` is tighter now too: its descriptor is kind `0`, title `1200` `XXX`, backlink
|
||
`0`, and header flag `0`, and no current post-constructor selector path has been recovered for
|
||
it. The descriptor side now also bounds the only known reverse route for the dormant detail
|
||
titles: `0x3ef8` is the table-driven backlink affordance, so if page `0x0b` or `0x0d` were ever
|
||
selected, the known reverse path would return to train-list page `0x0a` or station-list page
|
||
`0x0c` respectively rather than through a separate detail-only owner.
|
||
The launcher side is tighter too: current grounded `shell_open_or_focus_load_screen_page`
|
||
callers cover pages `1`, `2`, `3`, `4`, `5`, `6`, `7`, `9`, `0x0a`, `0x0c`, `0x0e`, and `0x0f`,
|
||
and no current recovered opener or row-click route selects `0x0b` or `0x0d`. So the
|
||
`LoadScreen.win` family now has a much cleaner shape: one outer message owner
|
||
`0x004e3a80`, one active-page render owner `0x004ea060`, and then the narrower page-specific
|
||
handlers and renderers beneath them. The launcher side is tighter now too: `0x004e4ee0` is the
|
||
shared open-or-focus ledger-page owner above this family. Outside sandbox it either re-enters
|
||
`shell_load_screen_select_page_subject_and_refresh` on the live runtime at `0x006d10a8` or
|
||
allocates that transient runtime, seeds it through `0x004e4b10`, and enters the visible modal
|
||
loop; inside sandbox it raises localized id `3899` `The ledger is not available in sandbox mode.`
|
||
instead. That narrows the remaining `LoadScreen.win` gap again: `TRAIN DETAIL` and
|
||
`STATION DETAIL` now read as dormant title-table entries unless some still-unrecovered nonstandard
|
||
selector reaches them.
|
||
- Editor breadth: the broader map-editor page owner is now bounded through
|
||
`map_editor_panel_select_active_section` `0x004ce070` and
|
||
`map_editor_panel_dispatch_active_section_message` `0x004cf700`, which switch among the grounded
|
||
setup pages, `Cities/Regions`, `Territories`, the `Players` and `Player Pool` setup slices, the
|
||
now-grounded `Building Density` page, the locomotives-available and industry-availability pages,
|
||
the economic and special-condition pages, the `Port/Warehouse Cargos` page, and the later report
|
||
pages. The mid-editor ownership is materially clearer now too: the chairman-slot editor is the
|
||
`Players` page, the available-chairman editor is the `Player Pool` page, and the former unnamed
|
||
dual tri-state page now lines up with localized page text `997` `Building Density` plus help text
|
||
`1017` and the direct field captions `1642` `Starting Building Density Level:` and `1644`
|
||
`Building Density Growth:`. Both controls are now bounded as stored ordinal bytes `0/1/2` rather
|
||
than loose labels: the first three-choice control is the map-wide starting-density selector, with
|
||
its default middle state `1` matching the documented `100%` baseline from `1643`; the second is
|
||
the paired overall growth selector whose effects later appear in the city-growth side of the
|
||
simulation. `map_editor_city_region_panel_construct` and
|
||
`map_editor_city_region_panel_handle_message` own the city-or-region editing lane with rename and
|
||
copy-industry-data flows; `map_editor_territory_panel_construct` and
|
||
`map_editor_territory_panel_handle_message` own the territory rename and border-remap lane;
|
||
`map_editor_locomotive_availability_panel_construct` plus
|
||
`map_editor_locomotive_availability_panel_handle_message` now bound the locomotive policy page
|
||
over `0x006ada84`; `map_editor_industry_availability_panel_construct` plus
|
||
`map_editor_industry_availability_panel_handle_message` do the same for the industry candidate
|
||
pool at `0x0062b268`; and `map_editor_port_warehouse_cargo_panel_construct` plus
|
||
`map_editor_port_warehouse_cargo_panel_handle_message` now ground the recipe-book page that edits
|
||
port or warehouse cargo policies through twelve per-book state blocks at
|
||
`[0x006cec78+0x0fe7+index*0x4e1]`. Each book now has a shared maximum annual production float at
|
||
`book+0x3ed` and five fixed cargo-line entries starting at `book+0x3f1` with stride `0x30`; each
|
||
line is bounded as a mode dword, annual amount, a supplied-cargo token at `+0x08`, and a
|
||
demanded-cargo token at `+0x1c`. The constructor and handler now make those fields materially
|
||
tighter too: the row pair shown in `Supply Only` and `Production Demand->Supply` is the `+0x08`
|
||
supplied-cargo selector, the row pair shown in `Demand Only` and `Production Demand->Supply` is
|
||
the `+0x1c` demanded-cargo selector, and the single amount field at `+0x04` is labeled `Annual
|
||
Demand:` only in mode `1` but `Annual Supply:` in modes `2/3`. The stronger new runtime-side
|
||
result is now a full chain rather than only the importer:
|
||
`scenario_state_rebuild_port_warehouse_cargo_recipe_runtime_tables` first imports those same five
|
||
lines into one repeated array of identical `0xbc`-byte runtime descriptors with no row-index
|
||
special casing, and the candidate-side rebuild pass at
|
||
`structure_candidate_collection_rebuild_runtime_records_from_scenario_state` `0x00412d70` then
|
||
projects those descriptors into the live structure collection at `0x0062ba8c` before
|
||
`structure_candidate_rebuild_cargo_membership_and_scaled_rate_tables` `0x00411ee0` rebuilds the
|
||
per-cargo runtime summary tables. The player-facing line modes remain `Disabled`, `Demand Only`,
|
||
`Supply Only`, and `Production Demand->Supply`, and the fourth mode is tighter now too: current
|
||
wording around `1674`, `1675`, and `504` plus the downstream scaling path says it is the
|
||
production-line mode governed by the shared annual production cap, where the entered annual amount
|
||
stays on the supply side while the demanded side becomes the normalized input branch. The
|
||
candidate-side accumulator pass reinforces that split by applying the shared production cap only
|
||
to the supply-half runtime branch and bypassing that scaling on the normalized demand half. The
|
||
lower gameplay side is tighter now too: `structure_candidate_query_cargo_runtime_summary_channels`
|
||
`0x00412650` is the first grounded consumer beneath that rebuild chain, because it lazily rebuilds
|
||
four per-cargo summary banks and returns one direct-supply channel, one cap-normalized supply
|
||
channel, one demand or input channel, and one scaled production-output subrow channel for a
|
||
requested cargo id; the sibling helper `structure_candidate_supports_or_references_cargo_id`
|
||
`0x004129d0` then uses those same banks plus the cached cargo-membership arrays to answer whether
|
||
a live candidate materially references a cargo at all. One broader collection pass also now ties
|
||
the editor rule side back into runtime filtering:
|
||
`structure_candidate_collection_refresh_cargo_economy_filter_flags` `0x0041eac0` rebuilds
|
||
per-candidate flag `[candidate+0x56]` across the live structure collection, and current grounded
|
||
callers show it rerunning directly off the runtime cargo-economy latch at `[0x006cec74+0x25f]`,
|
||
which aligns this lane with the editor's `Disable Cargo Economy` special condition rather than
|
||
leaving it as a purely editor-owned recipe page. The first common live gate beneath that filter is
|
||
now bounded too: `structure_candidate_is_enabled_for_current_year` `0x0041e220` is the shared
|
||
year-and-filter check used by the collection refresh and later candidate-selection branches, while
|
||
`structure_candidate_rebuild_local_service_metrics` `0x0041e2b0` is a setup-side local service
|
||
scorer that already consumes the same enabled candidate records through world-grid sampling. One
|
||
steady-state world-side consumer is now grounded as well:
|
||
`placed_structure_rebuild_candidate_cargo_service_bitsets` `0x0042c690` walks linked placed
|
||
structures, filters live category-`2` candidates through
|
||
`structure_candidate_is_enabled_for_current_year`, and compacts the direct and scaled supply-side
|
||
channels from `0x00412960` and `0x004129a0` into per-cargo bitsets on the placed-structure record.
|
||
The next site-side owner layer is tighter now too:
|
||
`placed_structure_refresh_linked_candidate_flag4` `0x0042c8f0` refreshes the sibling state bit at
|
||
`[site+0x0e6]`, `placed_structure_refresh_candidate_service_state` `0x0042cdf0` ties that flag
|
||
refresh to the cargo-service bitset rebuild,
|
||
`placed_structure_rebuild_candidate_local_service_tables` `0x0042ce00` then performs the heavier
|
||
per-site candidate score rebuild over the aligned float and word tables at `[site+0x107]`,
|
||
`[site+0x02]`, and `[site+0x6c]`, and `placed_structure_refresh_local_service_score_bundle`
|
||
`0x0042d580` is now the local wrapper that chains that rebuild into the neighboring post-passes
|
||
before the world-grid owner continues. Those post-passes are tighter too:
|
||
`placed_structure_apply_route_linked_service_caps` `0x0042cc50` is the first route-backed cap pass
|
||
over the rebuilt local tables,
|
||
`placed_structure_redistribute_local_service_pressure_from_neighbors` `0x0042c1b0` is the
|
||
neighboring-site redistribution pass, and `placed_structure_clamp_candidate_service_age_table`
|
||
`0x0042cb30` is the final recent-service clamp over the primary per-candidate word table. Above
|
||
that refresh lane, `placed_structure_query_candidate_local_service_metrics` `0x0047e240` is the
|
||
first higher-level query, `placed_structure_count_candidates_with_local_service_metrics`
|
||
`0x0047e330` counts how many candidates currently produce that query, and
|
||
`placed_structure_query_cached_express_service_class_score` `0x0047e390` caches one parallel class
|
||
score for the express family now strongly aligned with `Passengers`, `Mail`, and `Troops`. Those
|
||
site-side scores now have grounded shell read-side consumers too:
|
||
`shell_station_detail_format_freight_and_express_summary` `0x00506be0` formats the visible
|
||
`Freight: %1` and `Express: %1` lines in `StationDetail.win`, and the same station-detail family
|
||
now has a deeper candidate-service lane:
|
||
`shell_station_detail_set_active_candidate_service_preview` `0x00504ae0` stores the active
|
||
`(station id, candidate id)` pair for the world-side preview scan,
|
||
`shell_station_detail_clear_active_candidate_service_preview` `0x00504a90` tears that pair back
|
||
down, `shell_station_detail_update_candidate_service_entry` `0x00504ba0` is the shell-side entry
|
||
updater above that preview pair, `shell_station_detail_format_candidate_local_service_summary`
|
||
`0x00504bea` uses `placed_structure_query_candidate_local_service_metrics` together with localized
|
||
ids `681`, `682`, and `2813` to build the visible candidate service text,
|
||
`shell_station_detail_build_to_from_haul_summary_widget` `0x00505150` now grounds the paired `To`
|
||
and `From` hauled-traffic strip widgets, `shell_station_detail_present_to_from_haul_stats_popup`
|
||
`0x00504770` owns their click-through annual and lifetime `hauled TO/FROM this station` popup,
|
||
`shell_station_detail_refresh_nearby_structure_jump_rows` `0x00505470` owns the five-row
|
||
nearby-structure jump lane, and `shell_station_detail_refresh_candidate_service_rows` `0x00505760`
|
||
is now the larger row-list owner that enumerates active candidate-service entries and wires
|
||
`shell_station_detail_update_candidate_service_entry` into the per-row click path.
|
||
`shell_station_list_format_freight_and_express_availability_summary` `0x00506e50` feeds the
|
||
station-list summary `%1 has %2 freight loads and %3 express loads available for hauling...`, and
|
||
the paired modifier helper `shell_station_list_handle_center_or_rename_action` `0x00506d50` owns
|
||
the visible Shift-center and Ctrl-rename row actions. The report side is clearer as well:
|
||
`0x004d3060` is the dedicated `Stats - Trees` constructor over `map_editor_tree_stats_report`,
|
||
`0x004d3080` is the actual `General Validation` constructor over
|
||
`map_editor_general_validation_report`, and the same page table also now grounds `Stats - Cargo`,
|
||
`Stats - City/Region`, `Stats - City Count`, `Event Variable Values`, and the neighboring
|
||
event-validation page. The remaining open editor edge is therefore mostly the deeper gameplay
|
||
meaning of those site-side service scores and flag bits, not page ownership.
|
||
|
||
- Station-detail overlay: the shell-side candidate preview pair now has a grounded world consumer
|
||
too. `world_render_station_candidate_service_map_overlay` at `0x0043f640` reads the active
|
||
`(station id, candidate id)` pair from `0x005ee4fc` and `0x005ee500`, scans the placed-structure
|
||
collection, and then splits the legend by candidate mode. When the active candidate carries a
|
||
nonzero route-style byte at `[candidate+0x46]`, the overlay uses the heavier helper
|
||
`placed_structure_query_candidate_directional_route_overlay_summary` at `0x0047e690` in both
|
||
directions between the preview station and each scanned site and formats the resulting directional
|
||
rows as `3874` `Coming To %1` and `3875` `Going From %1`. When that byte is zero, the same overlay
|
||
falls back to the direct local-service path through
|
||
`placed_structure_query_candidate_local_service_metrics` and formats the two legend rows instead
|
||
as `3876` `Current Supply @ < %1` and `3877` `Current Demand @ > %1`. Empty directional lanes
|
||
collapse to `3878` `--None--`, and one title lane falls back to literal `All`. Current disassembly
|
||
no longer supports treating `3872` `Already Connected by Another Company` or `3873` `Not
|
||
Connected` as direct overlay-body emits.
|
||
- Station-detail overlay, corrected boundary: the neighboring connection-state note pair now appears
|
||
to live in a city connection-bonus label formatter, not in `0x0043f640`. That formatter is now
|
||
bounded as `city_site_format_connection_bonus_status_label` at `0x004207d0`: it directly chooses
|
||
localized ids `3868` through `3873` after consulting the reusable city-peer scan
|
||
`city_connection_bonus_exists_matching_peer_site` at `0x00420030`. Separately, `3879` `Out of
|
||
Sync` is grounded under the multiplayer preview dataset path instead:
|
||
`multiplayer_preview_dataset_service_launch_state_and_warn_out_of_sync` at `0x0046b780` checks
|
||
global `0x006cd91c`, raises the `Out of Sync` shell status through `0x5386e0`, and then continues
|
||
through the wider multiplayer preview launch-state service. So the station-detail overlay
|
||
currently owns only the `Coming To`, `Going From`, `Current Supply`, `Current Demand`, `--None--`,
|
||
and `All` legend lanes.
|
||
- Station-detail overlay, ownership side: one reusable site helper is grounded now too.
|
||
`placed_structure_query_linked_company_id` at `0x0047efe0` resolves the current placed structure's
|
||
linked instance through `0x0062b26c` and returns its company id from `[instance+0x276]`; the
|
||
adjacent city bonus formatter at `0x004207d0` compares that id against the active company selector
|
||
before choosing whether a scanned site should carry `3871` `Connected By Another Company` or
|
||
`3872` `Already Connected by Another Company`. The larger caller boundary is no longer open
|
||
either: the first bounded announcement owner above this formatter family is now
|
||
`company_evaluate_and_publish_city_connection_bonus_news` at `0x00406050`, which re-enters the
|
||
peer-route candidate builder at `0x004046a0` and later publishes one of the localized
|
||
city-connection bonus news strings `2888`, `2890`, or `2921` through the shell news path.
|
||
- Station-detail overlay, peer-selector side: the city bonus formatter no longer depends only on
|
||
boolean peer existence. The companion helper
|
||
`city_connection_bonus_select_first_matching_peer_site` at `0x00420280` is now grounded as the
|
||
first-match selector paired with `city_connection_bonus_exists_matching_peer_site`: it walks the
|
||
same city-peer candidate set, applies the same site-class table plus the narrower
|
||
station-or-transit and linked-instance class-byte flags, and returns one representative matching
|
||
peer site id instead of a boolean. `city_site_format_connection_bonus_status_label` reuses that
|
||
selector after the note checks so it can recover one linked company context from the selected
|
||
peer. The remaining open edge here is therefore above this formatter family, not inside the
|
||
peer-scan pair itself.
|
||
- Station-detail overlay, caller side: the reusable bridge between the status formatter and the
|
||
company news lane is now bounded too. `city_connection_bonus_build_peer_route_candidate` at
|
||
`0x004046a0` reuses `city_connection_bonus_select_first_matching_peer_site` with both selector
|
||
flags forced on, samples the selected peer's derived coordinates through `0x0047df30` and
|
||
`0x0047df50`, and then either tries the shared heavy builder
|
||
`city_connection_try_build_route_with_optional_direct_site_placement` `0x00402cb0` or falls
|
||
back to the smaller wrapper `city_connection_bonus_try_compact_route_builder_from_region_entry`
|
||
`0x00404640` before handing the result back to the company-side announcement sweep at
|
||
`0x00406050`. The score side of that announcement lane is tighter now as well:
|
||
`city_compute_connection_bonus_candidate_weight` at `0x004010f0` provides the per-city opportunity
|
||
weight, `company_query_min_linked_site_distance_to_xy` at `0x00405920` provides the nearest
|
||
linked-site distance term, `company_count_linked_transit_sites` at `0x00426590` provides one of
|
||
the company-side caps, `company_compute_connection_bonus_value_ladder` at `0x00425320` supplies
|
||
the bounded company-side value scalar, `company_compute_issue39_opinion_bias_scalar` at
|
||
`0x00424580` contributes one smaller issue-`0x39` opinion term,
|
||
`scenario_state_sum_issue_opinion_terms_raw` at `0x00436710` now bounds the raw additive
|
||
issue-total helper beneath that term, and `company_connection_bonus_lane_is_unlocked` at
|
||
`0x00427590` is the small boolean gate above the ladder. Wider governance and CompanyDetail xrefs
|
||
now tighten slot `0x2b` into a rolling shareholder-facing performance lane reused by annual
|
||
shareholder-revolt or creditor-pressure checks and a per-share/history formatter, while `0x09`
|
||
remains the narrower current governance-pressure term read after those broader gates. The wider
|
||
sibling news owner above the same city-pair route family is bounded now too:
|
||
`simulation_try_select_and_publish_company_start_or_city_connection_news` `0x00404ce0`
|
||
filters and scores candidate city entries, re-enters the same shared heavy builder through
|
||
`city_connection_try_build_route_between_region_entry_pair` `0x00404c60` for the dense pair
|
||
sweep and the final retry, and then publishes `2889` `%1 has started a new company - the %2`
|
||
or `2890` `%1 has connected %2 to %3.` through the shell news path. The remaining open edge on
|
||
this branch is therefore narrower now: it is mostly whether `0x39` should be read as the
|
||
narrower city-connection public-opinion lane or as part of a broader management-attitude family,
|
||
not the ownership of the connection-bonus formatter, peer-route candidate path, or company news
|
||
gate.
|
||
- Station-detail overlay, route-list side: the neighboring helper
|
||
`placed_structure_append_unique_route_entry` at `0x0047f010` is now grounded as the
|
||
append-if-missing builder for the six-byte route-entry list rooted at `[site+0x462]` and
|
||
`[site+0x466]`. That matters here because the directional overlay query at `0x0047e690` consumes
|
||
the same list, so the remaining uncertainty is no longer list ownership. It is down to the exact
|
||
semantics of each entry's `u32` payload.
|
||
|
||
## Next Mapping Passes
|
||
|
||
- Resolve the owner-side callback roles behind the validated GameSpy packet branches, especially
|
||
`[route+0x9c]`, `[route+0xa0]`, `[route+0xa4]`, and `[route+0xd4]`.
|
||
- Determine whether any later world-mode branch beneath the frame owner bypasses the shell
|
||
controller input path for non-cursor gameplay input, since the current grounded overlay and cursor
|
||
helpers still reuse `0x006d4018`.
|
||
- Keep detailed pending-template or transport work scoped to the specific atlas edges that remain
|
||
unresolved.
|