Skip to content

Map / province#

Purpose#

Strategic map rendering and province data: loading tile art from map01.pl8, province records from l2_maps.dat, building neighbor topology, placing starting units, and maintaining the 64×64 province cell table used during gameplay and sim-tick setup.

Key entry points#

Load pipeline#

Symbol Address Role
lotr2_map_load_new_game 0x00477580 New-game orchestrator: layers → dat → PL8 → neighbors → setup
lotr2_map_load_from_dat 0x00477610 Read l2_maps.dat slice (0x80c1 bytes) into province cell table
lotr2_map_clear_layer_province_lists 0x00433901 Zero 6×16 layer province-id lists before dat parse
lotr2_map_clear_province_slot_pairs 0x004b1e75 Zero 6 province slot pairs before dat parse
lotr2_map_append_province_to_layer 0x00433984 Append province id to layer list when cell flag 0x40
lotr2_map_set_province_slot_pair 0x004b1e40 Set province slot pair when cell flag 0x80
lotr2_map_load_pl8_tiles 0x0047a007 Count available map01.pl8 tile sheet variants
lotr2_map_load_pl8_province_tiles 0x00479f3d Load province PL8 tile slices (0x4000 bytes each)
lotr2_map_clear_province_layers 0x0047c48a Zero one of 8 parallel 64×64 province data layers
lotr2_map_clear_cell_highlights 0x0047de23 Clear highlight bit on all cells; mark viewport dirty
lotr2_map_paint_visible_grid 0x0047dac7 Paint visible 0x81×0x41 index grid from tile indices
lotr2_map_paint_sea_tiles 0x0047dc06 Set sea sentinel (0xfff0006) tiles in visible grid
lotr2_map_build_province_neighbors 0x00477bc4 Build DAT_0055c40c adjacency lists from grid scan
lotr2_map_setup_all_provinces 0x00478c6d Per-province castle, special tiles, keep, farm setup
lotr2_map_place_starting_units 0x004778d6 Difficulty-tier starting unit placement
lotr2_map_build_army_slot_table 0x0047d91d Map starting units into per-province army slot table

Grid / view#

Symbol Address Role
lotr2_map_init_province_grid 0x004346a1 Clear and fill 0x81×0x41 province index grid; set facing
lotr2_map_seed_grid_from_dat 0x0047781d Seed grid cell indices from l2_maps.dat offset 0x6000
lotr2_map_set_view_mode 0x0045c9ec Set map zoom/view scale (modes 0, 1, 2)
lotr2_map_clear_province_rect 0x0047a078 Clear province cells in a rectangular region
lotr2_map_clear_province_cell 0x0047a167 Clear one province grid cell
lotr2_map_stamp_tile_block 0x0047aaf4 Stamp N×N tile block on province grid
lotr2_map_set_cell_display 0x0047d6c6 Update cell tile index and sprite char

Province topology#

Symbol Address Role
lotr2_map_find_castle_cell 0x00477eef Locate castle tile; store in DAT_0055c420
lotr2_map_find_keep_cell 0x0047803f Locate keep tile; store in DAT_0055c428
lotr2_map_collect_special_tiles 0x00478b5f Collect 0x10-flag special tiles into province table
lotr2_map_locate_province_structures 0x00478d7f Mark town/castle/capital cells on grid
lotr2_map_place_province_farm 0x00478f48 Place farm tile nearest to capital
lotr2_map_province_has_neighbor 0x00477d4a Test membership in province neighbor list
lotr2_map_provinces_are_neighbors 0x00477dce Bidirectional adjacency check
lotr2_map_province_has_hostile_neighbor 0x00477e4c Hostile-neighbor check for diplomacy

Runtime / armies#

Symbol Address Role
lotr2_map_draw_capital_tiles 0x0047818a Stamp capital marker tiles
lotr2_map_restore_province_tiles 0x00478267 Restore 2×2 province tile sprites
lotr2_map_clear_province_armies 0x0047833a Clear army units at province capital
lotr2_map_recount_all_armies 0x00479a6f Recount armies for all provinces
lotr2_map_count_province_armies 0x00479aab Tally army types into province counters
lotr2_map_apply_gold_upkeep 0x004794ad Apply gold upkeep from army counts
lotr2_map_apply_food_upkeep 0x00479578 Apply food upkeep from army counts
lotr2_map_clear_cell_with_upkeep 0x00479d79 Clear cell; adjust province gold/food counters

Campaign army movement (pass 30–31)#

Symbol Address Role
lotr2_map_tick_province_army_move 0x004752b0 Per-tick army step along path; resource transfer hooks
lotr2_map_advance_army_path_step 0x004758a0 Advance one path step; relocate tile link
lotr2_map_probe_army_step_direction 0x00475de6 Probe neighbor cell for direction 0–7
lotr2_map_classify_army_target_cell 0x0047618f Classify target tile (empty, battle, structure)
lotr2_map_pathfind_province_army 0x004766e9 Build path for active province army record
lotr2_map_find_nearest_hostile_army 0x0047690b Nearest hostile army by Manhattan distance
lotr2_map_find_nearest_friendly_army 0x00476a85 Nearest friendly army (diplomacy filter)
lotr2_map_spawn_province_army_at_cell 0x0047fb40 Allocate province army record on empty cell
lotr2_map_highlight_province_cells 0x0047dea7 Set highlight bit on all cells owned by province
lotr2_map_rect_has_passable_cell 0x0047d002 Rect scan: any cell passable (flags & 0xfd == 0)

Army-type dispatch table (PTR_LAB_004f1b28): lotr2_map_army_handler_tick_move, lotr2_map_army_handler_merge_arrive, lotr2_map_army_handler_trade_arrive, stubs.

Campaign army AI (pass 34)#

AI turn step 10 calls lotr2_map_ai_pathfind_player_armies; order dispatch uses lotr2_map_ai_dispatch_army_by_order_type on army opcode DAT_005643ea (2=move, 3=follow, 4=siege, 5=assault, 6=merge). See game/MODULE.md turn-sim section and game_renames_pass34.py.

Symbol Address Role
lotr2_map_ai_pathfind_player_armies 0x004bb461 Pathfind all active armies for one player
lotr2_map_ai_dispatch_army_by_order_type 0x004bb5a8 Dispatch move/follow/siege/assault/merge orders
lotr2_map_ai_order_move_to_target 0x004bb842 Move/attack order handler
lotr2_map_ai_set_army_destination 0x004bc1eb Set army waypoint from castle/capital scan
lotr2_map_ai_pick_nearest_attack_province 0x004b63c2 Pick attack province by distance
lotr2_map_ai_validate_army_merge_target 0x004b61ff Validate merge destination army

Build queue / fog (pass 30)#

Symbol Address Role
lotr2_map_advance_build_queue_slot 0x0047c1b0 Advance build-queue overlay slot
lotr2_map_paint_build_queue_rect 0x0047c375 Paint build-queue rect on overlay grid
lotr2_map_init_edge_visibility 0x0047ad96 Init fog-edge visibility counters
lotr2_map_update_visibility_north 0x0047b1b9 North-edge fog update

Runtime armies + conquest (pass 40)#

Symbol Address Role
lotr2_map_spawn_starting_armies_from_list 0x00432940 Place starting armies from DAT_00573bc0 layer table
lotr2_map_cell_has_nearest_flag_ring 0x00432a77 Cell probe: nearest flagged tile within 3 rings
lotr2_map_cell_has_passable_rect_ring 0x00432ae8 Cell probe: passable rect within 3 rings
lotr2_map_tick_province_army_pathfind 0x00432b59 Per-tick pathfind for type-3 province armies
lotr2_map_spawn_reinforcement_army 0x00433ae0 Spawn type-4 reinforcement at capital cell
lotr2_map_tick_conquest_army_pathfind 0x00433c49 Pathfind tick for type-4 conquest armies
lotr2_map_on_province_army_conquered 0x00433ee6 Transfer loot; economy/diplomacy; release army slot
lotr2_map_build_layer_province_chains 0x004339e4 Collapse layer province lists into spawn table
lotr2_map_apply_population_growth 0x00433474 Apply gold-spend population tier growth

Map draw + viewport (pass 40)#

Symbol Address Role
lotr2_map_draw_cell_tile 0x00435263 Draw one 0x81×0x41 grid cell; dispatches lotr2_gfx_fill_iso_cell_rot* (9 variants on view rotation + edge clip)
lotr2_map_stamp_tile_pattern_small 0x0043500d 9-cell tile stamp composite
lotr2_map_stamp_tile_pattern_large 0x004350ec 11-cell tile stamp composite
lotr2_map_blit_map_sprite_scaled 0x004355d1 Blit map sprite from DAT_0059bfd0 sheet (scaled)
lotr2_map_find_cell_by_tile_index 0x004345e0 Reverse lookup tile index → grid coordinates (walks lotr2_map_tile_index_grid)
lotr2_map_hit_test_cell_from_cursor 0x00434982 Mouse hit-test on isometric map grid
lotr2_map_clamp_viewport_origin 0x004348fb Clamp scroll origin to map bounds
lotr2_map_rotate_grid_view_cw 0x00434cf0 Rotate province grid + adjust viewport
lotr2_map_rotate_grid_view_ccw 0x00434df9 Counter-clockwise grid rotation
lotr2_map_sync_cursor_to_cell_origin 0x00434f3d Sync pixel origin from last hit-test cell

Load pipeline#

flowchart TD
  loadFull[lotr2_map_load_new_game]
  clearLayers[lotr2_map_clear_province_layers]
  loadDat[lotr2_map_load_from_dat]
  clearHi[lotr2_map_clear_cell_highlights]
  paintVis[lotr2_map_paint_visible_grid]
  paintSea[lotr2_map_paint_sea_tiles]
  loadPl8[lotr2_map_load_pl8_province_tiles]
  neighbors[lotr2_map_build_province_neighbors]
  setupProv[lotr2_map_setup_all_provinces]
  startUnits[lotr2_map_place_starting_units]
  armySlots[lotr2_map_build_army_slot_table]

  loadFull --> clearLayers
  loadFull --> loadDat
  loadFull --> clearHi
  loadFull --> paintVis
  loadFull --> paintSea
  loadFull --> loadPl8
  loadFull --> neighbors
  loadFull --> setupProv
  loadFull --> startUnits
  loadFull --> armySlots

Dependencies#

Data files#

File Role
map01.pl8 Map tile sprite sheets (multiple variants)
l2_maps.dat Province layout records (0x80c1 bytes per province index)

Record layout: ../../spec/data/l2_maps_dat.md, struct ../../structs/lotr2_maps_dat_record.md.

Key globals (pass 51)#

Symbol Address Role
lotr2_map_tile_index_grid 0x5934e0 0x81×0x41 dword grid of province-cell indices for paint and hit-test
lotr2_map_province_cell 0x552180 64×64 province cell byte records (8-byte stride)

Open questions#

  • On-disk layout of l2_maps.dat loaded record — see ../../spec/data/l2_maps_dat.md
  • Province cell struct fields seeded from dat (+1..+4, +7) — see ../../structs/lotr2_game_state.md
  • Neighbor list format at DAT_0055c40c (16 entries per province)
  • Pass 30–31: army pathfind, build queue, fog, cell claim (0x7470x7fb4) — see symbol-map pass 30b/31
  • Pass 34: campaign army AI orders/pathfind (0x4b61ff0x4bc456) — see symbol-map pass 34e
  • Pass 40: map runtime armies, cell draw, viewport rotation (0x4329400x3578b) — see symbol-map pass 40
  • Province army record layout at lotr2_game_army_record_table (0x1a4 bytes per slot)