Game logic
Purpose
Core game simulation, configuration, and main-loop driving. Occupies the bulk
of the executable (~0x0040xxxx–0x0045xxxx). The lotr2_winmain call tree is
named from startup through idle-tick dispatch and the first sim_tick callees.
Key entry points
| Symbol |
Retail addr |
GoG addr |
Role |
lotr2_winmain |
0x41680b |
0x40e9ab |
Application entry; owns Win32 message loop |
lotr2_asset_read_file |
0x4ca01c |
0x4af8d9 |
Read l2_eng string table at startup |
lotr2_game_load_lords2_inf |
0x4c8559 |
0x4ae1b8 |
Read lords2.inf install config |
lotr2_game_load_config |
0x416aea |
0x40ecc4 |
Read l2.ini / _sierra.ini; set display/audio globals |
lotr2_game_check_easter_egg |
0x4c979f |
0x4aef7e |
Pumpkin WAV easter egg; sets lotr2_net_join_flags |
lotr2_game_play_intro_or_enable_tick |
0x4cec91 |
0x4b3571 |
Play intro Smacker or enable tick gate |
lotr2_game_init_phase1 |
0x4adbc9 |
0x4975d3 |
Pre-game init; loads base .256 assets |
lotr2_game_init_phase2 |
0x4adb00 |
0x497500 |
New-game setup; loads PL8 assets |
lotr2_game_reset_ui_slots |
0x4890df |
0x476a5d |
Reset UI slot table before main loop |
lotr2_game_idle_tick |
0x41671b |
0x40e8bb |
Idle handler when queue empty; DD flip + game tick |
lotr2_game_frame_advance |
0x4166f0 |
0x40e890 |
Advance frame counter; set tick-pending flag |
lotr2_game_tick_sp |
0x4addaa |
0x4978ad |
Single-player tick prep |
lotr2_game_tick_mp |
0x4ade2b |
0x49792e |
Multiplayer tick prep |
lotr2_game_sim_tick |
0x4adf31 |
0x497a34 |
Per-frame simulation (SP/MP via param) |
lotr2_game_ui_input_dispatch |
0x489129 |
— |
Route UI actions from slot flags |
lotr2_game_shutdown |
0x4adc73 |
0x49767d |
Winmain exit cleanup |
Screen state machine (pass 21–24)
See ../../docs/scenes/README.md for the full
interactive screen catalog grouped by scene type.
| Symbol |
Address |
Role |
lotr2_game_screen_mode |
0x507ab8 |
Current screen/mode id (char); central switch key |
lotr2_game_screen_phase |
0x59347c |
Screen phase (1 init / 2 active) paired with mode transitions |
lotr2_game_wizard_step |
0x593494 |
Sub-step within mode 0x1f (save/load, MP lobby wizard) |
lotr2_game_turn_phase |
0x5d9c88 |
Turn-phase index (1–4) set by action handlers |
lotr2_game_screen_mode_tick |
0x438b90 |
Main per-frame screen handler; branches on lotr2_game_screen_mode |
lotr2_game_pick_scroll_dir |
0x43a5af |
Compute 8-way scroll direction; call lotr2_game_screen_tick |
lotr2_game_ui_scroll_bar_hit |
0x43a9ce |
Scroll-bar hit zone from mouse position |
lotr2_game_validate_scroll |
0x4d65fc |
Debounce scroll apply via timeGetTime |
lotr2_game_screen_tick |
0x43a2e7 |
Apply map/panel scroll delta after direction pick |
lotr2_game_dispatch_click |
0x43a78c |
Route click to map or panel handler by lotr2_game_active_view |
lotr2_game_handle_map_click |
0x43a8c8 |
Hit-test map grid; call lotr2_game_select_province |
lotr2_game_handle_panel_click |
0x43a7d1 |
Panel viewport click; battle prep or map blit |
lotr2_game_select_province |
0x43ab19 |
Select province from map click; update scroll |
lotr2_game_ui_hit_test_campaign_toolbar |
0x43acbb |
Campaign toolbar button hit test |
lotr2_game_ui_hit_test_status_buttons |
0x43acf5 |
Status bar button hit test |
lotr2_game_ui_hit_test_battle_toolbar |
0x43ad32 |
Battle screen toolbar hit test |
lotr2_game_on_mp_lobby_start |
0x43ad7a |
MP lobby complete → start game or send net msg |
lotr2_game_return_to_campaign_map |
0x43cbd2 |
Cancel overlay; restore campaign map view |
lotr2_game_economy_tick_player |
0x456516 |
Per-player economy tick at end of turn |
lotr2_game_diplomacy_tick_player |
0x451511 |
Per-player diplomacy tick at end of turn |
lotr2_game_reset_lobby_flags |
0x43c007 |
Clear lobby globals before MP setup |
lotr2_game_action_enter_options_dialog |
0x43c023 |
Options dialog (mode '9'); MP client defers via msg 0xe3 |
lotr2_game_action_turn_phase1_toggle |
0x43c087 |
Turn phase 1; toggles lotr2_game_turn_phase1_ack; MP opcode 0x32 |
lotr2_game_action_end_turn |
0x43c101 |
Turn phase 2; toggles lotr2_game_turn_phase2_ack; economy/diplomacy loop |
lotr2_game_action_turn_phase3_toggle |
0x43c1c4 |
Turn phase 3; toggles lotr2_game_turn_phase3_ack |
lotr2_game_action_turn_phase4_toggle |
0x43c23e |
Turn phase 4; toggles lotr2_game_turn_phase4_ack |
Hub leaf callees (pass 25)
| Symbol |
Address |
Role |
lotr2_game_ui_hit_test_dialog_ok |
0x415da6 |
Dialog OK button hit test (0x18×0x18); hub callee in modes 2, 0xf |
lotr2_game_clear_bottom_panel_rows |
0x41c2f0 |
Clear bottom panel screen rows 0x1da–0x1df on dialog dismiss |
lotr2_game_mark_status_bar_dirty |
0x444488 |
Mark status bar dirty region after scroll/exit |
lotr2_game_wizard_on_mp_host |
0x43ae19 |
MP host wizard step; calls lotr2_net_start_session |
lotr2_game_wizard_on_save_step |
0x43af56 |
Save/load wizard turn-phase dispatch |
lotr2_game_ui_draw_modal_backdrop |
0x43bf96 |
Draw 0xa0×0xa0 modal backdrop tile |
Turn-action table (pass 26)
| Symbol |
Address |
Role |
lotr2_game_action_toggle_ui_slots |
0x43c2b8 |
Toggle lotr2_game_ui_action_slots_active; reset UI slots |
lotr2_game_action_toggle_map_scroll_lock |
0x43c2f8 |
Toggle lotr2_game_map_scroll_lock; clears scroll state |
lotr2_game_mp_send_toolbar_context_panel_0 … _4 |
0x43c33d–0x43c435 |
MP client toolbar context panels (msg ids 0x123–0x127, style 0x13) |
lotr2_game_action_show_help |
0x43c473 |
Open l2help.hlp via WinHelpA |
lotr2_game_action_enter_game_options_dialog |
0x43c49d |
Enter game-options dialog (mode 'B') |
lotr2_game_action_enter_display_options_dialog |
0x43c4b9 |
Enter display-options dialog (mode 'C') |
lotr2_game_action_enter_diplomacy_faction_dialog |
0x43db3d |
Enter diplomacy faction dialog (mode 0x0b); turn phase 5 |
lotr2_game_action_toggle_music |
0x43c4d5 |
Toggle campaign/battle music stream |
lotr2_game_action_toggle_sfx |
0x43c55a |
Toggle sound-effects flag |
lotr2_game_action_toggle_flag_b82c |
0x43c5cb |
Toggle DAT_0059b82c |
lotr2_game_action_toggle_flag_b868 |
0x43c606 |
Toggle DAT_0059b868 |
lotr2_game_action_toggle_hicolor |
0x43c641 |
Toggle hi-color mode; re-init DirectDraw |
lotr2_game_action_show_quit_confirm |
0x43c891 |
Enter mode 0x25; draw quit-confirm dialog (pass 49) |
lotr2_game_action_show_scroll_options |
0x43c864 |
Enter mode 0x31; draw scroll-speed options (pass 49) |
lotr2_game_mp_send_menu_selection_inc |
0x441bf3 |
MP build-menu increment; SP local or net opcode 0x1f |
lotr2_game_mp_send_menu_selection_dec |
0x441cb7 |
MP build-menu decrement; SP local or net opcode 0x20 |
Campaign UI cluster (pass 27)
| Symbol |
Address |
Role |
lotr2_game_ui_on_army_panel_recruit |
0x423f7 |
Army panel lower-right → mode 0x19 |
lotr2_game_ui_on_army_panel_trade |
0x424a3 |
Army panel upper-left → mode 0x14 |
lotr2_game_ui_on_army_panel_disband |
0x424d3 |
Army panel upper-right → mode 0x16 |
lotr2_game_ui_on_army_panel_move |
0x424ea |
Army panel lower-left → mode 0x15 |
lotr2_game_ui_detect_map_drag |
0x40df7 |
Detect map drag gesture (9 px threshold) |
lotr2_game_ui_on_mode_5_panel_click |
0x40f4d |
Mode 5 overlay panel click handler |
lotr2_game_ui_hit_test_minimap_province |
0x417fa |
Minimap province hit test; MP sends opcode 0x26 |
lotr2_game_ui_apply_minimap_action |
0x418e5 |
Apply minimap province action (SP) |
lotr2_game_ui_hit_test_build_action |
0x41b2d |
Build-menu toolbar hit test; sets build action |
lotr2_game_ui_on_end_turn_click |
0x42501 |
End-turn button; MP sends opcode 0x21 |
Campaign UI / sync cluster (pass 35)
Full rename sweep for 0x0043e000–0x0044ffff (146 symbols). Apply:
tools/ghidra/game_renames_pass35.py.
Turn finalize / end-turn chain
| Symbol |
Address |
Role |
lotr2_game_finalize_army_turn |
0x4400a0 |
Apply army 0x1a4 record to player; economy/diplomacy ticks |
lotr2_game_ui_apply_player_turn |
0x42548 |
Write turn phase; prosperity + diplomacy for active player |
lotr2_game_ui_commit_map_cell_action |
0x440521 |
Commit map cell action; MP opcode 0x22 |
lotr2_game_apply_map_cell_action_sp |
0x440626 |
SP map cell: diplomacy bucket + economy ticks |
lotr2_game_mp_send_upkeep_increase |
0x4425e2 |
MP opcode 0x23 upkeep +1 |
lotr2_game_mp_send_upkeep_decrease |
0x44267f |
MP opcode 0x24 upkeep −1 |
lotr2_game_ui_exit_build_submode |
0x442c37 |
Exit build sub-screen; restore map or mode 0x18 |
Battle exit / siege
| Symbol |
Address |
Role |
lotr2_game_ui_on_post_battle_return |
0x43f517 |
Post-battle screen restore; siege assault or MP 0x2d |
lotr2_game_start_siege_assault_battle |
0x43f9f7 |
Start siege assault from army pair |
lotr2_game_ui_on_siege_battle_end |
0x43f0cf |
Siege end → campaign; mode 0x11 |
lotr2_game_move_army_to_map_cell |
0x43ef48 |
SP army move to map cell; may trigger battle |
Panel hit-tests (extends pass 27)
| Symbol |
Address |
Role |
lotr2_game_ui_hit_test_turn_toolbar |
0x4403af |
Turn-phase toolbar widget hit |
lotr2_game_ui_hit_test_mode5_buildings |
0x440f96 |
Mode-5 building grid hit test |
lotr2_game_ui_minimap_tile_at_point |
0x441300 |
Minimap province index from coords |
lotr2_game_ui_on_minimap_province_click |
0x4413ba |
Minimap click; MP opcode 0x25 |
lotr2_game_ui_hit_test_army_order_bar |
0x43ea1f |
Army order bar when army selected |
Battle UI (in-battle scroll / figure pick)
| Symbol |
Address |
Role |
lotr2_game_ui_on_battle_scroll_drag |
0x443859 |
Battle map scroll drag; MP opcode 0x41 |
lotr2_game_ui_on_battle_figure_click |
0x443b7c |
Battle figure group pick; MP 0x42/0x44 |
lotr2_combat_apply_scroll_reveal_orders |
0x443b26 |
Scroll reveal + optional group orders |
Post-siege outcome + battle HUD (pass 45)
| Symbol |
Address |
Role |
lotr2_game_ui_draw_battle_player_compare_rows |
0x427fea |
Dual-player resource compare (7 types; layout modes 0–2) |
lotr2_game_ui_draw_siege_field_battle_outcome_panel |
0x428715 |
Screen mode 0x12; post field-battle siege assault outcome |
lotr2_game_ui_draw_siege_defender_routed_panel |
0x4289ec |
Screen mode 0x13; defender routed; calls lotr2_game_battle_on_defender_routed |
lotr2_game_ui_draw_battle_narrative_panel |
0x428c2d |
Screen mode 0x30; battle event narrative text |
lotr2_combat_init_battle_hud_frame |
0x428de3 |
Screen modes 0x28/0x2c; status bar + viewport + palette setup |
lotr2_combat_redraw_hud_resource_counters |
0x428f1c |
Food/gold counters on battle HUD strip |
lotr2_combat_tick_hud_army_roster |
0x429113 |
Refresh roster icons when army headcount changes |
lotr2_combat_blit_hud_roster_icons |
0x429292 |
Blit per-type figure icons on HUD roster |
lotr2_combat_draw_hud_roster_headcounts |
0x4293af |
Head-count labels beside roster icons |
lotr2_combat_draw_paused_overlay |
0x429527 |
Paused-game overlay during battle |
lotr2_game_ui_draw_intro_chapter_dialog |
0x42ace1 |
Screen mode 0x44; intro Smacker chapter picker |
Net remote-apply (0x44f000)
Thin unpack dispatch targets named lotr2_net_apply_remote_* — mirror the MP send
wrappers above (upkeep, turn phase, battle moves, diplomacy). See symbol-map Pass 35d.
l2_eng text draw (pass 29a)
| Symbol |
Address |
Role |
lotr2_game_ui_draw_eng_string |
0x40780e |
Draw l2_eng string by sheet index + string index |
lotr2_game_ui_draw_number_field |
0x407a3b |
Format number with prefix char and draw |
lotr2_game_ui_draw_eng_wrapped |
0x407cb4 |
Word-wrap l2_eng text into rows |
lotr2_game_ui_draw_text_run |
0x40710e |
Walk byte string; expand #; blit glyphs |
lotr2_game_ui_blit_font_glyph |
0x4074eb |
Single glyph lookup + clip + blit |
lotr2_game_ui_draw_line |
0x4084b4 |
Bresenham line draw |
lotr2_game_ui_draw_panel_frame_style0 |
0x408812 |
4-edge panel frame (style 0) |
Descriptor tables live in .rdata (e.g. DAT_004f8ee0); each entry is 0x18 bytes.
Type byte at offset 0xf: 0x02 toggle, 0x04/0x05 action (with press timer at
+0xd).
Screen dispatch
| Symbol |
Address |
Role |
lotr2_game_ui_draw_screen_widgets |
0x4d51a6 |
Frame render: draw widget tables for current lotr2_game_screen_mode |
lotr2_game_ui_handle_screen_widget_input |
0x4d57b4 |
lotr2_game_screen_mode_tick: per-mode widget click/hit-test before toolbar tests |
Core draw / blit
| Symbol |
Address |
Role |
lotr2_game_ui_draw_widget_table |
0x414611 |
Walk descriptor table; blit tiles; mark dirty |
lotr2_game_ui_blit_tile_at |
0x4147c9 |
Blit single tile from UI tile sheet at coords |
lotr2_game_ui_store_widget_positions |
0x41459b |
Copy widget origins into blit globals |
lotr2_game_ui_draw_sliders |
0x41487e |
Draw slider track, thumb, value line |
lotr2_game_ui_redraw_scroll_list |
0x413d65 |
Redraw bottom-panel scroll list rows |
Hit-test / input
| Symbol |
Address |
Role |
lotr2_game_ui_handle_widget_click |
0x415021 |
AABB hit-test, press timers, callback dispatch |
lotr2_game_ui_hit_test_widget_table |
0x4159ce |
Return 1-based widget index under cursor |
lotr2_game_ui_hit_test_rect_table |
0x415ba9 |
Generic rect-table hit test |
lotr2_game_ui_clear_widget_pressed |
0x414faf |
Clear press-state on widget entries |
lotr2_game_ui_hit_test_slider |
0x414a02 |
Slider decrement/track/increment zones |
lotr2_game_ui_slider_set_from_mouse |
0x414bad |
Map mouse X to slider value |
lotr2_game_ui_hit_test_dialog_ok |
0x415da6 |
Modal OK button (0x18×0x18) hit test |
Modals, menus, popups
| Symbol |
Address |
Role |
lotr2_game_ui_enter_modal_mode |
0x415cb4 |
Save screen mode; enter overlay mode 0x1e |
lotr2_game_ui_enter_parameter_dialog |
0x415d23 |
Save screen mode; enter parameter mode 0x21 |
lotr2_game_ui_enter_popup_menu |
0x4154af |
Enter popup submenu mode 0x32 |
lotr2_game_ui_handle_popup_menu |
0x415377 |
Popup menu input loop |
lotr2_game_ui_run_menu_dialog |
0x41570e |
Blocking menu dialog (measure/draw/hit-test) |
lotr2_game_ui_run_wait_dialog |
0x415c42 |
Blocking wait dialog with message pump |
lotr2_game_ui_draw_modal_frame_small |
0x414339 |
Small modal frame + title |
lotr2_game_ui_draw_modal_frame_large |
0x414397 |
Large parameter dialog frame |
Screen-mode dialogs (pass 31d)
Full-screen and panel overlays keyed by lotr2_game_screen_mode. Teardown when
lotr2_game_screen_phase clears runs through lotr2_game_ui_on_screen_phase_exit.
| Symbol |
Address |
Role |
lotr2_game_ui_on_screen_phase_exit |
0x417150 |
Per-mode exit/teardown dispatcher |
lotr2_game_ui_enter_campaign_map_mode |
0x41755f |
Campaign map draw/init (mode 0) |
lotr2_game_ui_redraw_province_panel |
0x417735 |
Province summary strip refresh |
lotr2_game_ui_draw_trade_dialog |
0x418ffe |
Trade (mode 0x14) |
lotr2_game_ui_draw_move_army_dialog |
0x41947c |
Move army (mode 0x15) |
lotr2_game_ui_draw_disband_dialog |
0x419648 |
Disband (mode 0x16) |
lotr2_game_ui_draw_recruit_dialog |
0x419abf |
Recruit (mode 0x19) |
lotr2_game_ui_draw_save_wizard_chrome |
0x41c77d |
Save/load wizard chrome (modes '5'/'6') |
lotr2_game_ui_draw_save_slot_list |
0x41c950 |
Save slot list inside wizard |
lotr2_game_ui_draw_options_dialog |
0x41cd6c |
Options (mode '9') |
lotr2_game_ui_enter_merchant_screen |
0x41ddbd |
Merchant overlay (mode 0x08) |
lotr2_game_ui_enter_armoury_screen |
0x41fcc1 |
Armoury overlay (mode 0x0a) |
Castle interior / gateway UI (pass 37)
Armoury refresh, province overview, event popups, and gateway screen callees in
VA 0x41f000–0x425fff. See symbol-map.md Pass 37.
| Symbol |
Address |
Role |
lotr2_game_ui_refresh_armoury_screen |
0x41ff10 |
Reload armoury assets after dialog dismiss (mode 0x0d→0x0a) |
lotr2_game_ui_draw_armoury_stat_rows |
0x420005 |
Draw 8 armoury build-slot stat rows |
lotr2_game_ui_blit_armoury_build_slots |
0x420243 |
Blit active build-slot sprites |
lotr2_game_ui_draw_armoury_weapon_panel |
0x4202e3 |
Load/draw selected weapon type PL8 panel |
lotr2_game_ui_draw_province_overview_panel |
0x420470 |
Province gold/pop summary (mode 0x17) |
lotr2_game_ui_enter_post_siege_screen |
0x4210d0 |
Post-siege battle result screen (mode 0x11) |
lotr2_game_ui_enter_siege_battle_prep_screen |
0x4215a8 |
Siege battle prep backdrop (mode 0x1b) |
lotr2_game_ui_draw_diplomacy_minimap_panel |
0x422acd |
Diplomacy minimap overlay (mode 0x18) |
lotr2_game_ui_present_province_event_popup |
0x422da2 |
Present province event modal; owned vs generic |
lotr2_game_ui_setup_province_event_chrome |
0x423c6e |
Event popup panel layout from lotr2_game_province_event_flags |
lotr2_game_ui_draw_province_event_body |
0x423f78 |
Event popup body text and icons |
lotr2_game_ui_enter_gateway_screen |
0x425f59 |
Gateway/new-game screen (gateway.256) |
lotr2_game_ui_draw_number_with_table_caption |
0x4228d7 |
Shared number + table-8 caption helper |
lotr2_game_ui_draw_status_bar_overlay |
0x421a97 |
Bottom status strip and debug mem overlay |
Castle overview rows (lotr2_game_ui_build_overview_row_list, lotr2_game_ui_draw_overview_*_row)
and mode-5 panel setup are pass 33 — see symbol-map.md Pass 32d.
Wizard step renderers (pass 39)
New-game / save-load wizard draw chain for screen mode 0x1f, continuing from
pass 37 gateway entry. Sub-steps dispatched via lotr2_game_wizard_step (1–8).
See symbol-map.md Pass 39.
| Symbol |
Address |
Role |
lotr2_game_wizard_load_step_assets |
0x42634c |
Load gateway/custom/panels2 assets for current wizard step |
lotr2_game_wizard_redraw_current_step |
0x426498 |
Redraw dispatcher; branches on lotr2_game_wizard_step |
lotr2_game_wizard_draw_loading_message |
0x426596 |
Loading overlay + present frame |
lotr2_game_wizard_draw_step1_panel |
0x426648 |
Step 1 intro panel |
lotr2_game_wizard_draw_step1_option_rows |
0x4266d7 |
Step 1 selectable option rows |
lotr2_game_wizard_draw_step2_panel |
0x426849 |
Step 2 panel |
lotr2_game_wizard_draw_step2_option_rows |
0x4268a5 |
Step 2 option rows |
lotr2_game_wizard_draw_step3_load_panel |
0x426a8c |
Step 3 save/load slot panel |
lotr2_game_wizard_draw_step4_name_panel |
0x426aa1 |
Step 4 game name text input |
lotr2_game_wizard_draw_step4_mp_options |
0x426b7a |
Step 4 MP host/client option rows |
lotr2_game_wizard_blit_player_slot_icons |
0x426c9c |
Player slot icon sprites |
lotr2_game_wizard_draw_name_text_input |
0x426dd3 |
Name field + blinking cursor |
lotr2_game_wizard_draw_step5_sp_setup |
0x426e9b |
Step 5 SP game setup (map + players) |
lotr2_game_wizard_draw_step6_mp_setup |
0x426f4e |
Step 6 MP host setup |
lotr2_game_wizard_draw_config_value_rows |
0x427073 |
Config value label rows |
lotr2_game_wizard_draw_map_size_selector |
0x427191 |
Map size minimap + selector bar |
lotr2_game_wizard_draw_player_slot_rows |
0x4273dc |
Nation/culture player slot rows |
lotr2_game_wizard_draw_step7_player_count |
0x4275e7 |
Step 7 player-count panel |
lotr2_game_wizard_draw_mp_player_list |
0x427747 |
MP connected player name list |
lotr2_game_wizard_draw_step6_name_field |
0x427853 |
MP ruler name input overlay |
lotr2_game_wizard_draw_step8_panel |
0x427968 |
Step 8 final confirmation panel |
lotr2_game_ui_draw_siege_assault_prep_panel |
0x427a17 |
Siege assault prep (mode 0x1d; sgeplans.pl8) |
Wizard + turn actions (pass 29c, pass 38)
| Symbol |
Address |
Role |
lotr2_game_wizard_on_start_step |
0x43b316 |
Wizard turn-phase dispatch; MP lobby start |
lotr2_game_wizard_apply_config_choice |
0x43b7f7 |
Apply wizard config row; MP net msg 9 |
lotr2_game_wizard_open_options_modal |
0x43bb91 |
Open options modal (lotr2_game_ui_enter_modal_mode) |
lotr2_game_wizard_cancel_and_resume |
0x43bbb5 |
Cancel wizard; SP tick or MP net opcode 0x19 |
lotr2_game_action_enter_diplomacy |
0x43ccf5 |
Enter diplomacy screen mode 0x20 |
lotr2_game_action_toggle_castle_view |
0x43cad2 |
Toggle castle siege map view |
lotr2_game_action_enter_replay_mode |
0x43cdb7 |
Enter replay (mode 8); SP stat apply or MP opcode 0x28 |
lotr2_game_action_set_replay_speed_campaign |
0x43d072 |
Set campaign replay speed; s068_02_wav |
lotr2_game_action_set_replay_speed_battle |
0x43d10c |
Set battle replay speed; s068_01_wav |
lotr2_game_action_build_queue_increment_slot |
0x43d46b |
Increment armoury build-queue slot |
lotr2_game_action_build_queue_decrement_slot |
0x43d4ed |
Decrement build-queue slot |
lotr2_game_action_enter_diplomacy_text_mode_1 … _6 |
0x43dbad–0x43dd40 |
Diplomacy text-input sub-modes (screen 0x1a) |
lotr2_game_action_apply_diplomacy_text_submit |
0x43de2a |
Submit diplomacy text; SP queue or MP 0x48/0x49 |
lotr2_game_ui_hit_test_tax_slider |
0x43d820 |
Tax slider hit-test; calls lotr2_game_apply_tax_rate |
Pass 38 renamed 56 symbols in 0x0043b000–0x0043dfff (turn-action table tail,
lobby flags, replay speed, build-queue ops). Empty table slots use
lotr2_game_action_noop_reserved_01–_20 (pass 48; was hex-suffix noop_c*) —
see symbol-map.md Pass 38/48 and
tools/ghidra/game_action_noop_renames_pass48.py.
Text input cursor (pass 33a)
| Symbol |
Address |
Role |
lotr2_game_ui_text_input_on_return |
0x406931 |
Enter key; sets selection max |
lotr2_game_ui_text_input_on_backspace |
0x406946 |
Backspace; cursor left + delete |
lotr2_game_ui_text_input_on_delete_key |
0x406969 |
Delete key at cursor |
lotr2_game_ui_text_input_toggle_insert_mode |
0x406979 |
Insert/overtype toggle |
lotr2_game_ui_text_input_cursor_left … cursor_end |
0x406992–0x4069e7 |
Arrow/home/end cursor movement |
lotr2_game_ui_text_input_update_length |
0x406c1f |
Recompute buffer length; sim-tick callee |
RNG + distance helpers (pass 33b)
| Symbol |
Address |
Role |
lotr2_game_ui_advance_frame_counters |
0x4092c2 |
Blink/animation counter tick |
lotr2_game_rng_lfsr_shift_wide_pair |
0x4093fb |
31-bit LFSR (wide pair) |
lotr2_game_rng_bounded |
0x40954e |
Bounded random via LFSR |
lotr2_game_dist_manhattan |
0x409862 |
Manhattan distance; combat + map |
lotr2_game_dist_chebyshev |
0x409901 |
Chebyshev (max axis) distance |
Campaign map entity draw (pass 33c)
| Symbol |
Address |
Role |
lotr2_game_ui_campaign_map_redraw_tick |
0x40a9d0 |
Per-frame campaign map redraw entry |
lotr2_game_ui_draw_map_entities_layered |
0x40ab7f |
Full layered entity pass |
lotr2_game_ui_draw_map_entities_simple |
0x40ad98 |
Simple entity chain blit |
lotr2_game_ui_draw_map_entities_with_badges |
0x40af13 |
Entity + building badge pass |
Campaign map entity draw tail (pass 46)
Completes pass 33c: column row passes, stamp/clip dispatch, stack-icon blit.
| Symbol |
Address |
Role |
lotr2_game_ui_draw_map_column_interior_row |
0x40b3f9 |
Interior column cells in layered draw |
lotr2_game_ui_draw_map_column_bridge_row |
0x40b53f |
Selected-column bridge row (pass types 0/3/4) |
lotr2_game_ui_prep_map_entity_stamp |
0x40bcd3 |
Entity metadata + stamp dispatch by pass type |
lotr2_game_ui_stamp_map_entity_pl8 |
0x40bf85 |
PL8 stamp router for map entities |
lotr2_game_ui_prep_map_entity_clip_blit |
0x40c4cc |
Clip-blitz path for map entity sprites |
lotr2_game_ui_blit_entity_stack_icon |
0x40cab2 |
Stack-count icon sprite (entity flag 0x80) |
lotr2_gfx_stamp_map_entity_stride282 |
0x45d460 |
Inline stride-0x282 row stamp helper |
See symbol-map.md Pass 46 and
tools/ghidra/game_renames_pass46.py.
Castle overview panel (pass 33d)
| Symbol |
Address |
Role |
lotr2_game_ui_build_overview_row_list |
0x417e20 |
Build overview row type list for player |
lotr2_game_ui_draw_overview_farm_row |
0x41800e |
Farm/production overview row |
lotr2_game_ui_draw_overview_lumber_row |
0x418199 |
Lumber overview row |
lotr2_game_ui_setup_mode5_panel |
0x41a02d |
Mode 5 castle panel setup |
Campaign UI blit + LZSS (pass 29d)
| Symbol |
Address |
Role |
lotr2_game_ui_blit_ui_sprite |
0x410f4c |
Primary UI sprite blit from global sheet |
lotr2_game_ui_draw_map_entity_chain |
0x40dd4a |
Draw linked map entity icons |
lotr2_game_save_compress_buffer |
0x4130b8 |
LZSS compress (save blob helper) |
lotr2_game_save_decompress_buffer |
0x41342d |
LZSS decompress |
End-turn economy / diplomacy (pass 29e)
| Symbol |
Address |
Role |
lotr2_game_end_turn_tick |
0x4513b0 |
Season orchestrator (events, tax, morale) |
lotr2_game_end_turn_all_players |
0x4514b0 |
Per-player economy + diplomacy tick loop |
lotr2_game_apply_season_random_events |
0x451785 |
Random seasonal event dispatch |
lotr2_game_apply_tax_income |
0x452602 |
Apply tax income; update map stamps |
lotr2_game_diplomacy_plan_slot |
0x4578cf |
Plan diplomacy slot spend/target |
lotr2_game_allocate_prosperity |
0x457c9f |
Distribute prosperity across buckets |
Province development / allocation sliders (pass 44)
| Symbol |
Address |
Role |
lotr2_game_resolve_capital_sieges |
0x458e96 |
End-turn province build completion; calls development helpers |
lotr2_game_deduct_province_development_stock |
0x459285 |
Deduct faction food/goods during active province build |
lotr2_game_update_province_development_targets |
0x4593fe |
Refresh growth targets after build progress check |
lotr2_game_calc_province_development_pct |
0x45956c |
Province build completion percent |
lotr2_game_recount_faction_developing_castles |
0x459619 |
Count in-progress castles per faction (map cell status 0x03) |
lotr2_game_init_new_campaign_players |
0x459709 |
New-game defaults for all player records |
lotr2_game_recalc_allocation_sliders |
0x459bd7 |
Recompute tax/allocation slider rows for local player |
lotr2_game_refresh_minimap_overlay_colors |
0x45a173 |
Update minimap overlay indices before pixel paint |
See symbol-map.md Pass 44 and
tools/ghidra/game_renames_pass44.py.
See symbol-map.md Pass 29 and tools/ghidra/game_renames_pass29.py for the full 219-symbol list.
Turn simulation / campaign AI (pass 34)
| Symbol |
Address |
Role |
lotr2_game_ai_turn_phase_tick |
0x4b0980 |
Sim-tick AI driver; branches on lotr2_game_ai_turn_phase (1–7) |
lotr2_game_ai_advance_turn_phase |
0x4b30a9 |
Advance outer phase; reset step counters |
lotr2_game_ai_player_step_loop |
0x4b0cde |
Per-player 14-step AI loop (lotr2_game_ai_player_step) |
lotr2_game_ai_downgrade_inactive_garrisons |
0x4b10c1 |
First step of lotr2_game_end_turn_tick for AI garrisons |
lotr2_game_ai_compute_player_rankings |
0x4b113f |
Sort players by score after end-turn (phase 7) |
lotr2_game_ai_turn_state_init |
0x4b2bed |
Initialize AI turn state at new game |
lotr2_game_ai_init_new_game_players |
0x4b1ff1 |
Reset player records; assign capitals |
lotr2_game_ai_process_diplomacy_queue |
0x4b86e5 |
Drain per-player diplomacy action queue |
lotr2_game_ai_build_recruit_pass |
0x4b3f8c |
AI build/recruit pass (step 5) |
lotr2_game_ai_army_attack_plan |
0x4b5c09 |
Pick attack target; spawn army (step 9) |
lotr2_game_battle_on_army_enter_province |
0x4bf708 |
Army tile entry → battle or conquest |
lotr2_game_conquest_capture_province |
0x4bfd36 |
Peaceful province capture |
Army movement AI (lotr2_map_ai_*) documented in map/MODULE.md.
Full 97-symbol list: tools/ghidra/game_renames_pass34.py.
Castle / build / recruit (0x4c, pass 36)
Army slot records use stride 0x1a4 (lotr2_game_army_record_table base). Build queue sub-fields
at +0x52/+0x54/+0x56 (+6 per slot, 3 slots). Province records remain stride
0x300.
| Symbol |
Address |
Role |
lotr2_game_build_find_active_slot |
0x4c0eca |
Scan build slots from lotr2_game_build_slot_index; advance progress |
lotr2_game_build_tick_slot_progress |
0x4c0f17 |
Per-turn build queue labor allocation |
lotr2_game_build_place_on_province |
0x4c03db |
Place building/army on province; merge or garrison |
lotr2_game_build_start_castle_action |
0x4c0842 |
Enter castle build screen (lotr2_game_screen_mode = 0x1d) |
lotr2_game_recruit_spawn_army |
0x4c23f1 |
Recruit unit stack; spawn army; debit gold |
lotr2_game_army_merge_into |
0x4c2ad7 |
Merge two army slots (strength + build queue) |
lotr2_game_battle_start_siege_assault |
0x4c14bb |
Begin siege assault; modes 0x12/0x13 |
lotr2_game_battle_resolve_siege_outcome |
0x4c3cde |
Post-siege capture, army release, end-turn |
lotr2_game_upkeep_tick_army_slots |
0x4c56eb |
Upkeep starvation per army slot |
lotr2_game_sum_faction_army_strength |
0x4c5d22 |
Faction total army strength for UI/budget |
lotr2_game_castles_dat_write_player_layout |
0x4c8cf3 |
Write siege castle layout slice to castles.dat |
lotr2_gfx_blit_castle_mask_to_backbuf |
0x4cfa6b |
Castle interior view mask blit to backbuf |
Full 109-symbol list: tools/ghidra/game_renames_pass36.py.
| Symbol |
Address |
Role |
lotr2_game_ui_apply_player_turn |
0x42548 |
Apply turn phase for one player locally |
flowchart TD
idleTick[lotr2_game_idle_tick]
uiDispatch[lotr2_game_ui_input_dispatch]
scrollPick[lotr2_game_pick_scroll_dir]
scrollApply[lotr2_game_screen_tick]
clickDispatch[lotr2_game_dispatch_click]
mapClick[lotr2_game_handle_map_click]
panelClick[lotr2_game_handle_panel_click]
selectProv[lotr2_game_select_province]
screenMode[lotr2_game_screen_mode]
mainTick[lotr2_game_screen_mode_tick]
idleTick --> uiDispatch
uiDispatch --> mainTick
mainTick --> screenMode
scrollPick --> scrollApply
clickDispatch --> mapClick
clickDispatch --> panelClick
mapClick --> selectProv
See symbol-map.md passes 25–27 for full rename tables.
Asset loading (init chain)
| Symbol |
Address |
Role |
lotr2_game_load_base_assets |
0x4af92e |
Load 13× base01.256 palette/tile files |
lotr2_game_reset_pl8_ptrs |
0x4af1ce |
Zero PL8 heap pointer globals |
lotr2_game_load_pl8_assets |
0x4ae81a |
Load base1a.pl8 / misc_cty.pl8 slots |
lotr2_asset_verify_pl8 |
0x4c9eaa |
Verify/open PL8 with decompress fallback |
Sim tick teardown / map
See map/MODULE.md for the full map subsystem (pass 14–15).
| Symbol |
Address |
Role |
lotr2_game_free_pl8_assets |
0x4af233 |
Free PL8 slot malloc blocks |
lotr2_game_free_ui_assets |
0x4af43b |
Free screen/UI malloc blocks |
lotr2_game_free_map_ptrs |
0x4af721 |
Free map-related malloc blocks |
lotr2_map_load_new_game |
0x477580 |
New-game map load orchestrator |
lotr2_map_load_from_dat |
0x477610 |
Read l2_maps.dat into province grid |
lotr2_map_init_province_grid |
0x4346a1 |
Initialize province index grid and facing |
lotr2_game_reset_map_state |
0x4ae5ae |
Reset map/view globals at sim-tick start |
lotr2_game_clear_map_ptrs |
0x4af6ee |
Zero map malloc pointer globals after free |
Sim tick init (game-level)
| Symbol |
Address |
Role |
lotr2_game_load_system_ui_pl8 |
0x4afaf1 |
Load system2.pl8 UI sheet variant |
lotr2_game_load_misc_sel_pl8 |
0x4af109 |
Load misc_sel.PL8 after asset teardown |
lotr2_game_set_difficulty_tier |
0x4c896d |
Set difficulty tier index from scenario param |
lotr2_game_sync_ui_scroll_offset |
0x43ba95 |
Recompute UI scroll offset from season counter |
lotr2_game_ui_queue_screen_action |
0x489419 |
Queue a UI screen action by slot index |
lotr2_game_ui_restore_screen_mode |
0x489491 |
Restore prior screen mode after action overlay |
Recovered globals
| Symbol |
Address |
Role |
lotr2_ui_slot_flags |
0x50c3a0 |
20-byte UI slot availability flags |
lotr2_ui_slot_count |
0x50c390 |
Active slot count (init to 0x14) |
lotr2_game_tick_pending |
0x5efe24 |
Non-zero when sim tick should run |
lotr2_game_screen_mode |
0x507ab8 |
Current screen/mode id |
lotr2_game_screen_phase |
0x59347c |
Screen phase (init vs active) |
lotr2_game_wizard_step |
0x593494 |
Wizard sub-step for mode 0x1f |
lotr2_game_turn_phase |
0x5d9c88 |
Turn-phase index 1–4 |
lotr2_winmain call tree
flowchart TD
winmain[lotr2_winmain @ 0x41680b]
loadInf[lotr2_game_load_lords2_inf]
loadEng[lotr2_asset_read_file l2_eng]
loadCfg[lotr2_game_load_config]
createWin[lotr2_ui_create_main_window]
initAudio[lotr2_audio_init_directsound]
easterEgg[lotr2_game_check_easter_egg]
intro[lotr2_game_play_intro_or_enable_tick]
initGame1[lotr2_game_init_phase1]
loadBase[lotr2_game_load_base_assets]
initGame2[lotr2_game_init_phase2]
loadPl8[lotr2_game_load_pl8_assets]
resetUi[lotr2_game_reset_ui_slots]
loop[Message loop]
pollNet[lotr2_net_poll_messages]
idleTick[lotr2_game_idle_tick]
tickSP[lotr2_game_tick_sp]
tickMP[lotr2_game_tick_mp]
simTick[lotr2_game_sim_tick]
frameAdv[lotr2_game_frame_advance]
winmain --> loadInf
winmain --> loadEng
winmain --> loadCfg
winmain --> createWin
createWin --> initAudio
winmain --> easterEgg
winmain --> intro
winmain --> initGame1
initGame1 --> loadBase
initGame1 --> initGame2
initGame2 --> loadPl8
initGame2 --> resetUi
resetUi --> loop
loop --> pollNet
loop --> idleTick
idleTick --> tickSP
idleTick --> tickMP
tickSP --> simTick
tickMP --> simTick
idleTick --> frameAdv
Startup sequence (ordered)
lotr2_game_load_lords2_inf — read lords2.inf config
- Load English string table:
lotr2_asset_read_file("l2_eng", …)
lotr2_game_load_config — parse l2.ini, resolve _sierra.ini path
lotr2_ui_create_main_window — register class, create HWND
lotr2_audio_init_directsound + lotr2_audio_start_stream_from_name — music stream
lotr2_game_check_easter_egg — optional pumpkin WAV check
lotr2_game_play_intro_or_enable_tick — intro Smacker or enable tick
lotr2_game_init_phase1 — load base .256 assets via lotr2_game_load_base_assets
lotr2_game_init_phase2 — load PL8 assets via lotr2_game_load_pl8_assets
lotr2_game_reset_ui_slots — clear UI slot table; lotr2_net_session_active = 0
- Enter message loop
Main loop (idle path)
When PeekMessageA returns false:
lotr2_net_poll_messages — DirectPlay receive pump
- If window visible (
DAT_005076c0 != 0): lotr2_game_idle_tick
- Checks DirectDraw surface state via
lotr2_gfx_surfaces_restorable;
lotr2_gfx_on_surface_lost invalidates the main window on failure
- When
lotr2_game_tick_pending: dispatches lotr2_game_tick_sp (SP) or
lotr2_game_tick_mp (MP), then lotr2_game_sim_tick
- Else:
lotr2_game_frame_advance
- Else:
WaitMessage
Address ranges (pass 29 update)
| Range |
Subsystem |
Status |
| 0x004065xx–0x00408exx |
l2_eng text draw + UI line/panel primitives |
Mapped (pass 29a) |
| 0x0040dxxx–0x00413xxx |
Campaign map entity blit + LZSS compress |
Mapped (pass 29d) |
| 0x00414xxx–0x00416xxx |
UI widget toolkit (draw, hit-test, modals) |
Mapped (pass 29b) |
| 0x00417xxx–0x0041fxxx |
Screen-mode dialog draw / teardown |
Mapped (pass 31d; overview rows pass 33) |
| 0x00425xxxx |
Wizard step renderers (gateway wizard draw chain) |
Mapped (pass 39) |
| 0x0043bxxx–0x0043dxxx |
Wizard cluster + turn-action table |
Mapped (pass 29c + pass 38 + pass 51 semantic) |
| 0x004451xx–0x00458exx |
End-turn economy / diplomacy orchestration |
Mapped (pass 29e) |
| 0x0043axxx |
Screen scroll, click dispatch |
Mapped (pass 21–24) |
| 0x004448xx+ |
Net handlers |
Mapped (net/MODULE.md) |
| 0x00456xxx |
Economy tick player (lotr2_game_economy_tick_player) |
Mapped (pass 24) |
| 0x0048xxxx–0x004affff |
Battle, siege |
Mapped (combat/MODULE.md) |
| 0x004069xx–0x00406cxx |
Text input cursor/edit helpers |
Mapped (pass 33a) |
| 0x004092xx–0x004099xx |
Frame counters, LFSR RNG, distance |
Mapped (pass 33b) |
| 0x0040a9xx–0x0040b8xx |
Campaign map entity draw passes |
Mapped (pass 33c + pass 46) |
| 0x00417exx–0x0041a6xx |
Castle overview + mode 5 panel |
Mapped (pass 33d) |
| 0x00406xxx (early) |
Config/string parsers, CRT helpers |
Partial |
| 0x0043exxx–0x00444xxx |
Campaign UI / sync (turn finalize, battle exit, MP wrappers) |
Mapped (pass 35) |
| 0x004b0000–0x004bffff |
Turn sim / campaign AI (phase dispatch, 14-step loop) |
Mapped (pass 34) |
| 0x004d4000–0x004dafff |
Combat battle viewport / frame glue (shared with combat) |
Mapped (pass 43; see combat/MODULE.md) |
Dependencies
Data files
lords2.inf — install/save-path config (read at startup via save/MODULE.md)
l2_eng — English text table (loaded at startup)
l2.ini — paths and display settings
_sierra.ini — Sierra install path (resolved from l2.ini)
base01.256 … — palette/tile base assets (13 files)
base1a.pl8, misc_cty.pl8 — PL8 sprite sheets
startXXX.pl8 — start screen (verified in sim tick)
map01.pl8 — map tile sheets
l2_maps.dat — province map data
Skirmish mode (GoG only)
Single-battle flow without campaign map — documented in
../../skirmish/README.md. Retail LORDS2.EXE lacks
this mode.
| Symbol |
GoG addr |
Role |
FUN_004b7a9a |
0x004b7a9a |
SIGS init; GameType=Skirmish → DAT_0057cb38 |
FUN_00433155 |
0x00433155 |
Route MP to wizard 0xb vs campaign step 8 |
FUN_0042b7f7 |
0x0042b7f7 |
Set DAT_0057a0f0 skirmish-active, battle view |
FUN_004329ec |
0x004329ec |
Start skirmish battle (SP/MP) |
lotr2_game_ui_on_battle_end_return |
0x0043be65 |
Skirmish → score screen 0x2e vs campaign 0x13 |
lotr2_game_sim_tick |
0x00497a34 |
Skip map load when DAT_0057a0f0==1 |
Wizard steps 0xb (MP) / 0xc (SP); screen modes 0x1f setup, .// scores,
0x1c gateway return.
Open questions
- Remaining
lotr2_game_ui_draw_* cluster (~200 symbols) — pass 51c batch 1
lotr2_game_wizard_draw_* step naming — pass 51c batch 2
- Full 8-byte
lotr2_map_province_cell field layout (+0..+7)
- Confirm where
lotr2_gfx_dirty_left / right/top/bottom bounds are consumed
- Recover remaining game-state globals in
0x0055xxxx (see lotr2_game_state.md)
- Recover player-record struct at army stride
0x1a4 (lotr2_game_army_record_table base)
- Split early
0x4060 config/string parsers into dedicated submodule
- Defer
DAT_005076c0 / DAT_00505424 visibility globals until wndproc cluster
- Add provenance for multi-line Ghidra signatures (split
void __cdecl / name lines)