2026-04-02 23:11:15 -07:00
|
|
|
|
# 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.
|
|
|
|
|
|
|
2026-04-06 01:11:06 -07:00
|
|
|
|
Subsystem notes now live under [docs/atlas](/home/jan/projects/rrt/docs/atlas/README.md) for
|
|
|
|
|
|
faster navigation. The longform atlas remains the canonical narrative reference during this
|
|
|
|
|
|
transition.
|
|
|
|
|
|
|
2026-04-02 23:11:15 -07:00
|
|
|
|
## CRT and Process Startup
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and
|
|
|
|
|
|
`app_bootstrap_main` at `0x00484440`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- Trigger/Cadence: single process startup path before shell or engine services exist.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Bootstrap and Shell Service Bring-Up
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- Roots: `app_bootstrap_main` at `0x00484440`, `bootstrap_init_shell_window_services` at
|
|
|
|
|
|
`0x004840e0`, and `shell_install_global_controller` at `0x0051ff90`.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Shell UI Command and Deferred Work Flow
|
|
|
|
|
|
|
|
|
|
|
|
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Presentation, Overlay, and Frame Timing
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Map and Scenario Content Load
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Multiplayer Session and Transport Flow
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
into a probe request id plus displayed version/build integer, and
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
that averaged `ms` sample through `multiplayer_transport_enqueue_callback_slot24_record`. So the
|
|
|
|
|
|
publication boundary is explicit and the request-id ownership is explicit: `[entry+0x80]` now
|
|
|
|
|
|
reads as the averaged millisecond probe sample and `[entry+0x54]` as the displayed version/build
|
|
|
|
|
|
companion integer. The adjacent route-callback side is tighter too, but it is now kept separate:
|
|
|
|
|
|
the staged route-callback path at `0x5958e0` and the later compatibility gate at
|
|
|
|
|
|
`multiplayer_transport_route_binding_matches_route_callback_descriptor_tuple` `0x595d00` operate
|
|
|
|
|
|
on a compact GameSpy-style server or route descriptor family with a primary endpoint tuple at
|
|
|
|
|
|
`[descriptor+0x00]/[+0x04]`, an optional secondary endpoint tuple at `[descriptor+0x08]/[+0x0c]`,
|
|
|
|
|
|
string-key lookups such as `hostname` and `gamever`, and numeric-key lookups such as
|
|
|
|
|
|
`numplayers`, `numservers`, `numwaiting`, and `gsi_am_rating`. The route-binding side uses that
|
|
|
|
|
|
descriptor family's primary dword and host-order port plus the optional secondary tuple against
|
|
|
|
|
|
route-binding offsets `+0x54/+0x58` and route word `+0x30`. Current evidence still does not prove
|
|
|
|
|
|
that descriptor tuple is the same field family as the selector-view marker companion integer at
|
|
|
|
|
|
`[entry+0x54]`. The higher compact decode owners are tighter now too: `0x5907d0` is the
|
|
|
|
|
|
allocate-and-append lane for one self-consistent compact payload, while `0x590d00` is the keyed
|
|
|
|
|
|
upsert-by-primary-endpoint lane that reuses an existing descriptor when possible and then notifies
|
|
|
|
|
|
the owner callback. The route-callback-table runtime above that decode side is tighter now too:
|
|
|
|
|
|
`multiplayer_transport_route_callback_table_construct` `0x5905e0` seeds one transport-owned
|
|
|
|
|
|
table block, `multiplayer_transport_route_callback_table_release_decoded_schema_dictionary`
|
|
|
|
|
|
`0x5906f0` tears down the decoded schema dictionary rooted at `[this+0x08]`,
|
|
|
|
|
|
`multiplayer_route_callback_runtime_acquire_shared_string_copy` `0x590540` and
|
|
|
|
|
|
`multiplayer_route_callback_runtime_release_shared_string_copy` `0x5905a0` now bound the shared
|
|
|
|
|
|
string pool used by that decoded schema, and the higher bring-up owner `0x596090` now clearly
|
|
|
|
|
|
splits between `[transport+0xba4]` with owner callback `0x595a40`, the local field-cache family
|
|
|
|
|
|
`[transport+0x1724]` seeded through `0x5a08f0/0x595b60`, and `[transport+0x1164]` with owner
|
|
|
|
|
|
callback `0x595bc0`, while
|
|
|
|
|
|
`multiplayer_transport_route_callback_table_service_receive_decode_state_machine` `0x5908c0`
|
|
|
|
|
|
is the current live receive/decode state machine serviced by `0x591290` in table states `2/3`.
|
|
|
|
|
|
The route-handle lifecycle above that decode path is tighter now too: `0x590740` cleanly resets
|
|
|
|
|
|
one table's live route plus decode-side runtime without destroying the outer object; `0x5907a0`
|
|
|
|
|
|
is the broader destroy path that also releases the active descriptor collection; `0x590ed0`
|
|
|
|
|
|
opens the live route handle into `[this+0x4a0]`, stages the initial outbound request, and seeds
|
|
|
|
|
|
state `3` plus the staged receive buffer; `0x5911e0` is the state-`2/3` socket-service wrapper
|
|
|
|
|
|
that reads new bytes, grows the staged buffer, and re-enters `0x5908c0`; `0x5912c0` is the
|
|
|
|
|
|
one-shot send-with-reopen-retry helper; and `0x590ea0` is the shared disconnect publication and
|
|
|
|
|
|
reset tail. The recurring service helper `0x591290` is tighter too: it now first clears the
|
|
|
|
|
|
staged intrusive descriptor list through `0x590490` before entering the state-driven seed-or-
|
|
|
|
|
|
receive branch. The upstream owners are tighter too: `0x5962e0` is now the field-subscription
|
|
|
|
|
|
route-table opener above `[transport+0xba4]`, while `0x596530` is the `gsi_am_rating` reopen
|
|
|
|
|
|
path above `[transport+0x18bc]`. On that latter branch, `0x590dc0` is now bounded as the
|
|
|
|
|
|
state-`0` raw-endpoint seed pass over the live route handle, repeatedly pulling endpoint tuples
|
|
|
|
|
|
through `0x58bc7e` record type `0x1f3` before stamping descriptor flag byte `0x15` with `0x11`.
|
|
|
|
|
|
That makes the remaining source-flag meaning narrower too: current evidence now supports reading
|
|
|
|
|
|
byte-`0x15` bit `0x1` as a primary-endpoint-seed or endpoint-only marker. In the `gsi_am_rating`
|
|
|
|
|
|
dispatcher, clear-bit descriptors can take the richer direct transition lane, while set-bit
|
|
|
|
|
|
descriptors are staged through the queued enrichment path and still suppress that direct
|
|
|
|
|
|
transition even after the ready bit arrives.
|
|
|
|
|
|
The adjacent capacity-descriptor side is tighter too: `0x595bc0` now clearly publishes a
|
|
|
|
|
|
descriptor block from live descriptor properties `hostname`, `numwaiting`, `maxwaiting`,
|
|
|
|
|
|
`numservers`, and `numplayers` plus three carried sidecar scalars. That sidecar at
|
|
|
|
|
|
`[transport+0x1778]` is tighter now too: current evidence says it behaves as one cached pointer
|
|
|
|
|
|
into the transient work-record family at `[transport+0x1780]`, because every meaningful branch
|
|
|
|
|
|
in `0x595bc0` reads the same `+0x0c/+0x10/+0x18` metadata triplet and replay modes later consume
|
|
|
|
|
|
the pointer through `0x5933a0`. The negative result is stronger too: local text-side xrefs still
|
|
|
|
|
|
show no direct store to `[transport+0x1778]`, and a wider local sweep also failed to show any
|
|
|
|
|
|
obvious `lea`-based replay-band writer, so the sidecar writer remains upstream of this leaf
|
|
|
|
|
|
publisher. Mode `0` is now also tied more cleanly to the generic descriptor append-notify lane
|
|
|
|
|
|
at `0x590370`, while mode `2` stays outside this helper as the separate
|
|
|
|
|
|
remove-notify-and-stage path at `0x590430`. The opcode-`2` payload boundary is tighter too:
|
|
|
|
|
|
`0x592ae0` now grounds that payload
|
|
|
|
|
|
as a seven-dword block with an owned string slot at `+0x08`, so live mode supplies a populated
|
|
|
|
|
|
payload while modes `3` and `5` deliberately enqueue an all-zero payload and reuse only
|
|
|
|
|
|
the wrapper-side sidecar metadata. Those two modes are tighter now too: they are the live
|
|
|
|
|
|
receive-state owner callbacks emitted by `0x5911e0 -> 0x5908c0`, not loose generic replay
|
|
|
|
|
|
guesses. So those paths are better read as delayed metadata replays over one cached work record,
|
|
|
|
|
|
not over a separate anonymous cache blob. The neighboring
|
|
|
|
|
|
work queue is tighter too: `0x593330/0x593370/0x593380` now bound `[transport+0x1780]` as the
|
|
|
|
|
|
construct/clear/destroy owner family, while `0x5933a0`, `0x5934e0`, and `0x593570` ground the
|
|
|
|
|
|
remove, allocate, and completion side over that same collection. The small sibling `0x593400` is
|
|
|
|
|
|
tighter too: it is a pure work-record uniqueness predicate over field `+0x0c`. Its caller is
|
|
|
|
|
|
tighter now too: `0x58d720` is an immediate-drain quiescence gate over one transport context id,
|
|
|
|
|
|
using `0x593400` for the queued work family at `[transport+0x1780]` and `0x592970` for the
|
|
|
|
|
|
active opcode-record collection at `[transport+0x17fc]`. The strongest current read is that
|
|
|
|
|
|
`0x5934c0` seeds that shared drain context id first, then the transport copies it into queued
|
|
|
|
|
|
work field `+0x0c` and active opcode-record field `+0x14` before the immediate-drain roots wait
|
|
|
|
|
|
on one shared disappearance test rather than on a vague settle loop. The currently grounded
|
|
|
|
|
|
roots are `0x58df20`, the neighboring formatted selector-text publish path at `0x58dfb0`, and
|
|
|
|
|
|
callback-table registration at `0x58e200`. The active-opcode side is tighter too: `0x5927b0`
|
|
|
|
|
|
now bounds the per-record service-and-retire path, `0x592800` the wider context-or-idle sweep,
|
|
|
|
|
|
`0x5929a0` the remove-by-opcode-type sweep, and `0x5929f0` the narrower opcode-`3`
|
|
|
|
|
|
field-snapshot removal keyed by the subscribed callback-pair payload. That also corrects
|
|
|
|
|
|
`0x595b80`, whose final cleanup is an active field-snapshot purge rather than a queued-work
|
|
|
|
|
|
drain. The adjacent route-callback descriptor-table lifecycle is tighter too: `0x590410` now
|
|
|
|
|
|
grounds `[table+0x5bc]` as the staged intrusive descriptor-list head, `0x590430` is the generic
|
|
|
|
|
|
remove-notify-and-stage lane, `0x590490` releases the staged list, and `0x5904d0` releases the
|
|
|
|
|
|
active descriptor collection before tearing that staged list down. That also makes the earlier
|
|
|
|
|
|
`0x5962e0` “release active descriptors” step explicit. The callback-table attach side now constrains the
|
|
|
|
|
|
same work-record metadata family a little further too: `0x593650` deliberately duplicates one
|
|
|
|
|
|
caller metadata dword into both fields `+0x0c` and `+0x18`, while preserving the remaining
|
|
|
|
|
|
caller callback function pointer in `+0x10`. The lower opcode wrappers are tighter now too:
|
|
|
|
|
|
`0x592a40` and `0x592a70` both consume that staged triplet in the order `( callback fn +0x10,
|
|
|
|
|
|
callback companion +0x18, drain context id +0x0c )`. The producer side is tighter too:
|
|
|
|
|
|
bound-route requests, selector-text route requests, and the type-`9` text fastpath also stage
|
|
|
|
|
|
that same triplet through `0x5934e0`, and the fastpath shim `0x593d00` now gives the cleanest
|
|
|
|
|
|
proof of the callback split by only re-emitting the follow-on lane when `+0x10` is nonnull and
|
|
|
|
|
|
then forwarding `(+0x10, +0x18, +0x0c)` into `0x593170` as callback function, callback
|
|
|
|
|
|
companion, and trailing drain context. So the replay-side triplet is clearly a broader
|
|
|
|
|
|
transport callback-wrapper family, not one fixed route-only tuple. The nearby
|
|
|
|
|
|
field-subscription side is tighter too: `0x592b50` now clearly uses `[transport+0x1774]` as a
|
|
|
|
|
|
cached progress percentage under `[transport+0xba4]`, and `0x5962e0` seeds that percentage to
|
|
|
|
|
|
`1` just before the first immediate mode-`3` snapshot. The nearby route-callback-table
|
|
|
|
|
|
lifecycle is tighter now too: `0x596090` seeds `[transport+0xba0]` as the callback-plumbing
|
|
|
|
|
|
enable latch, clears staged payload slot `[transport+0xb50]`, and constructs the three owner
|
|
|
|
|
|
branches rooted at `[transport+0xba4]`, `[transport+0x1164]`, and `[transport+0x18bc]`. The
|
|
|
|
|
|
matching local cleanup is tighter too: `0x5962c0` is the explicit staged route-callback payload
|
|
|
|
|
|
clear on `[transport+0xb50]`, while `0x595ce0` now clearly resets only the capacity-descriptor
|
|
|
|
|
|
route callback table at `[transport+0x1164]`, not the field-subscription table at
|
|
|
|
|
|
`[transport+0xba4]`. The remaining gap on the capacity side is
|
|
|
|
|
|
therefore narrower: mainly the still-unrecovered writer that stages `[transport+0x1778]`. The
|
|
|
|
|
|
carried sidecar fields themselves now read more cleanly as the cached callback-wrapper triplet
|
|
|
|
|
|
reused elsewhere (`drain context id +0x0c`, `callback fn +0x10`, `callback companion +0x18`),
|
|
|
|
|
|
and the negative result is stronger too: nearby replay-band fields `[transport+0x176c]`,
|
|
|
|
|
|
`[transport+0x1770]`, `[transport+0x1774]`, `[transport+0x177c]`, `[transport+0x1780]`, and
|
|
|
|
|
|
`[transport+0x1784]` all have direct local owners while `[transport+0x1778]` still appears only
|
|
|
|
|
|
as the single read in `0x595bc0`. So the remaining writer looks upstream and indirect rather
|
|
|
|
|
|
than like one missing ordinary field store in the local cluster. The adjacent staged-route
|
|
|
|
|
|
callback side is tighter too: `0x595860` is now bounded as the
|
|
|
|
|
|
submit-result handler beneath `0x5958e0`, and the old `[transport+0xac0]` ambiguity there is now
|
|
|
|
|
|
gone. That branch is using the already-grounded third selector-generation counter at `[0xac0]`
|
|
|
|
|
|
together with target `[0xb48]` to decide whether staged route-callback traffic can push the
|
|
|
|
|
|
multiplayer route-mode ladder from `2` into `3` and later `4`. The selector-view counter beneath
|
|
|
|
|
|
that gate is tighter now too: `0x594e30` counts slot-`2` entries whose flag dword carries bit
|
|
|
|
|
|
`0x20`, optionally filtered by the current transport name buffer. The selector-view mutation
|
|
|
|
|
|
family under that same lane is tighter too: `0x594a30` is now the direct keyed-store remover,
|
|
|
|
|
|
`0x594fb0` clears one selector-slot ownership pointer plus its slot-local flag dword and drops
|
|
|
|
|
|
the whole entry when no slots remain, `0x595010` rekeys one selector-view entry under a new name
|
|
|
|
|
|
while preserving the `0x40..` runtime band, and callback root `0x59f9c0` now reads as the
|
|
|
|
|
|
sibling lane that clears one named selector-view slot, publishes callback slot `18`, and may
|
|
|
|
|
|
still re-enter the route-mode setter from the same slot-`2` status and generation gates. The
|
|
|
|
|
|
neighboring callback roots are tighter now too: `0x5950a0` clears one selector slot from every
|
|
|
|
|
|
selector-view entry in the keyed store, `0x59fab0` is the rename or relabel sibling above
|
|
|
|
|
|
`0x595010`, `0x59faf0` updates one selector slot's fixed sample-text buffer and refreshes the
|
|
|
|
|
|
active selector object when present, and `0x59fb60` replaces one selector slot's name set,
|
|
|
|
|
|
requests the default profile-key bundle for that slot, and publishes callback slot `20`. Slot
|
|
|
|
|
|
`16` is tighter now too: current grounded caller `0x59f440` forwards the staged route-callback
|
|
|
|
|
|
payload handle from `[transport+0xb50]` through `0x592ea0` just before route mode `5`. The
|
|
|
|
|
|
last adjacent callback root in that block is tighter now too: `0x59fbd0` is the built-in
|
|
|
|
|
|
per-slot profile-key query sibling. It resolves the caller selector name into one slot index,
|
|
|
|
|
|
forwards the caller trio into `0x596b90`, and then publishes callback slot `28`; that lower
|
|
|
|
|
|
helper indexes one slot-specific built-in string pair from `[transport+0x189c]` and
|
|
|
|
|
|
`[transport+0x18ac]`, reuses the generic per-key handler `0x596970`, and only republishes slot
|
|
|
|
|
|
`28` when that lower query path succeeds.
|
|
|
|
|
|
The compact-header side is tighter now too: `0x58fe20` and `0x58ff20` now show that compact
|
|
|
|
|
|
payloads always carry the primary IPv4 dword and that header bit `0x10` only gates whether the
|
|
|
|
|
|
primary port word is inline or inherited from the owner default port. `0x58fe90` now validates
|
|
|
|
|
|
the `0x40` inline keyed-property vector against the owner schema, and `0x58fe50` validates the
|
|
|
|
|
|
signed-`0x80` trailing string-pair tail before decode. `0x58ff60` then grounds bit `0x02` as
|
|
|
|
|
|
the inline secondary IPv4 dword branch, bit `0x20` as the paired secondary-port word branch with
|
|
|
|
|
|
owner-port fallback, bit `0x08` as one still-unresolved auxiliary dword stored at
|
|
|
|
|
|
`[descriptor+0x10]`, bit `0x40` as one inline keyed-property vector decoded through the
|
|
|
|
|
|
property-store writers, and signed bit `0x80` as one trailing string-pair tail. The
|
|
|
|
|
|
descriptor-state side is tighter now too: the shared queue helper at
|
|
|
|
|
|
`0x005a09a0` stamps pending state `0x4` for the local field-cache family `[transport+0x1724]`
|
|
|
|
|
|
and pending state `0x8` for the `gsi_am_rating` queued-descriptor family `[transport+0x1e7c]`,
|
|
|
|
|
|
while later service through `0x005a0c80` promotes those pending tags into ready bits `0x1` and
|
|
|
|
|
|
`0x2` in descriptor byte `[entry+0x14]`. That makes the current transport-side tests cleaner:
|
|
|
|
|
|
`0x58d1c0` is the field-cache ready gate, `0x58d1d0` is the `gsi_am_rating` queued-descriptor
|
|
|
|
|
|
ready gate, and `0x58d230` is the remaining flag-byte split between direct primary-endpoint
|
|
|
|
|
|
handling at `[transport+0x18bc]` and the queued path at `[transport+0x1e7c]`. That byte-`0x14`
|
|
|
|
|
|
story is no longer queue-only either: `0x58ff60` can also OR in bit `0x1` after the inline
|
|
|
|
|
|
keyed-property vector and bit `0x2` after the signed string-pair tail. The flag-byte split is no
|
|
|
|
|
|
longer purely behavioral either: current evidence now says byte `[descriptor+0x15]` bit `0x1` is
|
|
|
|
|
|
a source-side descriptor header bit, explicitly seeded during the primary-endpoint table refresh
|
|
|
|
|
|
around `0x590dc0` and preserved by the compact descriptor decode path at `0x58ff60`, rather than
|
|
|
|
|
|
a queue-generated runtime state. The `gsi_am_rating` dispatcher side is tighter too: that same
|
|
|
|
|
|
bit no longer just looks like a direct-versus-queued routing split, because `0x595e10` also uses
|
|
|
|
|
|
it to suppress the direct `0x595dc0` transition even after queued ready bit `0x2` is present.
|
|
|
|
|
|
The descriptor body is tighter too: `[descriptor+0x20]` is now the intrusive next-link used by
|
|
|
|
|
|
the transport-owned primary-endpoint list headed at `[table+0x5bc]`, and `[descriptor+0x1c]` is
|
|
|
|
|
|
now the special numeric scalar behind the current `queryid`/`ping` fallback pair. The compact-only
|
|
|
|
|
|
auxiliary dword at `[descriptor+0x10]` is tighter in a negative way too: local xref scans now
|
|
|
|
|
|
only show it being preserved by later generic helpers like
|
|
|
|
|
|
`generic_record_0x1c_deep_copy_with_owned_string_at_0x08` `0x591410` and the adjacent
|
|
|
|
|
|
callback-marshaling wrappers `0x591480` and `0x591510`, not read through any dedicated semantic
|
|
|
|
|
|
accessor yet. The route-event dispatcher side is tighter too: the mode-`5` tails in both
|
|
|
|
|
|
callback families do not copy a descriptor-local field but instead mirror the transport-staged
|
|
|
|
|
|
companion dword at `[this+0x490]` into `[this+0x54]` and the local field-cache family. The
|
|
|
|
|
|
`gsi_am_rating` maintenance lane is tighter now too: after pruning failed descriptors it sorts
|
|
|
|
|
|
the surviving primary-endpoint table through `0x590310` in mode `1` with key `gsi_am_rating`,
|
|
|
|
|
|
then selects the new head through `0x590480` before re-entering the route-transition path. The
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
now looks more bounded too: it rebuilds the core `X%sX` marker text from `[entry+0x50]` through
|
|
|
|
|
|
`multiplayer_transport_format_selector_view_probe_marker_core`, formats one `PNG %s %d` line
|
|
|
|
|
|
around that marker and the entry-local averaged millisecond sample at `[entry+0x80]`, appends bounded
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 20:36:20 -07:00
|
|
|
|
route-mode state machine now looks consistent with that: `multiplayer_transport_set_route_mode`
|
|
|
|
|
|
latches the requested small mode at `[this+0x18b8]`, then uses mode `0` for the direct-versus
|
|
|
|
|
|
queued `gsi_am_rating` split, mode `1` for the ready-bit plus queued fallback, mode `2` for
|
|
|
|
|
|
pending-descriptor cleanup, mode `3` for the empty-table fallback, mode `4` for deferred
|
|
|
|
|
|
route-status recovery, and mode `5` for copying the staged route companion dword into `[this+0x54]`
|
|
|
|
|
|
and the local field cache. The current grounded mode transitions still 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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`[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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`[transport+0x17f8]`. The surrounding status-route callback vector is tighter now too:
|
|
|
|
|
|
`0x005970e0` publishes either the active selector text or the averaged probe sample at
|
|
|
|
|
|
`[entry+0x80]` and otherwise falls back to owner callback `[transport+0x17e0]`;
|
|
|
|
|
|
`0x00597180` is a straight owner-forwarding lane through `[transport+0x17e4]`;
|
|
|
|
|
|
`0x005971b0` seeds the local status-control id list and can then notify owner callback
|
|
|
|
|
|
`[transport+0x17e8]`; and `0x00597270` returns the third selector-slot generation counter
|
|
|
|
|
|
`[transport+0xac0]` on its bounded local branch before falling back to owner callback
|
|
|
|
|
|
`[transport+0x17ec]`. Subtype `6` still validates the same cookie, dedupes one 32-bit cookie or
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Input, Save/Load, and Simulation
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
management of the two companies. By contrast the broader support-adjusted share-price helper
|
|
|
|
|
|
`company_compute_public_support_adjusted_share_price_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
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`track_lay_window_handle_message`. The same family also owns the downstream route-entry policy
|
|
|
|
|
|
bridge at `0x004955b0`: current caller evidence says that helper maps the live TrackLay primary
|
|
|
|
|
|
mode into endpoint-synthesis policy `1` or `4` before the tool re-enters `0x00493cf0`, which is
|
|
|
|
|
|
the strongest current bridge from the player-facing single-track versus double-track buttons into
|
|
|
|
|
|
the lower route-builder policy bytes. The same family also owns a bridge-type preference selector
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
an early route-entry search or synthesis attempt through
|
|
|
|
|
|
`route_entry_collection_try_build_path_between_optional_endpoint_entries` `0x004a01a0`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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`.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
That early lane can now be described more concretely: it can reuse supplied route-entry
|
|
|
|
|
|
endpoints, synthesize a missing leading endpoint from the caller's coordinates, seed the
|
|
|
|
|
|
route-store builder-state block, and then hand off to the deeper path-search core without
|
|
|
|
|
|
placing a new site. The previously vague side family at `0x006cfcb4` is tighter too: current
|
|
|
|
|
|
evidence now bounds it as a small auxiliary route-entry tracker collection with an allocator at
|
|
|
|
|
|
`0x004a42b0`, a refcount or destroy path at `0x004a4340`, a direct route-entry group-id setter
|
2026-04-06 01:11:06 -07:00
|
|
|
|
at `0x00489f80`, an endpoint-membership probe at `0x00494ed0`, a boolean-latch refresh or
|
|
|
|
|
|
owner-notify path at `0x00494fb0`, and a two-endpoint merge or bind helper at `0x00494f00`.
|
|
|
|
|
|
That bind side is tighter now too: the trackers group route entries only when a route-key-like
|
|
|
|
|
|
value from `0x0048aa70`, the route-entry signature word `+0x22e`, and the boolean latch derived
|
|
|
|
|
|
from byte `+0x44` all agree. The deeper handoff under that lane is no longer anonymous either:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`route_entry_collection_search_path_between_entry_or_coord_endpoints` `0x0049d380` is now
|
|
|
|
|
|
bounded as the internal search core that runs the first candidate sweep, quality gates, and the
|
|
|
|
|
|
later route-extension fallbacks before returning one resolved route-entry id or `-1`. The first
|
|
|
|
|
|
sweep now has a bounded owner at `0x0049bd40`, and the two later fallback helpers are bounded as
|
|
|
|
|
|
the route-entry point-window coverage query `0x00494cb0` and the frontier-extension helper
|
|
|
|
|
|
`0x0049c900`. The math side is bounded more cleanly now too: the family reuses
|
|
|
|
|
|
`math_measure_float_xy_pair_distance` `0x0051db80` for its direct point-to-point scalar,
|
|
|
|
|
|
`math_compute_quadrant_adjusted_heading_angle_from_xy_pair` `0x004952f0` for heading-angle
|
|
|
|
|
|
construction, `math_normalize_subtracted_angle_delta_and_report_wrap` `0x004953c0` for wrapped
|
|
|
|
|
|
angle-delta checks, and `math_abs_double_with_crt_special_case_handling` `0x005a152e` for the
|
|
|
|
|
|
absolute-value side of the quality gates. Current evidence also bounds the threshold shape a
|
|
|
|
|
|
little more tightly: the initial sweep and later extension path both reuse one signed angle-bias
|
|
|
|
|
|
term `+/-0.181000038854627`, and the first sweep switches between a looser `1.4` and stricter
|
|
|
|
|
|
`1.8` quality multiplier depending on route-policy byte `4` or the broader display-runtime
|
|
|
|
|
|
preference `[0x006cec78+0x4c74]`, already grounded elsewhere as `Auto-Show Grade During Track
|
|
|
|
|
|
Lay`.
|
|
|
|
|
|
The small policy-byte semantics are still not fully closed, but current evidence is tighter than
|
|
|
|
|
|
before. Literal byte `1` is now the strongest current match for direct linked-site
|
|
|
|
|
|
endpoint-anchor creation or replacement, because the linked-site constructor and neighboring
|
|
|
|
|
|
repair branches around `0x00480463`, `0x00480a77`, and `0x00480b69` all feed that byte into
|
|
|
|
|
|
`0x00493cf0` before rebinding one chosen route-entry anchor through `0x0048abc0`. Literal byte
|
|
|
|
|
|
`2` now looks broader: the later world-side caller at `0x00480cd0` and the linked-site refresh
|
|
|
|
|
|
helper `placed_structure_refresh_linked_site_display_name_and_route_anchor` `0x00480bb0` both
|
|
|
|
|
|
reach `0x004a01a0` with both optional endpoint-entry ids unset and that byte, so the strongest
|
|
|
|
|
|
current read is a full linked-site route-anchor rebuild between optional endpoint entries rather
|
|
|
|
|
|
than the narrower direct creation lane. The TrackLay side now gives one tighter user-facing
|
|
|
|
|
|
anchor too: the live mode field `0x00622b0c` is already grounded as `Lay single track.` `0x1`
|
|
|
|
|
|
versus `Lay double track.` `0x4`, and the small mapper `0x004955b0` collapses that state into
|
|
|
|
|
|
the endpoint-policy bytes later passed to `0x00493cf0`, so the strongest current read is
|
|
|
|
|
|
`policy 1 = single-track endpoint synthesis` and `policy 4 = double-track endpoint synthesis`.
|
|
|
|
|
|
Bytes `1/2` are still the ones that enable the
|
|
|
|
|
|
auxiliary tracker lane in `0x004a01a0`, while byte `4` now reads more narrowly as the larger
|
|
|
|
|
|
company-side endpoint-synthesis charge inside `0x00493cf0`, because that helper feeds `-1`
|
|
|
|
|
|
versus `-2` into the saturating company counter helper `0x00423ec0` on `[company+0x7680]`
|
|
|
|
|
|
rather than skipping the company branch entirely. That counter is tighter now too: nearby
|
|
|
|
|
|
company initialization seeds it to `50` when scenario byte `0x4aaf` is enabled and to sentinel
|
|
|
|
|
|
`-1` otherwise, while the companion getter `0x004240a0` returns either the live counter or fixed
|
|
|
|
|
|
fallback `29999`. Current language-table correlation also gives `0x4aaf` a stronger
|
|
|
|
|
|
player-facing meaning: it is the live gate behind `Company track laying is limited...` and the
|
|
|
|
|
|
event variable label `Company Track Pieces Buildable`. So the current strongest read is
|
|
|
|
|
|
available track-laying capacity, not an abstract company route-budget. That site-side helper
|
|
|
|
|
|
also closes one adjacent ownership gap: beneath
|
|
|
|
|
|
`placed_structure_finalize_creation_or_rebuild_local_runtime_state` `0x0040ef10`, it rebuilds
|
|
|
|
|
|
one linked site's route-entry anchor and display-name buffer. That path is tighter now than just
|
|
|
|
|
|
“bind anchor then touch `0x006cfcb4`”: after the literal-policy-`2` rebuild succeeds it
|
|
|
|
|
|
re-enters `aux_route_entry_tracker_collection_refresh_route_entry_group_membership` `0x004a45f0`,
|
2026-04-06 01:11:06 -07:00
|
|
|
|
which can now be described more concretely. Its early prepass at `0x004a4380` can split one
|
|
|
|
|
|
mismatching adjacent subchain into a fresh tracker group; its later helper at `0x004a4ce0` can
|
|
|
|
|
|
transfer one compatible adjacent chain between neighboring groups; and the repair-side helper at
|
|
|
|
|
|
`0x004a4ff0` can reseed a whole route-entry component into a fresh tracker when the old group id
|
|
|
|
|
|
is missing or invalid. The regrouping pass also still rewrites compatible endpoint slots through
|
|
|
|
|
|
`0x004950f0` and refreshes one nearby cached-match payload band through `0x00495020` before the
|
|
|
|
|
|
helper resumes the name-buffer work. The remaining display-name side still matches the earlier
|
2026-04-05 20:11:47 -07:00
|
|
|
|
grounded text family: copy the resolved city name when available, append civic suffixes
|
|
|
|
|
|
`Township`, `New`, `Modern`, or `Renaissance`, append `Service Tower` or `Maintenance Facility`
|
|
|
|
|
|
on the linked-instance class branches, and otherwise fall back through the older
|
|
|
|
|
|
`Junction`..`Center` and `Anytown` text families.
|
2026-04-06 01:11:06 -07:00
|
|
|
|
The higher owner-refresh side of that same tracker family is tighter now too. The reusable gate
|
|
|
|
|
|
at `0x004a4c00` no longer looks like a stray predicate: it explicitly requires both route-entry
|
|
|
|
|
|
trackers to keep matching cached fields `+0x18/+0x1c/+0x1d/+0x1e` and both route entries to keep
|
|
|
|
|
|
their special side fields `+0x20e/+0x222` unset before adjacent-chain transfer is allowed. The
|
|
|
|
|
|
collection-wide traversal sweep at `0x004a5fc0` is bounded now as the connected-component refresh
|
|
|
|
|
|
owner over tracker field `+0x0c`, with a temporary queue at `[this+0x94]` and direct fallback
|
|
|
|
|
|
into the reseed helper `0x004a4ff0` whenever one neighboring route entry has lost valid tracker
|
|
|
|
|
|
ownership. Above both of those sits `aux_route_entry_tracker_collection_refresh_owner_adjacent_compatible_group_links`
|
|
|
|
|
|
`0x004a6360`, which now reads as the owner-side latch-change refresher invoked from
|
|
|
|
|
|
`0x00494fb0`: it starts from one owner route-entry id, confirms both bound endpoint entries agree
|
|
|
|
|
|
on the boolean class state implied by route-entry byte `+0x44`, and then probes both endpoint
|
|
|
|
|
|
sides for adjacent route-entry groups that can be absorbed through the `0x004a4c00 -> 0x004a4ce0`
|
|
|
|
|
|
gate-and-transfer pair. The sibling query side is bounded now too:
|
|
|
|
|
|
`aux_route_entry_tracker_collection_query_component_label_by_tracker_id` `0x004a6320` is the
|
|
|
|
|
|
dirty-aware accessor for tracker field `+0x0c`, while
|
|
|
|
|
|
`aux_route_entry_tracker_dispatch_route_entry_pair_metric_query` `0x004a65b0` is the remaining
|
|
|
|
|
|
mode-switched lower metric dispatcher beneath the heavier chooser at `0x004a6630`, forwarding
|
|
|
|
|
|
fixed route-entry-pair candidate sets into either `0x004a5280` or `0x004a5900` depending on the
|
|
|
|
|
|
shared hundredths-scaled build-version query
|
|
|
|
|
|
`runtime_query_hundredths_scaled_build_version` `0x00482e00` over `0x006cec74`. The current
|
|
|
|
|
|
caller thresholds `>= 0x67`, `>= 0x68`, `>= 0x69`, and `>= 0x6a` now line up with executable
|
2026-04-06 20:34:16 -07:00
|
|
|
|
build values `1.03`, `1.04`, `1.05`, and `1.06`, and the version source itself can come from
|
|
|
|
|
|
the multiplayer companion path as well as the local executable, so this split is no longer
|
|
|
|
|
|
best-read as a time- or era-side cutover at all. It now reads more cleanly as a pre-`1.03`
|
|
|
|
|
|
versus `1.03+` route-metric compatibility dispatcher. That lower split is tighter now too:
|
2026-04-06 01:11:06 -07:00
|
|
|
|
`0x004a5280` is the weighted recursive branch with heuristic ordering, per-tracker best-cost
|
|
|
|
|
|
cache `0x006cfcac`, and prune threshold `0x006cfcb0`, while `0x004a5900` is the alternate
|
|
|
|
|
|
recursive neighbor-walk branch that stays within compatible component labels through
|
|
|
|
|
|
`0x004a62c0` and accumulates step and mismatch penalties over route-entry links
|
|
|
|
|
|
`+0x206/+0x20a/+0x20e`. Above both of them,
|
|
|
|
|
|
`aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
|
|
|
|
|
`0x004a6630` is now bounded as the real chooser: direct fixed-pair query when both entries pass
|
|
|
|
|
|
`0x0048b870`, otherwise endpoint-pair fallback across the two tracker groups.
|
|
|
|
|
|
The adjacent route-style rebuild side is tighter now too: the same build-version family reaches
|
|
|
|
|
|
`placed_structure_rebuild_route_style_candidate_scores_and_peer_links` `0x004101e0`, where build
|
|
|
|
|
|
`1.04+` keeps one `Recycling Plant` stem-specific compatibility branch alive and build `1.05+`
|
|
|
|
|
|
skips one older descriptor-side attenuation fallback. There is also one neighboring world-side
|
|
|
|
|
|
compatibility lane now bounded below the same family: `placed_structure_collect_connected_component_tile_bounds_with_version_gate`
|
|
|
|
|
|
`0x004160c0` skips one older recursive component-bounds walk through `0x00415f20` on build
|
|
|
|
|
|
`1.03+` when scenario field `[0x006cec78+0x46c38]` is already active. The wrapper above that
|
|
|
|
|
|
path is bounded now too: `placed_structure_map_tile_range_to_connected_component_records_with_optional_bounds_refresh`
|
|
|
|
|
|
`0x00416170` walks the caller rectangle, maps tile keys into the component table rooted at
|
|
|
|
|
|
`0x0062ba7c`, and only re-enters `0x004160c0` when the current owner still has no cached bounds
|
|
|
|
|
|
and the local world or scenario suppressors stay clear. The higher neighboring raster side is
|
|
|
|
|
|
tighter too. `world_grid_refresh_projected_rect_sample_band_and_flag_mask` `0x00418610` is the
|
|
|
|
|
|
shared projected-rectangle helper above `world_grid_refresh_flagged_region_float_extrema_and_mean`
|
|
|
|
|
|
`0x00415020`: it refreshes the temporary sample band at `0x0062b7d0`, publishes the surviving
|
|
|
|
|
|
rectangle through `0x0044d410`, and on the single-sample path re-enters
|
|
|
|
|
|
`world_grid_toggle_flagged_mask_bit0_for_nonsentinel_rect_samples` `0x004185a0` to flip mask bit
|
|
|
|
|
|
`0x01` for the corresponding non-sentinel cells. Current grounded callers for `0x00418610` are
|
|
|
|
|
|
the neighboring placed-structure local-runtime helper `0x00418be0` and the heavier placement
|
|
|
|
|
|
validator `0x004197e0`, so this adjacent family now reads more like projected placement or local
|
|
|
|
|
|
raster prep than an unowned generic world-grid scan. The higher owner split is tighter now too.
|
|
|
|
|
|
`placed_structure_build_local_runtime_record_from_candidate_stem_and_projected_scratch`
|
|
|
|
|
|
`0x00418be0` is the broader construction or rebuild lane: resolve one candidate id from a stem,
|
|
|
|
|
|
build projected scratch through `0x00416ec0`, then publish the projected rectangle and validate
|
|
|
|
|
|
its side windows through `placed_structure_publish_projected_runtime_rect_globals_and_validate_side_windows`
|
|
|
|
|
|
`0x00416620`. That publish pass is bounded now too: it stages `0x0062b308/0x0062b30c/0x0062b310`,
|
|
|
|
|
|
validates the rectangle against route-entry coverage through
|
|
|
|
|
|
`route_entry_collection_query_rect_window_passes_entry_type_gate` `0x00494240`, can branch into
|
|
|
|
|
|
the special projected-slot picker `placed_structure_try_select_projected_rect_profile_slot`
|
|
|
|
|
|
`0x00415570`, and refreshes the compact per-cell side tables through
|
|
|
|
|
|
`world_grid_refresh_projected_rect_surface_and_region_byte_tables` `0x00414e10` before the
|
|
|
|
|
|
finished scratch is copied into one queued runtime record and the new record re-enters
|
|
|
|
|
|
`0x00416170` when its rectangle is valid. The smaller sibling
|
|
|
|
|
|
`placed_structure_clone_template_local_runtime_record_for_subject_and_refresh_component_bounds`
|
|
|
|
|
|
`0x00418a60` now reads as the current-subject clone path above the same connected-component and
|
|
|
|
|
|
mask-refresh helpers, returning the cloned local runtime record later stored into `[site+0x24e]`
|
|
|
|
|
|
by `placed_structure_refresh_cloned_local_runtime_record_from_current_candidate_stem`
|
|
|
|
|
|
`0x0040e450`. The higher wrapper above that clone path is bounded now too:
|
|
|
|
|
|
`placed_structure_collection_refresh_local_runtime_records_and_position_scalars` `0x004133b0`
|
|
|
|
|
|
first drains the temporary site-id queue rooted at `0x0062ba64/0x0062ba6c/0x0062ba70` through
|
|
|
|
|
|
`placed_structure_local_runtime_site_id_queue_count` `0x00414480` and
|
|
|
|
|
|
`placed_structure_local_runtime_site_id_queue_pop_next` `0x00413f50`, rebuilding one cloned
|
|
|
|
|
|
local-runtime record per queued placed structure through `0x0040e450`, and only then sweeps all
|
|
|
|
|
|
live placed structures through the side refresh helper
|
|
|
|
|
|
`placed_structure_refresh_local_runtime_position_triplet_and_linked_anchor_followon` `0x0040ee10`.
|
|
|
|
|
|
The last lower side reads are tighter now too. `0x0040e450` first seeds the projected-slot cache
|
|
|
|
|
|
through `placed_structure_cache_projected_rect_profile_slot_id` `0x00414470` before it re-enters
|
|
|
|
|
|
`0x00418a60`, and the broader stem-based builder at `0x00418be0` now has one named optional side
|
|
|
|
|
|
renderer instead of an anonymous callsite:
|
|
|
|
|
|
`placed_structure_render_local_runtime_overlay_payload_from_projected_bounds` `0x00418040`.
|
|
|
|
|
|
The side refresh split is bounded now too. `0x0040ee10` publishes one local position or scalar
|
|
|
|
|
|
triplet through the shared setter `0x00530720`, then tails into
|
|
|
|
|
|
`placed_structure_refresh_linked_site_anchor_position_triplet_for_local_runtime` `0x0040e360`;
|
|
|
|
|
|
that smaller follow-on only runs on the current subtype-`1`, class-`3`, linked-site branch, and
|
|
|
|
|
|
recomputes one local-runtime triplet from the linked peer's route-entry anchor when that anchor
|
|
|
|
|
|
is still live. The heavier sibling above that side refresh is bounded now too:
|
|
|
|
|
|
`placed_structure_set_world_coords_and_refresh_local_runtime_side_state` `0x0040eba0` is the
|
|
|
|
|
|
world-coordinate mutation helper that recomputes `[site+0x388]` and `[site+0x38c]`, rewrites the
|
|
|
|
|
|
world-grid owner mapping through `0x0042c9f0` and `0x0042c9a0`, updates the subtype-`4`
|
|
|
|
|
|
proximity-bucket family when needed, rebuilds the same local position or scalar triplet, and
|
|
|
|
|
|
then tails into the linked-site anchor follow-on at `0x0040e360`.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The sibling policy-`1` side is tighter now too. The constructor lane no longer stops at “one
|
|
|
|
|
|
linked site id at `[site+0x2a8]`”: the subtype-`1` branch in
|
|
|
|
|
|
`placed_structure_construct_entry_from_candidate_and_world_args` `0x0040f6d0` now clearly
|
|
|
|
|
|
allocates that linked record through
|
|
|
|
|
|
`placed_structure_collection_allocate_and_construct_linked_site_record` `0x00481390`, whose lower
|
|
|
|
|
|
constructor is `placed_structure_construct_linked_site_record_from_anchor_and_coords`
|
|
|
|
|
|
`0x00480210`. That lower constructor seeds the linked record's own id and anchor-site id, clears
|
|
|
|
|
|
the local route-anchor and display-name fields, projects the anchor footprint into world space,
|
|
|
|
|
|
and then either binds an already-covered route entry through `0x00417b40` or falls through into
|
|
|
|
|
|
the neighboring policy-`1` route-entry synthesis family around `0x00493cf0` before rebinding the
|
|
|
|
|
|
chosen route entry through `0x0048abc0`.
|
|
|
|
|
|
The cleanup side is tighter now too. Linked-site removal now has a bounded owner at
|
|
|
|
|
|
`placed_structure_collection_remove_linked_site_record` `0x004813d0`: it resolves the linked
|
|
|
|
|
|
record through `0x006cec20`, runs the per-record teardown
|
|
|
|
|
|
`placed_structure_teardown_linked_site_runtime_state_before_removal` `0x00480590`, removes the
|
|
|
|
|
|
live entry from the collection, and only then re-enters the still-bounded company-wide follow-on
|
|
|
|
|
|
at `0x00429c10` when the removed record passed the narrower transit-like latch. That per-record
|
|
|
|
|
|
teardown is no longer just “clear some scratch fields.” It now clearly clears the route-style
|
|
|
|
|
|
scratch lane, clears the five proximity buckets at `[site+0x590..0x5b8]`, detaches or invalidates
|
|
|
|
|
|
the current route-entry anchor, frees the three per-site byte arrays at `[site+0x24..0x2c]`,
|
|
|
|
|
|
clears this site's indexed byte from the corresponding arrays of later placed-structure records,
|
|
|
|
|
|
and then re-enters `0x00436040` with the current site id.
|
|
|
|
|
|
That company-side follow-on is no longer just one opaque callback either. It is now bounded as
|
|
|
|
|
|
`company_collection_refresh_active_company_linked_transit_site_peer_caches` `0x00429c10`, which
|
|
|
|
|
|
walks the active company roster and re-enters
|
|
|
|
|
|
`company_rebuild_linked_transit_site_peer_cache` `0x004093d0` on each company. That per-company
|
|
|
|
|
|
fast pass stamps a refresh tick at `[company+0x0d3e]`, clears and repopulates the
|
|
|
|
|
|
placed-structure-side cache cells addressed through `[site+0x5bd][company_id]`, marks the
|
|
|
|
|
|
eligible linked transit sites for that company, allocates one `0x0d`-stride peer table for each
|
2026-04-06 01:11:06 -07:00
|
|
|
|
eligible site, and fills those peer rows from
|
|
|
|
|
|
`aux_route_entry_tracker_query_best_route_entry_pair_metric_with_endpoint_fallbacks`
|
|
|
|
|
|
`0x004a6630`. That helper no longer reads as one anonymous route sweep: it either uses the fixed
|
|
|
|
|
|
pair directly or falls back across tracker endpoint combinations before returning the winning
|
|
|
|
|
|
route-entry id, one route-step count, and one companion mismatch count. The fast cache now reads
|
|
|
|
|
|
more cleanly too: peer-record dword `+0x05` stores that step count, while float `+0x09` stores
|
|
|
|
|
|
the normalized continuity share derived from `(steps - mismatches) / max(steps, 1)`, not a raw
|
|
|
|
|
|
delta-per-step ratio. The
|
2026-04-05 20:11:47 -07:00
|
|
|
|
adjacent timed wrapper `company_service_linked_transit_site_caches` `0x00409720` now shows the
|
|
|
|
|
|
cadence too: `0x004093d0` is the shorter-interval refresh, while the older heavier sibling at
|
|
|
|
|
|
`0x00407bd0` was only revisited on the longer interval.
|
|
|
|
|
|
That heavier sibling is now bounded too:
|
|
|
|
|
|
`company_rebuild_linked_transit_autoroute_site_score_cache` `0x00407bd0` no longer looks like a
|
2026-04-06 20:34:16 -07:00
|
|
|
|
generic tail refresh. It reuses the fast peer tables, rebuilds candidate-local amount bands plus
|
|
|
|
|
|
normalized issue-opinion scales, and then folds the peer-side route metrics back into three
|
|
|
|
|
|
per-site cache floats with a cleaner split:
|
|
|
|
|
|
`+0x12` is the raw surviving site-score total,
|
|
|
|
|
|
`+0x0e` is the continuity-and-step-weighted companion total,
|
|
|
|
|
|
and `+0x16` is the promoted final site-ranking lane chosen from the strongest grouped candidate
|
|
|
|
|
|
bands.
|
|
|
|
|
|
That also closes most of the cross-version impact question:
|
|
|
|
|
|
the pre-`1.03` versus `1.03+` tracker metric split now looks like it mainly perturbs the weighted
|
|
|
|
|
|
`+0x0e` lane and the promoted `+0x16` lane, not the raw `+0x12` total.
|
|
|
|
|
|
That final lane then feeds the neighboring selectors
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_select_best_owned_linked_transit_site_by_autoroute_score` `0x00408280` and
|
|
|
|
|
|
`company_build_linked_transit_autoroute_entry` `0x00408380`. That makes the company-side follow-on
|
|
|
|
|
|
read more like a linked-transit autoroute cache family than a generic company maintenance pass.
|
|
|
|
|
|
The neighboring reachability gate is tighter now too:
|
|
|
|
|
|
`company_query_cached_linked_transit_route_anchor_entry_id` `0x00401860` caches one company-side
|
|
|
|
|
|
route-entry anchor, and
|
|
|
|
|
|
`placed_structure_is_linked_transit_site_reachable_from_company_route_anchor` `0x004801a0`
|
|
|
|
|
|
uses that anchor to decide whether a foreign linked transit site can still participate in the
|
|
|
|
|
|
current company's fast peer cache.
|
|
|
|
|
|
The first direct train-side consumer above that cache family is bounded now too:
|
|
|
|
|
|
`train_try_append_linked_transit_autoroute_entry` `0x00409770`. After servicing the owning
|
|
|
|
|
|
company's caches, it asks `0x00408380` for one staged `0x33`-byte route entry using the train's
|
|
|
|
|
|
current anchor site, then either appends that entry through `0x004b3160` and refreshes the new
|
|
|
|
|
|
trailing selection through `0x004b2f00`, or rotates one existing slot in place when the local
|
|
|
|
|
|
two-entry cap has already been reached. So this edge now reaches an actual train-side autoroute
|
|
|
|
|
|
append lane rather than stopping at anonymous company-side cache cells.
|
|
|
|
|
|
The train-side follow-on above that seed path is bounded now too. The owning company can count
|
|
|
|
|
|
its live roster through `company_count_owned_trains` `0x004264c0`, measure one aggregate linked
|
|
|
|
|
|
transit site pressure through `company_compute_owned_linked_transit_site_score_total`
|
|
|
|
|
|
`0x00408f70`, and then rebalance that roster through
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`company_balance_linked_transit_train_roster` `0x00409950`.
|
|
|
|
|
|
The aggregate helper no longer reads as a raw sum either:
|
|
|
|
|
|
it starts from the site-cache `+0x12` totals, converts that into one tentative roster target
|
|
|
|
|
|
through year and site-count ladders, and on build `1.03+` adds one special distance-side scaling
|
|
|
|
|
|
branch when exactly two eligible linked transit sites survive.
|
|
|
|
|
|
Because it consumes `+0x12` rather than `+0x0e` or `+0x16`, current evidence now says the tracker
|
|
|
|
|
|
compatibility split is more important for seeded route choice and ranked site choice than for the
|
|
|
|
|
|
final company train-count target itself.
|
|
|
|
|
|
The balancer then applies two age bands to company-owned trains:
|
|
|
|
|
|
very old trains are removed when the roster already exceeds target or upgraded when it still
|
|
|
|
|
|
needs capacity, while the mid-age band can trigger one narrower upgrade pass.
|
|
|
|
|
|
After that it fills any remaining deficit by either packaging multiplayer opcode `0x75` or
|
|
|
|
|
|
locally re-entering
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_try_add_linked_transit_train_and_publish_news` `0x00409830`. The two visible news
|
|
|
|
|
|
helpers under it are bounded too: `0x00409830` emits RT3.lng `2896` for a newly added train,
|
|
|
|
|
|
while `company_publish_train_upgrade_news` `0x00409300` emits RT3.lng `2897` for the upgrade
|
|
|
|
|
|
branch.
|
|
|
|
|
|
The subtype-`4` sibling side is bounded now too. The nearby-site bucket family now has:
|
|
|
|
|
|
`placed_structure_append_nearby_transit_site_distance_bucket_entry` `0x0047fdb0`,
|
|
|
|
|
|
`placed_structure_remove_site_id_from_proximity_bucket_lists` `0x0047dd10`, and
|
|
|
|
|
|
`placed_structure_clear_proximity_bucket_lists` `0x0047dcd0`, plus the two collection sweeps
|
|
|
|
|
|
`placed_structure_collection_append_site_into_all_proximity_bucket_lists` `0x00481480` and
|
|
|
|
|
|
`placed_structure_collection_remove_site_id_from_all_proximity_bucket_lists` `0x004814c0`.
|
|
|
|
|
|
Current evidence says those five buckets store `(peer site id, distance)` pairs for nearby
|
|
|
|
|
|
station-or-transit peers, grouped through the five-way classifier at `0x0040d350`. The older
|
|
|
|
|
|
stream-backed side is bounded too: `placed_structure_collection_load_dynamic_side_buffers_from_stream`
|
|
|
|
|
|
`0x00481430` walks the live placed-structure collection and re-enters
|
|
|
|
|
|
`placed_structure_load_dynamic_side_buffers_from_stream` `0x0047d8e0`, which repopulates the
|
|
|
|
|
|
route-entry list, the three per-site byte arrays, the five proximity buckets, and the trailing
|
|
|
|
|
|
scratch band from the caller-supplied persistence stream.
|
|
|
|
|
|
The linked-site route-entry list itself is tighter now too. The refresh or teardown branch at
|
|
|
|
|
|
`0x0040e102` re-enters `placed_structure_remove_route_entry_key_and_compact` `0x0047d810`, which
|
|
|
|
|
|
removes one matching `u16` key from the six-byte list rooted at `[site+0x462]/[site+0x466]`,
|
|
|
|
|
|
compacts the surviving entries into a replacement buffer, and decrements the stored route-entry
|
|
|
|
|
|
count.
|
|
|
|
|
|
The broader company-side owner above these pieces is tighter now too. The periodic service pass
|
|
|
|
|
|
`company_service_periodic_city_connection_finance_and_linked_transit_lanes` `0x004019e0` now
|
|
|
|
|
|
reads as the current outer owner for this branch: it sequences the city-connection announcement
|
|
|
|
|
|
lanes, the linked-transit train-roster balancer, the acquisition-side sibling
|
|
|
|
|
|
`company_try_buy_unowned_industry_near_city_and_publish_news` `0x004014b0`, the annual finance-policy helper
|
|
|
|
|
|
`company_evaluate_annual_finance_policy_and_publish_news` `0x00401c50`, and the shorter-versus-
|
|
|
|
|
|
longer linked-transit cache refresh tail through `0x004093d0` and `0x00407bd0`. That outer pass
|
|
|
|
|
|
also has one tighter temporary-state role now: it clears company bytes `0x0d17/0x0d18/0x0d56`,
|
|
|
|
|
|
mirrors `0x0d17` into scenario field `0x006cec78+0x4c74` only while the earlier route-building
|
|
|
|
|
|
side runs, and restores the original scenario value on exit. Current evidence now ties `0x0d17`
|
|
|
|
|
|
to the shared preferred-locomotive chooser `0x004078a0`: the override is only armed when that
|
|
|
|
|
|
helper picks one locomotive whose engine-type dword is `2`. That engine-type lane now best reads
|
|
|
|
|
|
as electric rather than an unnamed class slot, because the linked approval helper around
|
|
|
|
|
|
`0x0041d550` now reads as a real locomotive-era and engine-type policy gate, dispatching the same
|
|
|
|
|
|
`0/1/2` field across three scenario-opinion lanes while the local player-facing engine-type
|
|
|
|
|
|
family is explicitly `Steam / Diesel / Electric`. That same gate also now ties the later scenario
|
|
|
|
|
|
bytes `0x4c97..0x4c99` back to the editor-side `Locomotives` page rather than treating them as
|
|
|
|
|
|
anonymous availability flags: they are the live `All Steam Locos Avail.`, `All Diesel Locos
|
|
|
|
|
|
Avail.`, and `All Electric Locos Avail.` policy bytes behind `0x004cd680` / `0x004cf0d0`. The
|
|
|
|
|
|
current logic is also tighter than a plain override tier: a positive engine-family opinion result
|
|
|
|
|
|
can carry the gate by itself only while none of those three editor bytes is enabled; once any of
|
|
|
|
|
|
them is nonzero, `0x0041d550` instead requires one matching intersection between the record-local
|
|
|
|
|
|
family bytes and the enabled editor family bytes. The `WhaleL` carveout is narrower now too:
|
|
|
|
|
|
current data-file correlation ties that stem to the `Orca NX462` / `WhaleL_NE` locomotive family,
|
|
|
|
|
|
and it explicitly zeros that positive-opinion result before the later family-availability checks,
|
|
|
|
|
|
so it loses the shortcut rather than gaining a special approval path. The same
|
|
|
|
|
|
chooser now also has a bounded fallback below it, `locomotive_collection_select_best_era_matched_non_electric_fallback_id`
|
|
|
|
|
|
`0x00461cd0`, which explicitly skips engine-type `2` and chooses the lowest-penalty remaining
|
|
|
|
|
|
locomotive by era mismatch and approval gates. The route-search side is tighter too: this
|
|
|
|
|
|
electric-only override now clearly feeds the first path-sweep branch in `0x0049bd40`, forcing the
|
|
|
|
|
|
larger `1.8` initial quality multiplier instead of `1.4`, which is the same branch otherwise
|
|
|
|
|
|
chosen by explicit route-policy byte `4`. So the later annual finance helper is reading
|
|
|
|
|
|
same-cycle side-channel state from the earlier city-connection and linked-transit branches, not
|
|
|
|
|
|
unrelated long-lived finance fields. The inner
|
|
|
|
|
|
finance helper is not debt-only either. Current grounded outcomes include bankruptcy news
|
|
|
|
|
|
`2881`, the debt restructure family `2882..2886`, a later share-repurchase headline `2887`, and
|
|
|
|
|
|
the dividend-side adjustment branch. The main commit verbs under that helper are now grounded too:
|
|
|
|
|
|
`company_repay_bond_slot_and_compact_debt_table` `0x00423d70`,
|
|
|
|
|
|
`company_issue_bond_and_record_terms` `0x004275c0`,
|
|
|
|
|
|
`company_repurchase_public_shares_and_reduce_capital` `0x004273c0`, and
|
|
|
|
|
|
`company_issue_public_shares_and_raise_capital` `0x00427450`. The threshold side is tighter now
|
|
|
|
|
|
too. The earliest creditor-pressure lane requires scenario mode `0x0c`, the bankruptcy toggle
|
|
|
|
|
|
`[0x006cec78+0x4a8f]` to be clear, at least `13` years since `[company+0x163]`, and at least
|
2026-04-06 20:34:16 -07:00
|
|
|
|
`4` years since founding year `[company+0x157]`; it then scans the last three years of the
|
|
|
|
|
|
derived net-profits and revenue lanes `0x2b` and `0x2c`, chooses one negative pressure ladder
|
|
|
|
|
|
`-600000 / -1100000 / -1600000 /
|
|
|
|
|
|
-2000000` from the current slot-`0x2c` bands around `120000 / 230000 / 340000`, requires the
|
|
|
|
|
|
broader support-adjusted share-price or public-support scalar at least `15` or `20` depending on
|
|
|
|
|
|
whether all three sampled years failed, checks the current fuel-cost lane in slot `0x09` against
|
|
|
|
|
|
`0.08` times that ladder, and requires the
|
|
|
|
|
|
three-year slot-`0x2b` total to clear one final `-60000` gate before it falls into the
|
|
|
|
|
|
bankruptcy commit and RT3.lng `2881` headline.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The middle debt-capital layer is split more clearly now too. With `[+0x4a8b]` clear, one annual
|
|
|
|
|
|
bond lane first simulates full repayment through `company_repay_bond_slot_and_compact_debt_table`
|
|
|
|
|
|
and then uses the post-repayment cash window with fixed `-250000` and `-30000` thresholds plus
|
|
|
|
|
|
the broader linked-transit train-service latch `[company+0x0d56]` to decide whether to append one
|
|
|
|
|
|
or more `500000` principal, `30`-year bonds. The repurchase lane is separate again: when the
|
|
|
|
|
|
city-connection announcement-side latch `[company+0x0d18]` is set, growth setting `2` does not
|
|
|
|
|
|
suppress it, and `[+0x4a87]` is clear, it starts from one `1000`-share batch, can replace its
|
|
|
|
|
|
default `1.0` factor with one linked-chairman personality scalar, scales that by `1.6` when
|
|
|
|
|
|
growth setting `1` is active, and then runs one `800000` stock-value gate plus one
|
2026-04-06 20:34:16 -07:00
|
|
|
|
support-adjusted-share-price-times-factor-times-`1000`-times-`1.2` affordability gate before the
|
|
|
|
|
|
repeated `1000`-share buyback commits behind RT3.lng `2887`. The ordering above this helper is
|
|
|
|
|
|
tighter now too:
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_service_periodic_city_connection_finance_and_linked_transit_lanes` clears those latches
|
|
|
|
|
|
first, runs the city-connection and linked-transit branches, and only then enters the annual
|
|
|
|
|
|
finance helper, so these look like same-cycle reaction gates rather than long-lived balance-sheet
|
|
|
|
|
|
flags.
|
|
|
|
|
|
After the earlier debt or bankruptcy outcomes stay inactive, the later stock-capital lane also
|
2026-04-06 20:34:16 -07:00
|
|
|
|
has a tighter bounded shape now too: it only opens on build `1.03+`, only after the earlier
|
|
|
|
|
|
bankruptcy, bond, and repurchase outcomes stay inactive, and with the bond and stock toggles
|
|
|
|
|
|
`[+0x4a8b]` and `[+0x4a87]` clear, at least two bond slots live, and at least one year since
|
|
|
|
|
|
founding. It derives one issue batch from outstanding shares rounded down to `1000`-share lots
|
|
|
|
|
|
with floor `2000`, trims that batch until the broader support-adjusted share-price scalar times
|
|
|
|
|
|
batch no longer exceeds the `55000` gate, requires that scalar at least `22`, resolves the
|
|
|
|
|
|
highest-coupon live bond slot, and then uses current cash from `0x2329/0x0d` as a gate against
|
|
|
|
|
|
that slot's principal plus a small fixed buffer before it compares the chosen bond-rate lane
|
|
|
|
|
|
against a normalized scalar ratio built from the same support-adjusted share-price lane and
|
|
|
|
|
|
current `Book Value Per Share` from `0x2329/0x1d` through the piecewise approval ladder
|
|
|
|
|
|
`0.07/1.3 -> 0.14/0.35`. On success it issues two
|
|
|
|
|
|
same-sized tranches through repeated `company_issue_public_shares_and_raise_capital` calls and
|
|
|
|
|
|
publishes a separate equity-offering news family rooted at localized id `4053`, not the earlier
|
|
|
|
|
|
debt or buyback headline family.
|
2026-04-05 20:11:47 -07:00
|
|
|
|
The dividend side is bounded too: it requires the dividend toggle `[0x006cec78+0x4a93]` to be
|
|
|
|
|
|
clear, mode `0x0c`, at least `1` year since `[company+0x0d2d]`, and at least `2` years since
|
2026-04-06 20:34:16 -07:00
|
|
|
|
founding, then averages the last three years of the net-profits lane `0x2b`, folds in the
|
|
|
|
|
|
unassigned-share pool and the current `0x0d` band, applies the map-editor building-growth
|
|
|
|
|
|
setting `[0x006cec78+0x4c7c]`, treats growth setting `1` as a `0.66` scale-down and setting `2`
|
|
|
|
|
|
as a zeroing pass on the current dividend, quantizes surviving adjustments in tenths, and
|
|
|
|
|
|
finally clamps against
|
2026-04-05 20:11:47 -07:00
|
|
|
|
`company_compute_board_approved_dividend_rate_ceiling` `0x00426260`.
|
|
|
|
|
|
The linked-transit route-seeding side has one tighter sibling now too:
|
|
|
|
|
|
`company_reset_linked_transit_caches_and_reseed_empty_train_routes` `0x00401940`. It clears the
|
|
|
|
|
|
two company-side linked-transit cache timestamps, forces one immediate cache rebuild through
|
|
|
|
|
|
`0x00409720`, strips route lists from company-owned trains in modes `0x0a/0x13`, and then
|
|
|
|
|
|
re-enters `train_try_append_linked_transit_autoroute_entry` `0x00409770` only when a train's
|
|
|
|
|
|
route list has become empty.
|
|
|
|
|
|
On the wider chooser question, the current evidence is also tighter than before: every recovered
|
|
|
|
|
|
external owner of `0x00402cb0` is still in the city-connection family, so the two later direct
|
|
|
|
|
|
placement lanes currently read as city-connection fallback behavior rather than a broadly shared
|
|
|
|
|
|
world placement service.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-05 20:11:47 -07:00
|
|
|
|
before either direct-placement commit is allowed to fire. Its strongest current subtype branch is
|
|
|
|
|
|
a station-attachment or upgrade-style lane keyed by `[candidate+0x32] == 1`, which now has the
|
|
|
|
|
|
concrete failure family `2901..2906`: blocked upgrade footprint, ground not flat enough, not
|
|
|
|
|
|
your track, insufficient track-laying capacity, cannot connect to existing track, and ground too
|
|
|
|
|
|
steep. That tightens the chooser materially without yet proving that the validator is
|
|
|
|
|
|
station-only for every caller.
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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
|
2026-04-06 01:11:06 -07:00
|
|
|
|
live-company presentation path through the company-list window. The later interior order of that
|
|
|
|
|
|
same `319` lane is tighter now too: after the route-entry collection refresh on `0x006cfca8` it
|
|
|
|
|
|
refreshes the auxiliary route-entry tracker collection `0x006cfcb4`, then runs
|
|
|
|
|
|
`placed_structure_collection_refresh_local_runtime_records_and_position_scalars` `0x004133b0`,
|
|
|
|
|
|
then a flagged world-grid cleanup sweep through `0x00448af0/0x00533fe0`, and only after that the
|
|
|
|
|
|
later route-entry post-pass at `0x00491c20`. The same later lane now also reaches a separate
|
|
|
|
|
|
event-side runtime branch: the live event collection at `0x0062be18` re-enters
|
|
|
|
|
|
`scenario_event_collection_refresh_runtime_records_from_packed_state` `0x00433130`, which in
|
|
|
|
|
|
turn materializes each live event record through
|
|
|
|
|
|
`scenario_event_refresh_runtime_record_from_packed_state` `0x0042db20`. Current shell-side xrefs
|
|
|
|
|
|
now tighten that event branch too: the first rebuilt linked row family under `0x0042db20` aligns
|
|
|
|
|
|
with the standalone condition list later queried by `EventConditions.win`, while the second
|
|
|
|
|
|
rebuilt family aligns with the four grouped effect lists later deep-copied through
|
|
|
|
|
|
`scenario_event_clone_runtime_record_deep_copy` `0x0042e050` during event duplication and effect
|
|
|
|
|
|
staging. The condition side is tighter now too: the tiny helper cluster
|
|
|
|
|
|
`0x0042df30/0x0042df70/0x0042dfb0/0x0042dff0` is no longer just "some adjacent list scans".
|
|
|
|
|
|
Current evidence bounds it as four predicates over the standalone `0x1e`-row condition list,
|
|
|
|
|
|
testing class bits `0x01`, `0x02`, `0x04`, or any of those bits in the static table
|
|
|
|
|
|
`0x005f3e04 + id*0x81`, with special fallback checks through event fields `[event+0x7f9]`,
|
|
|
|
|
|
`[event+0x7fa]`, and `[event+0x7f0] == 0x63`. The shell side is tighter too: vtable slot
|
|
|
|
|
|
`0x005d0cd8` now binds `shell_event_conditions_window_handle_message` `0x004d59e0`, and vtable
|
|
|
|
|
|
slot `0x005d0cf8` binds `shell_event_effects_window_handle_message` `0x004d7060`. The effects
|
|
|
|
|
|
side is tighter too: the lower helper trio is no longer anonymous. `0x004d5d00` now reads as the
|
|
|
|
|
|
effect-type selector refresh under control family `0x4fb2`, `0x004d5f50` reads as the selected
|
|
|
|
|
|
effect parameter-row repaint, `0x004d6090` is the heavier staged-effect editor refresh over the
|
|
|
|
|
|
`0x4fc7/0x4fce/0x4ff6/0x4ff9/0x4ffc/0x5041/0x5044/0x5046/0x5047` bands, and `0x004d67f0`
|
|
|
|
|
|
commits the current editor state back into the staged effect row at `[this+0x78]`. The verb side
|
|
|
|
|
|
is tighter now too: `shell_open_event_conditions_modal_and_return_result` `0x004d9dc0` and
|
|
|
|
|
|
`shell_open_event_effects_modal_and_return_result` `0x004d9e40` are the shared modal openers
|
|
|
|
|
|
above the two editor windows; `0x004da640`, `0x004da700`, and `0x004d9ed0` now read as the add,
|
|
|
|
|
|
edit, and remove verbs for standalone condition rows; `0x004da7c0`, `0x004da860`, and
|
|
|
|
|
|
`0x004da920` are the matching add, edit, and remove verbs for grouped effect rows; and
|
|
|
|
|
|
`0x004d8120` is now the heavier condition-row list panel refresh those condition-side verbs
|
|
|
|
|
|
re-enter after mutation. The conditions-side refresh split is tighter too: `0x0042d700`
|
|
|
|
|
|
aggregates standalone condition-list class or modifier flags, `0x0042d740` aggregates grouped
|
|
|
|
|
|
effect-row type flags for one selected grouped list, `0x004d9970` owns the condition-class
|
|
|
|
|
|
summary and grouped-row status bands, `0x004d77b0` owns the grouped summary-band affordance gate
|
|
|
|
|
|
for `0x4fed..0x4ff0` when selector `0x5000` lands on `0x5002`, `0x004d9d10` owns the smaller
|
|
|
|
|
|
grouped-effect territory-target affordance on control `0x500b`, `0x004d9f50` owns the selected-event mode
|
|
|
|
|
|
strip and summary text panels, and `0x004d9390` is the mode-dependent detail-row switch beneath
|
|
|
|
|
|
that strip. `0x004da0f0` is tighter too: selector `0x5001` now has the strongest current
|
|
|
|
|
|
RT3.lng fit as the condition-side `Test against...` mode above `0x004d9970`, while selector
|
|
|
|
|
|
`0x5002` has the strongest current fit as the grouped-effect-side `Apply effects...` mode.
|
|
|
|
|
|
That `0x5002` branch now clearly builds control `0x5014` from RT3.lng `1160..1164` as `to the
|
|
|
|
|
|
company/player/player (i.e. chairman)/territory for which the condition is TRUE` before
|
|
|
|
|
|
enabling the adjacent `0x5005`, `0x500a`, and `0x5014..0x501c` family. The strongest current
|
|
|
|
|
|
RT3.lng fit for the remaining visible target-scope strip is now `0x5015 = to the whole game`,
|
|
|
|
|
|
`0x5016..0x5018 = to all/human/AI companies`, `0x5019 + 0x500b = to territories`, and
|
|
|
|
|
|
`0x501a..0x501c = to all/human/AI players`; the grouped effect-row type mask matches that split
|
|
|
|
|
|
directly through bits `0x08`, `0x01`, `0x04`, and `0x02`. The
|
|
|
|
|
|
selected-event strip is tighter now too: `0x004db120` is the broader selected-event repaint and
|
|
|
|
|
|
navigation refresh above those smaller helpers, `0x004db520` and `0x004db5e0` are the previous
|
|
|
|
|
|
and next selected-event stepping verbs, `0x004db8b0` is the add-or-clone event modal helper,
|
|
|
|
|
|
`0x004dba90` is the rename verb, `0x004d9360` is the delete verb, `0x004db6a0` is the live
|
|
|
|
|
|
selected-event id setter behind control `0x4e84`, and `0x004db6f0` is the callback-binding plus
|
|
|
|
|
|
pending-selection bootstrap path that seeds the strip during window bring-up. The larger
|
|
|
|
|
|
dispatcher at `0x004dbb80` now makes the strip explicit: `0x4e85..0x4e8a` are previous, next,
|
|
|
|
|
|
add blank, clone selected, rename, and delete event, while the later grouped band commits current
|
|
|
|
|
|
summary state through `0x004d8d50` before changing grouped selector `[this+0x9c]` via `0x004dbf93`.
|
|
|
|
|
|
The selection bootstrap side is tighter too: `0x004daf40` is now the placeholder reset helper for
|
|
|
|
|
|
the selected-event summary controls `0x4eaf`, `0x4eac`, `0x4ed9`, `0x4edb`, and `0x4fdf..0x4fe2`.
|
|
|
|
|
|
The grouped target-scope side is tighter too: `0x004d8ea0` now reads as the commit helper for current
|
|
|
|
|
|
selected-event text panels before selection or grouped-action changes, `0x004d8d50` now records
|
|
|
|
|
|
the hidden selector family `0x5006..0x500e -> 0..8`, and `0x004dab60` projects that ordinal
|
|
|
|
|
|
one-to-one onto the visible grouped-effect target-scope display strip `0x5014..0x501c`. That
|
|
|
|
|
|
split is firmer now: `0x5006..0x500e` are the canonical hidden selectors that get stored into
|
|
|
|
|
|
`[event + group + 0x7fb]`, while `0x5014..0x501c` are the visible mirror rows republished from
|
|
|
|
|
|
that same ordinal rather than a second independently named selector family. The grouped row and
|
|
|
|
|
|
stored-summary refresh side is tighter too: `0x004d88f0` is now the selected grouped-effect
|
|
|
|
|
|
row-list renderer for `0x4ed5`, formatting the grouped `0x28`-byte rows through RT3.lng
|
|
|
|
|
|
`1154..1159`, and `0x004da9a0` is the current grouped-summary-state republisher that reloads
|
|
|
|
|
|
`0x500a`, `0x500b`, and visible action selection `0x5014..0x501c` from `[event + group + ...]`
|
|
|
|
|
|
before tailing back into `0x004da0f0`,
|
|
|
|
|
|
`0x004dbfca` as the grouped target-scope mode selector that persists the chosen control id into
|
|
|
|
|
|
`0x00622074`, with `0x5001/0x5002` now strongest-fit as `Test against...` and
|
|
|
|
|
|
`Apply effects...`, `0x004dbeeb` as the pending shared summary-text triplet publish helper for
|
|
|
|
|
|
`0x4eac/0x4ed9/0x4edb`,
|
|
|
|
|
|
`0x004d91e0` as the selected-event summary-header and grouped-mode commit helper above
|
|
|
|
|
|
`0x004d8d50`, and `0x004dbe7a` as the narrower `0x4ec6/0x4ec7` choice-event single-player-only
|
|
|
|
|
|
warning modal branch rooted at RT3.lng `3887`. The remaining gaps on
|
|
|
|
|
|
this lane are narrower again because the grouped-band `0x4dc09c` table now closes one earlier
|
|
|
|
|
|
overclaim: controls `0x5001/0x5002` are the only `0x4fed..0x501c` entries that route into
|
|
|
|
|
|
`0x004dbfca` on the `0xcf` side, while visible rows `0x5014..0x501c` only route to the smaller
|
|
|
|
|
|
`0x004d9d10` affordance path and the rest of `0x4ff1..0x5013` are default no-ops. The open
|
|
|
|
|
|
question is therefore no longer whether those visible target-scope rows are direct selector verbs;
|
|
|
|
|
|
current evidence says they are not.
|
|
|
|
|
|
The local slot records are rooted at
|
|
|
|
|
|
`[world+0x69d8]`,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`[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`,
|
2026-04-06 20:34:16 -07:00
|
|
|
|
control `0x9488` now grounds the debt or credit or rate summary block through
|
|
|
|
|
|
`shell_company_detail_render_debt_credit_and_rate_summary_panel`, control `0x948a` now grounds the
|
|
|
|
|
|
share-value and dividend-payout block through
|
|
|
|
|
|
`shell_company_detail_render_share_value_and_dividend_summary_panel`, while the broader six-row
|
|
|
|
|
|
per-share stock-data family is now bounded under `shell_format_company_stock_data_panel`, and the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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`.
|
2026-04-06 20:34:16 -07:00
|
|
|
|
The remaining company-side uncertainty is therefore narrower than before: the broader support and
|
|
|
|
|
|
valuation side is now tighter too because
|
|
|
|
|
|
`company_compute_cached_recent_per_share_performance_subscore`,
|
|
|
|
|
|
`company_compute_five_year_weighted_shareholder_return`, and
|
|
|
|
|
|
`company_compute_public_support_adjusted_share_price_scalar` bound the recent per-share
|
|
|
|
|
|
performance and investor-support/share-price blend beneath those vote resolvers,
|
|
|
|
|
|
`scenario_state_compute_issue_opinion_multiplier` now bounds the
|
2026-04-05 15:36:45 -07:00
|
|
|
|
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.
|
2026-04-04 21:21:59 -07:00
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
the bounded company-side value scalar, and
|
|
|
|
|
|
`company_compute_prime_rate_from_issue39_scenario_baseline` at `0x00424580` now bounds the
|
|
|
|
|
|
shared prime-rate-side helper that this lane reuses beside the raw issue-`0x39` total,
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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
|
2026-04-06 20:34:16 -07:00
|
|
|
|
now tighten slot `0x2b` into the rolling net-profits lane reused by annual finance checks and a
|
|
|
|
|
|
per-share/history formatter, while the report-history descriptor table now aligns raw slot `0x09`
|
|
|
|
|
|
with the Income Statement fuel-cost lane surfaced by tooltip `1309`. The wider result now reads
|
|
|
|
|
|
more like recent net profits minus recent fuel burden than a governance-pressure term. That now
|
|
|
|
|
|
also sharpens the annual finance lane at `0x00401c50`: the first bankruptcy branch reads as
|
|
|
|
|
|
sustained cash-and-debt stress over recent profits and fuel burden, the later fallback branch as
|
|
|
|
|
|
a deeper `-300000` cash / three bad years cleanup trigger, and the later stock-issue branch reads
|
|
|
|
|
|
as a price-to-book-versus-coupon approval ladder rather than a generic support vote. The tail is
|
|
|
|
|
|
cleaner now too: it compares total retired versus newly issued principal to choose the `2882..2886`
|
|
|
|
|
|
debt headline family, then publishes `2887` separately from the accumulated repurchased-share
|
|
|
|
|
|
count. The sibling news owner above the same
|
|
|
|
|
|
city-pair route family is bounded now too:
|
2026-04-05 15:36:45 -07:00
|
|
|
|
`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.
|
2026-04-02 23:11:15 -07:00
|
|
|
|
|
|
|
|
|
|
## Next Mapping Passes
|
|
|
|
|
|
|
2026-04-05 15:36:45 -07:00
|
|
|
|
- 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.
|