From f7ad8debee4dd6d51286747a15f22f909f7f5f65 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 12:19:27 -0500 Subject: [PATCH 01/20] fix(placement-plan): check the exit inventory and refuse failed placements Close the verifier and generator gaps deferred from #45 (issue #46). - build_plan.py publishes exit_inventory, every exit object read from the content. verify_plan.py derives the expected exit record copies from it (one per group a crossing exit touches, the overworld's own exits in internal_exits) and requires exactly those copies, each equal to its inventory entry. It re-derives the summary and per-group crossing counts, requires plane endpoints on every exit whose ends are placed, and compares every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) with its record or inventory entry. - Each stairwell replaces entry must equal its exit record: source tile, target map and tile, archetype, plane endpoints and direction. - A group whose placement fails stays unplaced: it leaves the placement order, is listed as unplaced and in the new summary.failed_placements, and is never retried. The verifier rejects a failed placement and any place or uncertain group without its placement, so the build does not publish such a plan. The view-radius sensitivity run lists its unplaced groups. - An overworld exit wired to another overworld map goes into the overworld's internal_exits; before, no list held a copy of it. - test_placement_plan.py: tampered copies of the committed plan for each check, and place_pool() with a failing group. No content checkout needed. Regenerated from content 3ad20150da76: placement-plan.json gains exit_inventory and summary.failed_placements, REPORT.md's check line and Reproducing section change, and the PNGs are unchanged. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/REPORT.md | 5 +- migration/placement-plan/placement-plan.json | 17401 +++++++++++++++- .../placement-plan/scripts/build_plan.py | 93 +- migration/placement-plan/scripts/report.py | 20 +- .../scripts/test_placement_plan.py | 341 + .../placement-plan/scripts/verify_plan.py | 243 +- 6 files changed, 18059 insertions(+), 44 deletions(-) create mode 100644 migration/placement-plan/scripts/test_placement_plan.py diff --git a/migration/placement-plan/REPORT.md b/migration/placement-plan/REPORT.md index 80e6bb4..afc6e4f 100644 --- a/migration/placement-plan/REPORT.md +++ b/migration/placement-plan/REPORT.md @@ -18,7 +18,7 @@ Version 2 applies the owner's decisions of 2026-10-18 on top of version 1 (PR #4 - Placement changes from version 1: `ellamin` layer offset -4 -> -5; `rockforge_a` layer offset -4 -> -5; `rockforge_b` layer offset -5 -> -6; `rockforge_c` layer offset -6 -> -7. - Why portals remain: 17 other-entrance-aligned; 1 slope-blocked; 1 stairwell-blocked. See [Portals that remain](#portals-that-remain). - View radius: **16 tiles** (the classic server's limit; the shipped client sees 10). With radius 10 the plan would have 65 stairs, 48 stairwell exits and 19 portals; see [View radius](#view-radius). -- Independent check (`scripts/verify_plan.py`, JSON only): **PASS**: 207 placed member maps, 7 nearby owner pairs measured (closest unrelated pair 22 tiles apart, gap rule needs more than 16), 40 stairwells with 76 intermediate tiles, 1456 exit endpoints re-derived; relayering re-derived from 242,824 overworld tiles and 82,133 tile heights (12 regions, 243 units, 47,594 moved tiles, 904 cut edges in 44 seam pieces). +- Independent check (`scripts/verify_plan.py`, JSON only): **PASS**: 207 placed member maps, 7 nearby owner pairs measured (closest unrelated pair 22 tiles apart, gap rule needs more than 16), 40 stairwells with 76 intermediate tiles, 1,007 exit record copies matched against the inventory of 1,200 exit objects, 1456 exit endpoints re-derived; relayering re-derived from 242,824 overworld tiles and 82,133 tile heights (12 regions, 243 units, 47,594 moved tiles, 904 cut edges in 44 seam pieces). | classification | reason | groups | maps | |---|---|---:|---:| @@ -882,9 +882,10 @@ Each image covers its regions and every storey or cave unit that moved because o cd migration/placement-plan python3 -I scripts/build_plan.py --content /home/zoey/dev/worktrees/content-plan-v2-input --out . python3 -I scripts/verify_plan.py placement-plan.json +python3 -I scripts/test_placement_plan.py ``` -`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. +`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of the committed JSON and checks that the verifier rejects each one; it needs no content checkout. The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content checkout; rerunning with the same arguments on the same content rewrites `placement-plan.json`, `REPORT.md` and the PNGs byte-for-byte. `content_git_head`, `content_worktree_clean` and `content_inputs_digest` (SHA-256 over every file under `arch/` and `maps/`, checked again after the run) in the JSON identify the input. Scripts: `mapdata.py` (read-only reader, tile links, occupied tiles, floors per sub-layer, `no_pass` tiles), `analysis.py` (components, layout, exit resolution), `relayer.py` (the relayering: regions, units, conflicts, seams), `footprint.py` (gap rule, column and sloped stairwell chains), `build_plan.py` (classification, relayering, placement, stairwells, wiring, JSON, staged publication), `report.py`, `render.py` and `raster.py` (images), and `verify_plan.py` (independent check of the JSON). Tunables (`VIEW_RADIUS`, `CONTACT_MARGIN`, `MAX_EXTRA_LAYERS`, `MAX_SHIFT`, `STAIR_TOLERANCE`, `STAIRWELL_MAX_BEND`, `LAYER_HEIGHT_Z` in `build_plan.py`; `MIN_REGION_TILES`, `WALKABLE_DZ`, `SEAM_CELL` in `relayer.py`) and the hand-written inputs (system maps, owner decisions, the Rockforge mines alignment hint, version 1's placements for the comparison) are constants. diff --git a/migration/placement-plan/placement-plan.json b/migration/placement-plan/placement-plan.json index ce7c767..6a1c0a4 100644 --- a/migration/placement-plan/placement-plan.json +++ b/migration/placement-plan/placement-plan.json @@ -97,7 +97,8 @@ "stairwell_ids": "SWn = stairwell built for a placed group's aligned exits, SLn = sloped stairwell, SRn = stairwell for an overworld exit whose ends the relayering put 2+ layers apart", "relayering": "overworld tiles are on their final layer everywhere in this plan (see relayering); occupied_rows_by_layer gives each map's tiles per final layer", "wired_exits": "an exit with form 'wired' led nowhere in legacy; it now targets the content on the layer above or below (see its 'wired' evidence)", - "exits_kept_as_decoration": "dangling exits the owner keeps as decoration (no exit): not wired, with the landing they would have had" + "exits_kept_as_decoration": "dangling exits the owner keeps as decoration (no exit): not wired, with the landing they would have had", + "exit_inventory": "every exit object read from the content (inventory exits excluded), keyed by source_map and line, with its legacy form and resolved target (none for an exit that leads nowhere); the exit records in the groups are checked against it" }, "summary": { "maps_total": 3629, @@ -187,7 +188,8 @@ "/shattered_islands/strakewood_island/rockforge/rockforge_c" ], "unplaced_place_groups": [], - "unplaced_uncertain_groups": [] + "unplaced_uncertain_groups": [], + "failed_placements": [] }, "start_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", "view_radius_sensitivity": { @@ -200,6 +202,7 @@ }, "stairwells": 38, "stairwell_sites_blocked": 1, + "groups_unplaced": [], "groups_placed_differently": [ "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_a", "/shattered_islands/incuna/ellamin", @@ -6554,6 +6557,17398 @@ } } ], + "exit_inventory": [ + { + "source_map": "/emergency", + "line": 17, + "source_tile": [ + 0, + 0 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 3, + 7 + ] + }, + { + "source_map": "/guilds/quick_wolves/guild", + "line": 171, + "source_tile": [ + 1, + 12 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/guilds/quick_wolves/storage", + "target_tile": [ + 19, + 23 + ] + }, + { + "source_map": "/guilds/quick_wolves/guild", + "line": 202, + "source_tile": [ + 1, + 15 + ], + "arch": "stairs_red_up", + "form": "explicit", + "target_map": "/guilds/quick_wolves/oracle", + "target_tile": [ + 10, + 2 + ] + }, + { + "source_map": "/guilds/quick_wolves/guild", + "line": 2078, + "source_tile": [ + 18, + 20 + ], + "arch": "stairs_wood_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/guilds/quick_wolves/guild", + "line": 2631, + "source_tile": [ + 22, + 12 + ], + "arch": "stairs_wood_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/guilds/quick_wolves/guild", + "line": 2681, + "source_tile": [ + 22, + 20 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_4_65", + "target_tile": [ + 12, + 6 + ] + }, + { + "source_map": "/guilds/quick_wolves/oracle", + "line": 830, + "source_tile": [ + 10, + 2 + ], + "arch": "stairs_red_down", + "form": "explicit", + "target_map": "/guilds/quick_wolves/guild", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/guilds/quick_wolves/storage", + "line": 3780, + "source_tile": [ + 19, + 23 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/guilds/quick_wolves/guild", + "target_tile": [ + 1, + 12 + ] + }, + { + "source_map": "/hall_of_dms", + "line": 629, + "source_tile": [ + 10, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/guilds/quick_wolves/oracle", + "target_tile": [ + 7, + 7 + ] + }, + { + "source_map": "/hall_of_dms", + "line": 748, + "source_tile": [ + 12, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/planes/creation/creation_0808", + "target_tile": [ + 11, + 12 + ] + }, + { + "source_map": "/hall_of_dms", + "line": 876, + "source_tile": [ + 14, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_1_68", + "target_tile": [ + 12, + 19 + ] + }, + { + "source_map": "/hall_of_dms", + "line": 995, + "source_tile": [ + 16, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_9_68", + "target_tile": [ + 16, + 11 + ] + }, + { + "source_map": "/hall_of_dms", + "line": 1114, + "source_tile": [ + 18, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_7_78", + "target_tile": [ + 11, + 19 + ] + }, + { + "source_map": "/planes/bone_cairn/bone_cairn_0_0", + "line": 1676, + "source_tile": [ + 9, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/planes/bone_cairn/bone_cairn_0_0", + "line": 1845, + "source_tile": [ + 10, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/planes/bone_cairn/bone_cairn_0_0", + "line": 1939, + "source_tile": [ + 10, + 17 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_3_3_-2", + "target_tile": [ + 1, + 2 + ] + }, + { + "source_map": "/planes/bone_cairn/bone_cairn_0_0", + "line": 2009, + "source_tile": [ + 11, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 427, + "source_tile": [ + 2, + 1 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/planes/creation/creation_0601", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 996, + "source_tile": [ + 6, + 1 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 13, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 1706, + "source_tile": [ + 12, + 1 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 2234, + "source_tile": [ + 16, + 1 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 16, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 2455, + "source_tile": [ + 17, + 13 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/planes/creation/creation_0601", + "target_tile": [ + 1, + 11 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 2736, + "source_tile": [ + 19, + 20 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/planes/creation/creation_0601", + "target_tile": [ + 19, + 6 + ] + }, + { + "source_map": "/planes/creation/creation_0501", + "line": 2916, + "source_tile": [ + 21, + 1 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 21, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0601", + "line": 304, + "source_tile": [ + 1, + 12 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 17, + 12 + ] + }, + { + "source_map": "/planes/creation/creation_0601", + "line": 376, + "source_tile": [ + 2, + 1 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/planes/creation/creation_0601", + "line": 2825, + "source_tile": [ + 20, + 6 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/planes/creation/creation_0501", + "target_tile": [ + 19, + 19 + ] + }, + { + "source_map": "/planes/creation/creation_0601", + "line": 2949, + "source_tile": [ + 21, + 1 + ], + "arch": "portal_azure", + "form": "same-map", + "target_map": "/planes/creation/creation_0601", + "target_tile": [ + 16, + 1 + ] + }, + { + "source_map": "/planes/creation/creation_0801", + "line": 1199, + "source_tile": [ + 7, + 1 + ], + "arch": "teleporter", + "form": "same-map", + "target_map": "/planes/creation/creation_0801", + "target_tile": [ + 1, + 4 + ] + }, + { + "source_map": "/planes/creation/creation_0801", + "line": 3227, + "source_tile": [ + 21, + 3 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/planes/creation/creation_0801", + "target_tile": [ + 18, + 5 + ] + }, + { + "source_map": "/planes/creation/creation_0801", + "line": 3241, + "source_tile": [ + 21, + 5 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/planes/creation/creation_0801", + "target_tile": [ + 18, + 5 + ] + }, + { + "source_map": "/planes/creation/creation_0801", + "line": 3257, + "source_tile": [ + 21, + 7 + ], + "arch": "teleporter", + "form": "same-map", + "target_map": "/planes/creation/creation_0801", + "target_tile": [ + 18, + 5 + ] + }, + { + "source_map": "/planes/creation/creation_0808", + "line": 1488, + "source_tile": [ + 11, + 11 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/hall_of_dms", + "target_tile": [ + 12, + 17 + ] + }, + { + "source_map": "/planes/creation/creation_0907", + "line": 999, + "source_tile": [ + 4, + 15 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/planes/creation/creation_0907", + "target_tile": [ + 4, + 21 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/desert_plane_0101", + "line": 2345, + "source_tile": [ + 23, + 0 + ], + "arch": "ladder_down2", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/unusual_garden", + "target_tile": [ + 15, + 10 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 787, + "source_tile": [ + 8, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 798, + "source_tile": [ + 8, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 809, + "source_tile": [ + 8, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 820, + "source_tile": [ + 8, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 831, + "source_tile": [ + 8, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 842, + "source_tile": [ + 8, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 853, + "source_tile": [ + 8, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 931, + "source_tile": [ + 9, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 962, + "source_tile": [ + 9, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1040, + "source_tile": [ + 10, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1071, + "source_tile": [ + 10, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1149, + "source_tile": [ + 11, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1187, + "source_tile": [ + 11, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1265, + "source_tile": [ + 12, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1296, + "source_tile": [ + 12, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1374, + "source_tile": [ + 13, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1405, + "source_tile": [ + 13, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1483, + "source_tile": [ + 14, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1494, + "source_tile": [ + 14, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1505, + "source_tile": [ + 14, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1516, + "source_tile": [ + 14, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1527, + "source_tile": [ + 14, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1538, + "source_tile": [ + 14, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "line": 1549, + "source_tile": [ + 14, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "line": 71, + "source_tile": [ + 1, + 2 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "target_tile": [ + 3, + 2 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", + "line": 295, + "source_tile": [ + 6, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/desert_plane_0101", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/unusual_garden", + "line": 1613, + "source_tile": [ + 12, + 15 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/world_12_59", + "target_tile": [ + 12, + 15 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "line": 1165, + "source_tile": [ + 8, + 1 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "target_tile": [ + 15, + 10 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "line": 1699, + "source_tile": [ + 11, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", + "target_tile": [ + 11, + 11 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "line": 2935, + "source_tile": [ + 19, + 16 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", + "target_tile": [ + 22, + 22 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "line": 3517, + "source_tile": [ + 23, + 5 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "target_tile": [ + 22, + 22 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", + "line": 441, + "source_tile": [ + 4, + 10 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_11_61", + "target_tile": [ + 10, + 10 + ] + }, + { + "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", + "line": 740, + "source_tile": [ + 7, + 1 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", + "target_tile": [ + 15, + 10 + ] + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", + "line": 605, + "source_tile": [ + 11, + 3 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_76", + "target_tile": [ + 13, + 21 + ] + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 64, + "source_tile": [ + 0, + 4 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 148, + "source_tile": [ + 1, + 0 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 206, + "source_tile": [ + 1, + 6 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_-7_76", + "target_tile": [ + 8, + 14 + ] + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 345, + "source_tile": [ + 2, + 6 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_-7_76", + "target_tile": [ + 8, + 14 + ] + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 595, + "source_tile": [ + 4, + 4 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", + "line": 638, + "source_tile": [ + 4, + 8 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_cheap", + "line": 79, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_expensive", + "line": 97, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_normal", + "line": 85, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/cave_morlarg", + "line": 2637, + "source_tile": [ + 22, + 4 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_11_79", + "target_tile": [ + 8, + 7 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/cave_morlarg", + "line": 2652, + "source_tile": [ + 22, + 5 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_11_79", + "target_tile": [ + 8, + 8 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 64, + "source_tile": [ + 1, + 1 + ], + "arch": "ladder_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 71, + "source_tile": [ + 1, + 1 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/world_8_78", + "target_tile": [ + 6, + 23 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 181, + "source_tile": [ + 3, + 3 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 537, + "source_tile": [ + 8, + 3 + ], + "arch": "ladder_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 732, + "source_tile": [ + 10, + 5 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "line": 1196, + "source_tile": [ + 15, + 5 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "target_tile": [ + 17, + 14 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "line": 175, + "source_tile": [ + 8, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", + "target_tile": [ + 37, + 16 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "line": 358, + "source_tile": [ + 14, + 13 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", + "target_tile": [ + 17, + 4 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "line": 641, + "source_tile": [ + 17, + 14 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "target_tile": [ + 15, + 5 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", + "line": 1017, + "source_tile": [ + 8, + 22 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_a", + "target_tile": [ + 21, + 2 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", + "line": 1092, + "source_tile": [ + 9, + 11 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "target_tile": [ + 14, + 13 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_a", + "line": 3226, + "source_tile": [ + 21, + 2 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", + "target_tile": [ + 8, + 22 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", + "line": 102, + "source_tile": [ + 2, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_8_78", + "target_tile": [ + 6, + 23 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", + "line": 560, + "source_tile": [ + 17, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_8_78", + "target_tile": [ + 6, + 23 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", + "line": 590, + "source_tile": [ + 17, + 4 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", + "target_tile": [ + 14, + 13 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", + "line": 136, + "source_tile": [ + 2, + 2 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", + "line": 263, + "source_tile": [ + 4, + 4 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/world_7_77", + "target_tile": [ + 18, + 21 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", + "line": 76, + "source_tile": [ + 1, + 1 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", + "line": 199, + "source_tile": [ + 3, + 3 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", + "line": 140, + "source_tile": [ + 2, + 2 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", + "target_tile": [ + 3, + 3 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", + "line": 267, + "source_tile": [ + 4, + 4 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", + "target_tile": [ + 3, + 3 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", + "line": 238, + "source_tile": [ + 3, + 3 + ], + "arch": "ladder_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", + "line": 327, + "source_tile": [ + 4, + 7 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", + "target_tile": [ + 4, + 6 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", + "line": 298, + "source_tile": [ + 2, + 22 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_14_71", + "target_tile": [ + 14, + 4 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", + "line": 444, + "source_tile": [ + 3, + 22 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_14_71", + "target_tile": [ + 15, + 4 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", + "line": 1503, + "source_tile": [ + 9, + 22 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_19_72", + "target_tile": [ + 0, + 4 + ] + }, + { + "source_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", + "line": 1666, + "source_tile": [ + 10, + 22 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_19_72", + "target_tile": [ + 1, + 4 + ] + }, + { + "source_map": "/shattered_islands/everlink/everlink_0102", + "line": 564, + "source_tile": [ + 5, + 7 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/everlink/everlink_0301", + "line": 3009, + "source_tile": [ + 20, + 12 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/everlink/everlink_0301", + "target_tile": [ + 20, + 12 + ] + }, + { + "source_map": "/shattered_islands/everlink/everlink_0301", + "line": 3027, + "source_tile": [ + 20, + 14 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/everlink/everlink_0301", + "target_tile": [ + 20, + 14 + ] + }, + { + "source_map": "/shattered_islands/everlink/everlink_0302", + "line": 1734, + "source_tile": [ + 15, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/everlink/everlink_0302", + "target_tile": [ + 15, + 11 + ] + }, + { + "source_map": "/shattered_islands/everlink/everlink_0302", + "line": 2052, + "source_tile": [ + 17, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/everlink/everlink_0302", + "target_tile": [ + 17, + 11 + ] + }, + { + "source_map": "/shattered_islands/incuna/apartments/beach_nook", + "line": 1295, + "source_tile": [ + 10, + 13 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/incuna/ellamin", + "line": 460, + "source_tile": [ + 3, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_4_83", + "target_tile": [ + 15, + 7 + ] + }, + { + "source_map": "/shattered_islands/incuna/ellamin", + "line": 737, + "source_tile": [ + 4, + 13 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_83", + "target_tile": [ + 13, + 12 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck", + "line": 284, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck", + "line": 301, + "source_tile": [ + 6, + 3 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", + "line": 272, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", + "line": 284, + "source_tile": [ + 6, + 3 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", + "line": 333, + "source_tile": [ + 2, + 7 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_70", + "target_tile": [ + 1, + 13 + ] + }, + { + "source_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", + "line": 465, + "source_tile": [ + 3, + 7 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_0_70", + "target_tile": [ + 1, + 13 + ] + }, + { + "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "line": 1477, + "source_tile": [ + 8, + 25 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_13_7", + "target_tile": [ + 12, + 8 + ] + }, + { + "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "line": 1654, + "source_tile": [ + 9, + 25 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_13_7", + "target_tile": [ + 13, + 8 + ] + }, + { + "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "line": 1904, + "source_tile": [ + 10, + 25 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_13_7", + "target_tile": [ + 14, + 8 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 122, + "source_tile": [ + 1, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/emergency", + "target_tile": [ + 0, + 0 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 165, + "source_tile": [ + 1, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_70", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 208, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_9_56", + "target_tile": [ + 17, + 17 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 624, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/emergency", + "target_tile": [ + 0, + 0 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 668, + "source_tile": [ + 6, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_78", + "target_tile": [ + 19, + 21 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 712, + "source_tile": [ + 6, + 18 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_2_74", + "target_tile": [ + 9, + 21 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 990, + "source_tile": [ + 12, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_9_42", + "target_tile": [ + 12, + 2 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 1033, + "source_tile": [ + 12, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_12_68", + "target_tile": [ + 20, + 12 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 1076, + "source_tile": [ + 12, + 18 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_9_41", + "target_tile": [ + 12, + 3 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 1492, + "source_tile": [ + 17, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_17_3", + "target_tile": [ + 11, + 18 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 1536, + "source_tile": [ + 17, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_14_64", + "target_tile": [ + 20, + 11 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_1", + "line": 1580, + "source_tile": [ + 17, + 18 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_12_68", + "target_tile": [ + 10, + 22 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_2", + "line": 96, + "source_tile": [ + 1, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_2_70", + "target_tile": [ + 5, + 12 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_2", + "line": 139, + "source_tile": [ + 1, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_15_4", + "target_tile": [ + 15, + 12 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_2", + "line": 481, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_51", + "target_tile": [ + 16, + 6 + ] + }, + { + "source_map": "/shattered_islands/ships/ship_2", + "line": 527, + "source_tile": [ + 6, + 10 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_18_3", + "target_tile": [ + 6, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/apartments/apartment_cheap", + "line": 59, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/apartments/apartment_expensive", + "line": 83, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/apartments/apartment_luxurious", + "line": 117, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/apartments/apartment_normal", + "line": 75, + "source_tile": [ + 1, + 1 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 191, + "source_tile": [ + 2, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 212, + "source_tile": [ + 2, + 27 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 309, + "source_tile": [ + 3, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 330, + "source_tile": [ + 3, + 27 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 367, + "source_tile": [ + 3, + 31 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 388, + "source_tile": [ + 3, + 32 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 409, + "source_tile": [ + 3, + 33 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 502, + "source_tile": [ + 4, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 523, + "source_tile": [ + 4, + 27 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 552, + "source_tile": [ + 4, + 30 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 569, + "source_tile": [ + 4, + 31 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 586, + "source_tile": [ + 4, + 32 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 607, + "source_tile": [ + 4, + 33 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 693, + "source_tile": [ + 5, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 714, + "source_tile": [ + 5, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 735, + "source_tile": [ + 5, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 780, + "source_tile": [ + 5, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 823, + "source_tile": [ + 5, + 30 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 840, + "source_tile": [ + 5, + 31 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 861, + "source_tile": [ + 5, + 32 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 882, + "source_tile": [ + 5, + 33 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 985, + "source_tile": [ + 6, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1006, + "source_tile": [ + 6, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1157, + "source_tile": [ + 6, + 30 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1178, + "source_tile": [ + 6, + 31 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1199, + "source_tile": [ + 6, + 32 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1292, + "source_tile": [ + 7, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1374, + "source_tile": [ + 7, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1391, + "source_tile": [ + 7, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1408, + "source_tile": [ + 7, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1618, + "source_tile": [ + 8, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1635, + "source_tile": [ + 8, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1674, + "source_tile": [ + 8, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1731, + "source_tile": [ + 8, + 34 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1752, + "source_tile": [ + 8, + 35 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1835, + "source_tile": [ + 9, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1856, + "source_tile": [ + 9, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1877, + "source_tile": [ + 9, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1918, + "source_tile": [ + 9, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1953, + "source_tile": [ + 9, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 1974, + "source_tile": [ + 9, + 27 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2015, + "source_tile": [ + 9, + 33 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2032, + "source_tile": [ + 9, + 34 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2049, + "source_tile": [ + 9, + 35 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2132, + "source_tile": [ + 10, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2149, + "source_tile": [ + 10, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2166, + "source_tile": [ + 10, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2183, + "source_tile": [ + 10, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2204, + "source_tile": [ + 10, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2255, + "source_tile": [ + 10, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2276, + "source_tile": [ + 10, + 27 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2317, + "source_tile": [ + 10, + 33 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2334, + "source_tile": [ + 10, + 34 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2351, + "source_tile": [ + 10, + 35 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2442, + "source_tile": [ + 11, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2459, + "source_tile": [ + 11, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2476, + "source_tile": [ + 11, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2497, + "source_tile": [ + 11, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2518, + "source_tile": [ + 11, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2581, + "source_tile": [ + 11, + 26 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2623, + "source_tile": [ + 11, + 30 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2799, + "source_tile": [ + 12, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2820, + "source_tile": [ + 12, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2841, + "source_tile": [ + 12, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2965, + "source_tile": [ + 12, + 30 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 2986, + "source_tile": [ + 12, + 31 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3332, + "source_tile": [ + 14, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3353, + "source_tile": [ + 14, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3506, + "source_tile": [ + 15, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3523, + "source_tile": [ + 15, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3544, + "source_tile": [ + 15, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3714, + "source_tile": [ + 16, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3731, + "source_tile": [ + 16, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3752, + "source_tile": [ + 16, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3799, + "source_tile": [ + 16, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 3820, + "source_tile": [ + 16, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4046, + "source_tile": [ + 17, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4067, + "source_tile": [ + 17, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4108, + "source_tile": [ + 17, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4125, + "source_tile": [ + 17, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4142, + "source_tile": [ + 17, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4358, + "source_tile": [ + 18, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 4615, + "source_tile": [ + 19, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 20, + 43 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "line": 5252, + "source_tile": [ + 21, + 43 + ], + "arch": "rope_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_66", + "target_tile": [ + 18, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", + "line": 2090, + "source_tile": [ + 11, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", + "target_tile": [ + 23, + 13 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", + "line": 3596, + "source_tile": [ + 19, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_4_66_-2", + "target_tile": [ + 0, + 12 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", + "line": 1777, + "source_tile": [ + 11, + 12 + ], + "arch": "portal_green", + "form": "automatic-link", + "target_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", + "target_tile": [ + 11, + 12 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", + "line": 2890, + "source_tile": [ + 19, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_4_66_-2", + "target_tile": [ + 0, + 12 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/dark_cave/dark_cave_0102", + "line": 165, + "source_tile": [ + 1, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_70", + "target_tile": [ + 6, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", + "line": 2999, + "source_tile": [ + 21, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_0_65", + "target_tile": [ + 17, + 10 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", + "line": 3112, + "source_tile": [ + 22, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_0_65", + "target_tile": [ + 18, + 10 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 1549, + "source_tile": [ + 8, + 12 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 1792, + "source_tile": [ + 9, + 12 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 1833, + "source_tile": [ + 9, + 17 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 1895, + "source_tile": [ + 9, + 22 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 2113, + "source_tile": [ + 10, + 22 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 2284, + "source_tile": [ + 11, + 17 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 2365, + "source_tile": [ + 11, + 27 + ], + "arch": "exit_green", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 2634, + "source_tile": [ + 12, + 22 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 2887, + "source_tile": [ + 13, + 22 + ], + "arch": "invis_exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", + "line": 11690, + "source_tile": [ + 46, + 26 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 351, + "source_tile": [ + 1, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 16, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 362, + "source_tile": [ + 1, + 20 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 16, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 415, + "source_tile": [ + 2, + 1 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 476, + "source_tile": [ + 2, + 7 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 541, + "source_tile": [ + 2, + 13 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 2557, + "source_tile": [ + 12, + 19 + ], + "arch": "stairs_red_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", + "target_tile": [ + 5, + 11 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 2619, + "source_tile": [ + 13, + 1 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 2688, + "source_tile": [ + 13, + 7 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 2757, + "source_tile": [ + 13, + 13 + ], + "arch": "ladder_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "line": 2806, + "source_tile": [ + 13, + 19 + ], + "arch": "stairs_red_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", + "target_tile": [ + 6, + 11 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", + "line": 149, + "source_tile": [ + 1, + 11 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", + "line": 518, + "source_tile": [ + 5, + 11 + ], + "arch": "stairs_red_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "target_tile": [ + 12, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", + "line": 649, + "source_tile": [ + 6, + 11 + ], + "arch": "stairs_red_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "target_tile": [ + 13, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "line": 181, + "source_tile": [ + 1, + 1 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_57", + "target_tile": [ + 7, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "line": 372, + "source_tile": [ + 2, + 1 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_57", + "target_tile": [ + 8, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "line": 503, + "source_tile": [ + 3, + 1 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_57", + "target_tile": [ + 9, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", + "line": 1505, + "source_tile": [ + 10, + 18 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", + "line": 1517, + "source_tile": [ + 10, + 19 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", + "line": 296, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", + "target_tile": [ + 10, + 18 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", + "line": 307, + "source_tile": [ + 1, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", + "target_tile": [ + 10, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", + "line": 2595, + "source_tile": [ + 17, + 2 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", + "target_tile": [ + 21, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", + "line": 3018, + "source_tile": [ + 20, + 1 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", + "target_tile": [ + 18, + 11 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0204", + "line": 778, + "source_tile": [ + 8, + 19 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/world_5_57_-2", + "target_tile": [ + 14, + 14 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 135, + "source_tile": [ + 6, + 7 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 6, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 154, + "source_tile": [ + 6, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 6, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 229, + "source_tile": [ + 7, + 7 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 7, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 242, + "source_tile": [ + 7, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 7, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 941, + "source_tile": [ + 11, + 21 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 22, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 954, + "source_tile": [ + 11, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 959, + "source_tile": [ + 11, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 22, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1148, + "source_tile": [ + 12, + 21 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 23, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1161, + "source_tile": [ + 12, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1166, + "source_tile": [ + 12, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 23, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1727, + "source_tile": [ + 16, + 7 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 16, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1746, + "source_tile": [ + 16, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 16, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1792, + "source_tile": [ + 17, + 7 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 17, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "line": 1805, + "source_tile": [ + 17, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 17, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 343, + "source_tile": [ + 6, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 348, + "source_tile": [ + 6, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 6, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 363, + "source_tile": [ + 6, + 8 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 6, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 506, + "source_tile": [ + 7, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 511, + "source_tile": [ + 7, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 7, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 526, + "source_tile": [ + 7, + 8 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 7, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 578, + "source_tile": [ + 7, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 18, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 591, + "source_tile": [ + 7, + 20 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 596, + "source_tile": [ + 7, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 18, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 608, + "source_tile": [ + 7, + 21 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 629, + "source_tile": [ + 7, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 722, + "source_tile": [ + 8, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 19, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 735, + "source_tile": [ + 8, + 20 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 740, + "source_tile": [ + 8, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-1_57", + "target_tile": [ + 19, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 759, + "source_tile": [ + 8, + 21 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 787, + "source_tile": [ + 8, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1412, + "source_tile": [ + 15, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_57", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1425, + "source_tile": [ + 15, + 20 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1430, + "source_tile": [ + 15, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_0_57", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1442, + "source_tile": [ + 15, + 21 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1470, + "source_tile": [ + 15, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1504, + "source_tile": [ + 16, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1509, + "source_tile": [ + 16, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 16, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1524, + "source_tile": [ + 16, + 8 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 16, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1576, + "source_tile": [ + 16, + 19 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_57", + "target_tile": [ + 3, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1589, + "source_tile": [ + 16, + 20 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1594, + "source_tile": [ + 16, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_0_57", + "target_tile": [ + 3, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1613, + "source_tile": [ + 16, + 21 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1641, + "source_tile": [ + 16, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1675, + "source_tile": [ + 17, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1680, + "source_tile": [ + 17, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 17, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "line": 1695, + "source_tile": [ + 17, + 8 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 17, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "line": 1397, + "source_tile": [ + 12, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "target_tile": [ + 12, + 15 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "line": 1408, + "source_tile": [ + 12, + 16 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "target_tile": [ + 12, + 15 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "line": 1527, + "source_tile": [ + 13, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "target_tile": [ + 13, + 15 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "line": 1538, + "source_tile": [ + 13, + 16 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "target_tile": [ + 13, + 15 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "line": 204, + "source_tile": [ + 2, + 5 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", + "target_tile": [ + 2, + 6 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "line": 217, + "source_tile": [ + 2, + 6 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", + "target_tile": [ + 2, + 6 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "line": 2704, + "source_tile": [ + 17, + 14 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "target_tile": [ + 17, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "line": 1104, + "source_tile": [ + 12, + 15 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "target_tile": [ + 12, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "line": 1117, + "source_tile": [ + 12, + 16 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "target_tile": [ + 12, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "line": 1209, + "source_tile": [ + 13, + 15 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "target_tile": [ + 13, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", + "line": 1222, + "source_tile": [ + 13, + 16 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", + "target_tile": [ + 13, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", + "line": 214, + "source_tile": [ + 2, + 5 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "target_tile": [ + 2, + 5 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", + "line": 228, + "source_tile": [ + 2, + 6 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", + "target_tile": [ + 2, + 5 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_0", + "line": 193, + "source_tile": [ + 1, + 1 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_65", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", + "line": 2972, + "source_tile": [ + 22, + 12 + ], + "arch": "portal_green", + "form": "explicit", + "target_map": "/shattered_islands/world_2_69", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1_-2", + "line": 3718, + "source_tile": [ + 22, + 21 + ], + "arch": "portal_yellow", + "form": "explicit", + "target_map": "/shattered_islands/world_6_67_-1", + "target_tile": [ + 7, + 9 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "line": 267, + "source_tile": [ + 4, + 1 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "target_tile": [ + 4, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "line": 275, + "source_tile": [ + 4, + 2 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "target_tile": [ + 4, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "line": 336, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "target_tile": [ + 4, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "line": 345, + "source_tile": [ + 5, + 2 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "target_tile": [ + 4, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "line": 414, + "source_tile": [ + 4, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "line": 423, + "source_tile": [ + 4, + 2 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "target_tile": [ + 5, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "line": 518, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", + "line": 527, + "source_tile": [ + 5, + 2 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", + "target_tile": [ + 5, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "line": 2656, + "source_tile": [ + 17, + 2 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "target_tile": [ + 18, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "line": 2664, + "source_tile": [ + 17, + 3 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "target_tile": [ + 18, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "line": 2803, + "source_tile": [ + 18, + 2 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "target_tile": [ + 18, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "line": 2811, + "source_tile": [ + 18, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "target_tile": [ + 18, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "line": 443, + "source_tile": [ + 2, + 20 + ], + "arch": "exit_green", + "form": "same-map", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "target_tile": [ + 11, + 20 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "line": 3605, + "source_tile": [ + 17, + 2 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "target_tile": [ + 17, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "line": 3613, + "source_tile": [ + 17, + 3 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "target_tile": [ + 17, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "line": 3751, + "source_tile": [ + 18, + 2 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "target_tile": [ + 17, + 2 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", + "line": 3759, + "source_tile": [ + 18, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", + "target_tile": [ + 17, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_3_3_-2", + "line": 33, + "source_tile": [ + 0, + 2 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/planes/bone_cairn/bone_cairn_0_0", + "target_tile": [ + 10, + 15 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "line": 1679, + "source_tile": [ + 13, + 16 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "target_tile": [ + 13, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "line": 1693, + "source_tile": [ + 13, + 17 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "target_tile": [ + 13, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "line": 1815, + "source_tile": [ + 14, + 16 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "target_tile": [ + 13, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "line": 1824, + "source_tile": [ + 14, + 17 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "target_tile": [ + 13, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "line": 1604, + "source_tile": [ + 13, + 16 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "target_tile": [ + 14, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "line": 1613, + "source_tile": [ + 13, + 17 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "target_tile": [ + 14, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "line": 1721, + "source_tile": [ + 14, + 16 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "target_tile": [ + 14, + 16 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", + "line": 1734, + "source_tile": [ + 14, + 17 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", + "target_tile": [ + 14, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_2_-1", + "line": 192, + "source_tile": [ + 2, + 3 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_67_-1", + "target_tile": [ + 7, + 17 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_3_-1", + "line": 247, + "source_tile": [ + 3, + 13 + ], + "arch": "exit_green", + "form": "same-map", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_3_-1", + "target_tile": [ + 4, + 8 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0", + "line": 1011, + "source_tile": [ + 7, + 7 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_7_67_-1", + "target_tile": [ + 16, + 9 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", + "line": 433, + "source_tile": [ + 4, + 2 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-3", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 311, + "source_tile": [ + 3, + 0 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 3, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 318, + "source_tile": [ + 3, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 3, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 379, + "source_tile": [ + 4, + 0 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 4, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 386, + "source_tile": [ + 4, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 4, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 446, + "source_tile": [ + 5, + 0 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 5, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "line": 453, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "target_tile": [ + 5, + 0 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 245, + "source_tile": [ + 3, + 0 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 253, + "source_tile": [ + 3, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 401, + "source_tile": [ + 4, + 0 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 4, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 412, + "source_tile": [ + 4, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 4, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 544, + "source_tile": [ + 5, + 0 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", + "line": 552, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", + "line": 293, + "source_tile": [ + 2, + 1 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", + "target_tile": [ + 2, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", + "line": 437, + "source_tile": [ + 3, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", + "target_tile": [ + 2, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", + "line": 377, + "source_tile": [ + 2, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", + "line": 509, + "source_tile": [ + 3, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-3", + "line": 2634, + "source_tile": [ + 17, + 21 + ], + "arch": "grave_cross2.111", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", + "target_tile": [ + 4, + 3 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "line": 66, + "source_tile": [ + 0, + 18 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "target_tile": [ + 0, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "line": 73, + "source_tile": [ + 0, + 19 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "target_tile": [ + 0, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "line": 172, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "line": 181, + "source_tile": [ + 1, + 19 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "line": 88, + "source_tile": [ + 0, + 18 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "target_tile": [ + 0, + 18 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "line": 95, + "source_tile": [ + 0, + 19 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "target_tile": [ + 0, + 18 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "line": 196, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "line": 205, + "source_tile": [ + 1, + 19 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_-10_54", + "line": 2097, + "source_tile": [ + 14, + 12 + ], + "arch": "hole", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_-10_54", + "line": 2207, + "source_tile": [ + 15, + 6 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-1_44", + "line": 1592, + "source_tile": [ + 10, + 6 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-1_44", + "line": 1607, + "source_tile": [ + 10, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-1_44", + "line": 1733, + "source_tile": [ + 11, + 6 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-1_44", + "line": 1748, + "source_tile": [ + 11, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 2800, + "source_tile": [ + 18, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 7, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 2811, + "source_tile": [ + 18, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 7, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 2950, + "source_tile": [ + 19, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 8, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 2961, + "source_tile": [ + 19, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 8, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 3405, + "source_tile": [ + 22, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 11, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 3416, + "source_tile": [ + 22, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 11, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 3547, + "source_tile": [ + 23, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 12, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_-1_57", + "line": 3558, + "source_tile": [ + 23, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", + "target_tile": [ + 12, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_-4_56_-1", + "line": 2495, + "source_tile": [ + 14, + 8 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/world_-4_56_-2", + "target_tile": [ + 14, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_-4_56_-2", + "line": 1537, + "source_tile": [ + 17, + 23 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-1", + "target_tile": [ + 12, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-1", + "line": 1648, + "source_tile": [ + 9, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-2", + "target_tile": [ + 9, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-1", + "line": 1666, + "source_tile": [ + 9, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-2", + "target_tile": [ + 9, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-1", + "line": 3712, + "source_tile": [ + 20, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-2", + "target_tile": [ + 20, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-1", + "line": 3730, + "source_tile": [ + 20, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-2", + "target_tile": [ + 20, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-2", + "line": 56, + "source_tile": [ + 8, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-1", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-2", + "line": 74, + "source_tile": [ + 8, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-1", + "target_tile": [ + 8, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-2", + "line": 1058, + "source_tile": [ + 21, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-1", + "target_tile": [ + 21, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_-5_56_-2", + "line": 1076, + "source_tile": [ + 21, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-5_56_-1", + "target_tile": [ + 21, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_-5_76", + "line": 2436, + "source_tile": [ + 12, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_-5_76", + "line": 2454, + "source_tile": [ + 12, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_-5_76", + "line": 2472, + "source_tile": [ + 12, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_-6_56_-1", + "line": 1066, + "source_tile": [ + 7, + 17 + ], + "arch": "hole", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_-6_58", + "line": 1201, + "source_tile": [ + 7, + 23 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-6_58_-1", + "target_tile": [ + 19, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_-6_58_-1", + "line": 3170, + "source_tile": [ + 19, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-6_58", + "target_tile": [ + 5, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_-6_58_-1", + "line": 3377, + "source_tile": [ + 20, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-6_58", + "target_tile": [ + 5, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_-6_79", + "line": 1139, + "source_tile": [ + 7, + 7 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/incuna/ship_lower_deck", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_-7_54", + "line": 1105, + "source_tile": [ + 7, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-7_54_-1", + "target_tile": [ + 8, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_-7_54_-1", + "line": 921, + "source_tile": [ + 7, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_-7_54", + "target_tile": [ + 8, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_-7_76", + "line": 1192, + "source_tile": [ + 8, + 14 + ], + "arch": "stairs_wood_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_-7_76", + "line": 1456, + "source_tile": [ + 9, + 18 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_-7_76", + "line": 1563, + "source_tile": [ + 10, + 11 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_-7_76", + "line": 1747, + "source_tile": [ + 11, + 12 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_51", + "line": 1411, + "source_tile": [ + 10, + 21 + ], + "arch": "ladder_tall_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_51_1", + "target_tile": [ + 10, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_51", + "line": 2485, + "source_tile": [ + 18, + 23 + ], + "arch": "ladder_tall_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_51_1", + "target_tile": [ + 18, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_0_51_1", + "line": 83, + "source_tile": [ + 10, + 21 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_51", + "target_tile": [ + 10, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_51_1", + "line": 174, + "source_tile": [ + 12, + 20 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_51_1", + "line": 286, + "source_tile": [ + 18, + 23 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_51", + "target_tile": [ + 18, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_0_51_1", + "line": 353, + "source_tile": [ + 20, + 22 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 790, + "source_tile": [ + 4, + 16 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 4, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 1231, + "source_tile": [ + 6, + 16 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 6, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 1788, + "source_tile": [ + 10, + 3 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52_-1", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 1839, + "source_tile": [ + 10, + 12 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52_1", + "target_tile": [ + 11, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 1948, + "source_tile": [ + 11, + 3 + ], + "arch": "stairs_down", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52_-1", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_0_52", + "line": 1999, + "source_tile": [ + 11, + 12 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52_1", + "target_tile": [ + 11, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_52_-1", + "line": 1212, + "source_tile": [ + 11, + 3 + ], + "arch": "stairs_wood_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_0_52_-1", + "line": 1228, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_0_52_1", + "line": 1473, + "source_tile": [ + 11, + 11 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 10, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_0_52_1", + "line": 1485, + "source_tile": [ + 11, + 12 + ], + "arch": "stairs_down", + "form": "explicit", + "target_map": "/shattered_islands/world_0_52", + "target_tile": [ + 10, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_0_57", + "line": 319, + "source_tile": [ + 2, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 15, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_0_57", + "line": 330, + "source_tile": [ + 2, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 15, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_0_57", + "line": 474, + "source_tile": [ + 3, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 16, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_0_57", + "line": 485, + "source_tile": [ + 3, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", + "target_tile": [ + 16, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_0_65", + "line": 2258, + "source_tile": [ + 17, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", + "target_tile": [ + 21, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 902, + "source_tile": [ + 6, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 919, + "source_tile": [ + 6, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 936, + "source_tile": [ + 6, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1055, + "source_tile": [ + 7, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1072, + "source_tile": [ + 7, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1085, + "source_tile": [ + 7, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1102, + "source_tile": [ + 7, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1119, + "source_tile": [ + 7, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1227, + "source_tile": [ + 8, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1240, + "source_tile": [ + 8, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1253, + "source_tile": [ + 8, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1266, + "source_tile": [ + 8, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1283, + "source_tile": [ + 8, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1300, + "source_tile": [ + 8, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1427, + "source_tile": [ + 9, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1444, + "source_tile": [ + 9, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1457, + "source_tile": [ + 9, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1470, + "source_tile": [ + 9, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1483, + "source_tile": [ + 9, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1500, + "source_tile": [ + 9, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1517, + "source_tile": [ + 9, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1640, + "source_tile": [ + 10, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1657, + "source_tile": [ + 10, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1670, + "source_tile": [ + 10, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1683, + "source_tile": [ + 10, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1696, + "source_tile": [ + 10, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1713, + "source_tile": [ + 10, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1730, + "source_tile": [ + 10, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1838, + "source_tile": [ + 11, + 11 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1855, + "source_tile": [ + 11, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1868, + "source_tile": [ + 11, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1881, + "source_tile": [ + 11, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1894, + "source_tile": [ + 11, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1911, + "source_tile": [ + 11, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 1928, + "source_tile": [ + 11, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2040, + "source_tile": [ + 12, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2057, + "source_tile": [ + 12, + 13 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2074, + "source_tile": [ + 12, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2087, + "source_tile": [ + 12, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2100, + "source_tile": [ + 12, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2117, + "source_tile": [ + 12, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2134, + "source_tile": [ + 12, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2249, + "source_tile": [ + 13, + 14 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2266, + "source_tile": [ + 13, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2279, + "source_tile": [ + 13, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2292, + "source_tile": [ + 13, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2309, + "source_tile": [ + 13, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2326, + "source_tile": [ + 13, + 19 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2455, + "source_tile": [ + 14, + 15 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2472, + "source_tile": [ + 14, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2485, + "source_tile": [ + 14, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2498, + "source_tile": [ + 14, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2515, + "source_tile": [ + 14, + 19 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2532, + "source_tile": [ + 14, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2656, + "source_tile": [ + 15, + 16 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2673, + "source_tile": [ + 15, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2686, + "source_tile": [ + 15, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2699, + "source_tile": [ + 15, + 19 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2716, + "source_tile": [ + 15, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2733, + "source_tile": [ + 15, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2863, + "source_tile": [ + 16, + 17 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2880, + "source_tile": [ + 16, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2893, + "source_tile": [ + 16, + 19 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2906, + "source_tile": [ + 16, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2919, + "source_tile": [ + 16, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 2936, + "source_tile": [ + 16, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3048, + "source_tile": [ + 17, + 18 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3065, + "source_tile": [ + 17, + 19 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3082, + "source_tile": [ + 17, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3095, + "source_tile": [ + 17, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3108, + "source_tile": [ + 17, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3130, + "source_tile": [ + 17, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3247, + "source_tile": [ + 18, + 20 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3264, + "source_tile": [ + 18, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3277, + "source_tile": [ + 18, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3294, + "source_tile": [ + 18, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3435, + "source_tile": [ + 19, + 21 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3452, + "source_tile": [ + 19, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3465, + "source_tile": [ + 19, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3615, + "source_tile": [ + 20, + 22 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3632, + "source_tile": [ + 20, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_66", + "line": 3764, + "source_tile": [ + 21, + 23 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 1386, + "source_tile": [ + 8, + 16 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67_1", + "target_tile": [ + 8, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 1400, + "source_tile": [ + 8, + 17 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67_1", + "target_tile": [ + 8, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 2866, + "source_tile": [ + 18, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3032, + "source_tile": [ + 19, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3048, + "source_tile": [ + 19, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3205, + "source_tile": [ + 20, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3221, + "source_tile": [ + 20, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3238, + "source_tile": [ + 20, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3255, + "source_tile": [ + 20, + 3 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3421, + "source_tile": [ + 21, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3433, + "source_tile": [ + 21, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3450, + "source_tile": [ + 21, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3467, + "source_tile": [ + 21, + 3 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3616, + "source_tile": [ + 22, + 0 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3632, + "source_tile": [ + 22, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67", + "line": 3649, + "source_tile": [ + 22, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", + "target_tile": [ + 21, + 43 + ] + }, + { + "source_map": "/shattered_islands/world_0_67_1", + "line": 98, + "source_tile": [ + 8, + 16 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67", + "target_tile": [ + 8, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_0_67_1", + "line": 107, + "source_tile": [ + 8, + 17 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67", + "target_tile": [ + 8, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_0_67_1", + "line": 821, + "source_tile": [ + 20, + 16 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67_2", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_67_2", + "line": 827, + "source_tile": [ + 20, + 16 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_67_1", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 629, + "source_tile": [ + 4, + 9 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 4, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 1510, + "source_tile": [ + 10, + 21 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_-1", + "target_tile": [ + 10, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 2348, + "source_tile": [ + 16, + 9 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 16, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 2357, + "source_tile": [ + 16, + 10 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 16, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 2521, + "source_tile": [ + 17, + 9 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 17, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 2530, + "source_tile": [ + 17, + 10 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 17, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 3257, + "source_tile": [ + 21, + 7 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 21, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 3303, + "source_tile": [ + 21, + 9 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 21, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 3339, + "source_tile": [ + 21, + 11 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 21, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_68", + "line": 3375, + "source_tile": [ + 21, + 13 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68_1", + "target_tile": [ + 21, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_-1", + "line": 676, + "source_tile": [ + 10, + 21 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 10, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 372, + "source_tile": [ + 15, + 13 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 438, + "source_tile": [ + 15, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 480, + "source_tile": [ + 16, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 16, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 489, + "source_tile": [ + 16, + 10 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 16, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 567, + "source_tile": [ + 17, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 17, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 576, + "source_tile": [ + 17, + 10 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_68", + "target_tile": [ + 17, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 636, + "source_tile": [ + 17, + 13 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 697, + "source_tile": [ + 17, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 907, + "source_tile": [ + 19, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 1108, + "source_tile": [ + 21, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 1321, + "source_tile": [ + 23, + 13 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_68_1", + "line": 1382, + "source_tile": [ + 23, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_0_69", + "line": 2534, + "source_tile": [ + 15, + 9 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_0_69", + "target_tile": [ + 15, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_0_69", + "line": 2550, + "source_tile": [ + 15, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_0_69", + "target_tile": [ + 15, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-1", + "line": 1158, + "source_tile": [ + 15, + 1 + ], + "arch": "sewer_hole2", + "form": "same-map", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 15, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-1", + "line": 1263, + "source_tile": [ + 15, + 11 + ], + "arch": "sewer_hole2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_69_-2", + "target_tile": [ + 15, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-1", + "line": 2449, + "source_tile": [ + 23, + 1 + ], + "arch": "sewer_hole2", + "form": "same-map", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 23, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-1", + "line": 2550, + "source_tile": [ + 23, + 11 + ], + "arch": "sewer_hole2", + "form": "same-map", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 23, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-2", + "line": 1598, + "source_tile": [ + 15, + 1 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 15, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-2", + "line": 1656, + "source_tile": [ + 15, + 11 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 15, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-2", + "line": 2650, + "source_tile": [ + 23, + 1 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 23, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_0_69_-2", + "line": 2716, + "source_tile": [ + 23, + 11 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_69_-1", + "target_tile": [ + 23, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_0_70", + "line": 255, + "source_tile": [ + 1, + 13 + ], + "arch": "stairs_wood_down", + "form": "same-map", + "target_map": "/shattered_islands/world_0_70", + "target_tile": [ + 1, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_0_70", + "line": 2835, + "source_tile": [ + 20, + 16 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 1, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_0_71_-2", + "line": 330, + "source_tile": [ + 1, + 21 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_71_-3", + "target_tile": [ + 1, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_0_71_-2", + "line": 339, + "source_tile": [ + 1, + 22 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_0_71_-3", + "target_tile": [ + 1, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_0_71_-3", + "line": 58, + "source_tile": [ + 1, + 21 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_0_71_-2", + "target_tile": [ + 1, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_71_-3", + "line": 73, + "source_tile": [ + 1, + 22 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_0_71_-2", + "target_tile": [ + 1, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_0_83", + "line": 1452, + "source_tile": [ + 8, + 16 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", + "target_tile": [ + 2, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_10_42_1", + "line": 163, + "source_tile": [ + 2, + 19 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_10_42_2", + "target_tile": [ + 2, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_10_42_1", + "line": 172, + "source_tile": [ + 2, + 20 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_10_42_2", + "target_tile": [ + 2, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_10_42_2", + "line": 168, + "source_tile": [ + 2, + 19 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_10_42_1", + "target_tile": [ + 2, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_10_42_2", + "line": 176, + "source_tile": [ + 2, + 20 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_10_42_1", + "target_tile": [ + 2, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_10_43", + "line": 2878, + "source_tile": [ + 19, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_10_43_-2", + "target_tile": [ + 19, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_10_43", + "line": 3024, + "source_tile": [ + 20, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_10_43_-2", + "target_tile": [ + 19, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_10_43_-2", + "line": 696, + "source_tile": [ + 19, + 2 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_10_43", + "target_tile": [ + 20, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_10_43_-2", + "line": 737, + "source_tile": [ + 20, + 2 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_10_43_-2", + "line": 742, + "source_tile": [ + 20, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_10_43", + "target_tile": [ + 20, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_10_44", + "line": 275, + "source_tile": [ + 1, + 20 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_10_44", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_10_44", + "line": 617, + "source_tile": [ + 3, + 20 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_10_44", + "target_tile": [ + 3, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_10_67", + "line": 221, + "source_tile": [ + 1, + 9 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_10_67", + "target_tile": [ + 1, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_10_67", + "line": 241, + "source_tile": [ + 1, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_10_67", + "target_tile": [ + 1, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_10_68", + "line": 19, + "source_tile": [ + 0, + 0 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_10_68", + "line": 232, + "source_tile": [ + 2, + 0 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 1, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_10_68", + "line": 1447, + "source_tile": [ + 11, + 11 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 5, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_10_69", + "line": 2016, + "source_tile": [ + 12, + 15 + ], + "arch": "exit", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_11_61", + "line": 668, + "source_tile": [ + 4, + 10 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", + "target_tile": [ + 4, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 232, + "source_tile": [ + 1, + 12 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 1, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 363, + "source_tile": [ + 2, + 7 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_11_67", + "target_tile": [ + 3, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 540, + "source_tile": [ + 3, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 673, + "source_tile": [ + 4, + 5 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_10_68", + "target_tile": [ + 12, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 1633, + "source_tile": [ + 10, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_10_68", + "target_tile": [ + 12, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 2589, + "source_tile": [ + 16, + 7 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_11_67", + "line": 2600, + "source_tile": [ + 16, + 8 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_11_68", + "line": 2745, + "source_tile": [ + 20, + 3 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_1", + "target_tile": [ + 20, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_11_68", + "line": 2940, + "source_tile": [ + 20, + 20 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_1", + "target_tile": [ + 20, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_1", + "line": 205, + "source_tile": [ + 20, + 3 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68", + "target_tile": [ + 20, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_1", + "line": 236, + "source_tile": [ + 20, + 7 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_2", + "target_tile": [ + 20, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_1", + "line": 289, + "source_tile": [ + 20, + 16 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_2", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_1", + "line": 320, + "source_tile": [ + 20, + 20 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68", + "target_tile": [ + 20, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_2", + "line": 273, + "source_tile": [ + 20, + 7 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_1", + "target_tile": [ + 20, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_11_68_2", + "line": 334, + "source_tile": [ + 20, + 16 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_11_68_1", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_11_79", + "line": 1130, + "source_tile": [ + 7, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/cave_morlarg", + "target_tile": [ + 21, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_11_79", + "line": 1147, + "source_tile": [ + 7, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/cave_morlarg", + "target_tile": [ + 21, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_12_4", + "line": 2058, + "source_tile": [ + 12, + 10 + ], + "arch": "stairs_wood_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_12_4", + "line": 2742, + "source_tile": [ + 15, + 22 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_12_4_1", + "target_tile": [ + 15, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_12_4", + "line": 2903, + "source_tile": [ + 16, + 22 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_12_4_1", + "target_tile": [ + 15, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_12_4_1", + "line": 694, + "source_tile": [ + 15, + 22 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_12_4", + "target_tile": [ + 16, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_12_4_1", + "line": 843, + "source_tile": [ + 16, + 22 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_12_4", + "target_tile": [ + 16, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_12_63", + "line": 1920, + "source_tile": [ + 15, + 13 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_12_63", + "target_tile": [ + 15, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_12_63", + "line": 1940, + "source_tile": [ + 15, + 15 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_12_63", + "target_tile": [ + 15, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_12_64", + "line": 2382, + "source_tile": [ + 19, + 7 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_12_68", + "line": 1327, + "source_tile": [ + 10, + 22 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 17, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_12_68", + "line": 2679, + "source_tile": [ + 20, + 12 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 12, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_13_6", + "line": 904, + "source_tile": [ + 5, + 0 + ], + "arch": "stairs_wood_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_13_6", + "line": 912, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_wood_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_13_62", + "line": 1148, + "source_tile": [ + 7, + 10 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_13_64", + "line": 885, + "source_tile": [ + 6, + 14 + ], + "arch": "hole", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_13_7", + "line": 1443, + "source_tile": [ + 12, + 8 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "target_tile": [ + 8, + 25 + ] + }, + { + "source_map": "/shattered_islands/world_13_7", + "line": 1563, + "source_tile": [ + 13, + 8 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "target_tile": [ + 9, + 25 + ] + }, + { + "source_map": "/shattered_islands/world_13_7", + "line": 1698, + "source_tile": [ + 14, + 8 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", + "target_tile": [ + 10, + 25 + ] + }, + { + "source_map": "/shattered_islands/world_14_5", + "line": 2530, + "source_tile": [ + 14, + 13 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_14_5_1", + "target_tile": [ + 14, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_14_5", + "line": 2539, + "source_tile": [ + 14, + 14 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_14_5_1", + "target_tile": [ + 14, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_14_5_1", + "line": 586, + "source_tile": [ + 14, + 10 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_14_5_1", + "target_tile": [ + 14, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_14_5_1", + "line": 602, + "source_tile": [ + 14, + 13 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_14_5", + "target_tile": [ + 14, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_14_5_1", + "line": 611, + "source_tile": [ + 14, + 14 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_14_5", + "target_tile": [ + 14, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_14_5_1", + "line": 769, + "source_tile": [ + 16, + 10 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_14_5_1", + "target_tile": [ + 16, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_14_64", + "line": 2380, + "source_tile": [ + 20, + 11 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 17, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_14_71", + "line": 1908, + "source_tile": [ + 14, + 4 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", + "target_tile": [ + 2, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_14_71", + "line": 2040, + "source_tile": [ + 15, + 4 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", + "target_tile": [ + 3, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_15_4", + "line": 2320, + "source_tile": [ + 15, + 12 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_2", + "target_tile": [ + 1, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_17_2", + "line": 1083, + "source_tile": [ + 5, + 5 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_2", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2", + "line": 1191, + "source_tile": [ + 5, + 17 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_5", + "target_tile": [ + 4, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2", + "line": 3384, + "source_tile": [ + 17, + 5 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_3", + "target_tile": [ + 19, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2", + "line": 3492, + "source_tile": [ + 17, + 17 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_4", + "target_tile": [ + 19, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_2", + "line": 700, + "source_tile": [ + 3, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2", + "target_tile": [ + 6, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_2", + "line": 872, + "source_tile": [ + 3, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_5", + "target_tile": [ + 4, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_2", + "line": 4061, + "source_tile": [ + 20, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_3", + "target_tile": [ + 19, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_2", + "line": 4233, + "source_tile": [ + 20, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_4", + "target_tile": [ + 19, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_3", + "line": 696, + "source_tile": [ + 3, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_2", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_3", + "line": 878, + "source_tile": [ + 3, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_5", + "target_tile": [ + 4, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_3", + "line": 4306, + "source_tile": [ + 20, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2", + "target_tile": [ + 16, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_3", + "line": 4488, + "source_tile": [ + 20, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_4", + "target_tile": [ + 19, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_4", + "line": 612, + "source_tile": [ + 3, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_2", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_4", + "line": 784, + "source_tile": [ + 3, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_5", + "target_tile": [ + 4, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_4", + "line": 2554, + "source_tile": [ + 13, + 5 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_19_4", + "target_tile": [ + 5, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_4", + "line": 4191, + "source_tile": [ + 20, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_3", + "target_tile": [ + 19, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_4", + "line": 4363, + "source_tile": [ + 20, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2", + "target_tile": [ + 16, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_5", + "line": 746, + "source_tile": [ + 3, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_2", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_5", + "line": 968, + "source_tile": [ + 3, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2", + "target_tile": [ + 6, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_5", + "line": 3969, + "source_tile": [ + 20, + 3 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_3", + "target_tile": [ + 19, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_17_2_5", + "line": 4119, + "source_tile": [ + 20, + 20 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_4", + "target_tile": [ + 19, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_17_3", + "line": 1601, + "source_tile": [ + 11, + 18 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 17, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_18_3", + "line": 899, + "source_tile": [ + 6, + 17 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_2", + "target_tile": [ + 6, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_18_4", + "line": 1958, + "source_tile": [ + 19, + 19 + ], + "arch": "stairs_stone_down", + "form": "dynamic" + }, + { + "source_map": "/shattered_islands/world_19_4", + "line": 773, + "source_tile": [ + 6, + 22 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_17_2_4", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_19_72", + "line": 40, + "source_tile": [ + 0, + 4 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", + "target_tile": [ + 9, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_19_72", + "line": 142, + "source_tile": [ + 1, + 4 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", + "target_tile": [ + 10, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_1_47", + "line": 2740, + "source_tile": [ + 23, + 15 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_47_-1", + "target_tile": [ + 23, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_1_47_-1", + "line": 2427, + "source_tile": [ + 23, + 15 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_47", + "target_tile": [ + 0, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_1_50", + "line": 2559, + "source_tile": [ + 19, + 23 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_51_-1", + "target_tile": [ + 19, + 0 + ] + }, + { + "source_map": "/shattered_islands/world_1_50_-1", + "line": 2305, + "source_tile": [ + 19, + 23 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_1_50", + "target_tile": [ + 19, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_1_51", + "line": 2218, + "source_tile": [ + 19, + 0 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_51_-1", + "target_tile": [ + 19, + 0 + ] + }, + { + "source_map": "/shattered_islands/world_1_51_-1", + "line": 2310, + "source_tile": [ + 19, + 0 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_50", + "target_tile": [ + 19, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_1_67", + "line": 2774, + "source_tile": [ + 16, + 16 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_1_67_1", + "target_tile": [ + 16, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_1_67", + "line": 2783, + "source_tile": [ + 16, + 17 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_67_1", + "target_tile": [ + 16, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_1_67_1", + "line": 1057, + "source_tile": [ + 16, + 16 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_67", + "target_tile": [ + 16, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_1_67_1", + "line": 1066, + "source_tile": [ + 16, + 17 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_67", + "target_tile": [ + 16, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 73, + "source_tile": [ + 0, + 7 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_1", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 103, + "source_tile": [ + 0, + 9 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_1", + "target_tile": [ + 0, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 133, + "source_tile": [ + 0, + 11 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_1", + "target_tile": [ + 0, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 163, + "source_tile": [ + 0, + 13 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_1", + "target_tile": [ + 0, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 193, + "source_tile": [ + 0, + 15 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_1", + "target_tile": [ + 0, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 766, + "source_tile": [ + 5, + 0 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_-1", + "target_tile": [ + 5, + 0 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 916, + "source_tile": [ + 5, + 21 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_-1", + "target_tile": [ + 5, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_1_68", + "line": 3015, + "source_tile": [ + 22, + 7 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/hall_of_dms", + "target_tile": [ + 5, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_1_68_-1", + "line": 491, + "source_tile": [ + 5, + 0 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68", + "target_tile": [ + 5, + 0 + ] + }, + { + "source_map": "/shattered_islands/world_1_68_-1", + "line": 2785, + "source_tile": [ + 23, + 5 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_-2", + "target_tile": [ + 23, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_1_68_-2", + "line": 994, + "source_tile": [ + 23, + 5 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_68_-1", + "target_tile": [ + 23, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_1_69", + "line": 801, + "source_tile": [ + 5, + 17 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_1_69_-1", + "target_tile": [ + 5, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_1_69_-1", + "line": 965, + "source_tile": [ + 5, + 17 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_69", + "target_tile": [ + 5, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_1_69_-1", + "line": 1683, + "source_tile": [ + 15, + 16 + ], + "arch": "sewer_hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_1_69_-2", + "line": 2029, + "source_tile": [ + 15, + 16 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_69_-1", + "target_tile": [ + 15, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_1_70_-2", + "line": 1674, + "source_tile": [ + 13, + 6 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_1_70_-3", + "target_tile": [ + 13, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_1_70_-3", + "line": 799, + "source_tile": [ + 13, + 6 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/world_1_70_-2", + "target_tile": [ + 13, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_1_71_-2", + "line": 1061, + "source_tile": [ + 7, + 11 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_71_-2", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_1_73", + "line": 2367, + "source_tile": [ + 10, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_1_73", + "line": 2384, + "source_tile": [ + 10, + 8 + ], + "arch": "stairs_stone_down", + "form": "dynamic" + }, + { + "source_map": "/shattered_islands/world_1_73", + "line": 4830, + "source_tile": [ + 22, + 13 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_73_-1", + "target_tile": [ + 22, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-1", + "line": 122, + "source_tile": [ + 1, + 20 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-1", + "line": 1274, + "source_tile": [ + 22, + 13 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_73", + "target_tile": [ + 22, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 146, + "source_tile": [ + 1, + 8 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 156, + "source_tile": [ + 1, + 9 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 202, + "source_tile": [ + 1, + 20 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_1_73_-1", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 309, + "source_tile": [ + 2, + 14 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 877, + "source_tile": [ + 6, + 10 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 903, + "source_tile": [ + 6, + 14 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 913, + "source_tile": [ + 6, + 15 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 943, + "source_tile": [ + 6, + 20 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 953, + "source_tile": [ + 6, + 21 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 996, + "source_tile": [ + 7, + 3 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1156, + "source_tile": [ + 8, + 3 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1370, + "source_tile": [ + 9, + 6 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1380, + "source_tile": [ + 9, + 7 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1422, + "source_tile": [ + 9, + 14 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1456, + "source_tile": [ + 9, + 21 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1638, + "source_tile": [ + 10, + 21 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1666, + "source_tile": [ + 11, + 1 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 1716, + "source_tile": [ + 11, + 10 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2108, + "source_tile": [ + 13, + 18 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2130, + "source_tile": [ + 13, + 21 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_4_73", + "target_tile": [ + 20, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2207, + "source_tile": [ + 14, + 8 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2233, + "source_tile": [ + 14, + 11 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2271, + "source_tile": [ + 14, + 18 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2339, + "source_tile": [ + 15, + 4 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2369, + "source_tile": [ + 15, + 8 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2395, + "source_tile": [ + 15, + 11 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 2518, + "source_tile": [ + 16, + 4 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 3058, + "source_tile": [ + 19, + 14 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 3068, + "source_tile": [ + 19, + 15 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_1_73_-2", + "line": 3541, + "source_tile": [ + 22, + 15 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 22, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_2_47", + "line": 93, + "source_tile": [ + 0, + 15 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_1_47_-1", + "target_tile": [ + 23, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_47", + "line": 2605, + "source_tile": [ + 17, + 4 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_2_47", + "line": 2642, + "source_tile": [ + 17, + 6 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_2_47", + "line": 3103, + "source_tile": [ + 20, + 4 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_2_47", + "line": 3140, + "source_tile": [ + 20, + 6 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_2_47_-1", + "line": 90, + "source_tile": [ + 0, + 15 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_47", + "target_tile": [ + 0, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_50_-1", + "line": 1262, + "source_tile": [ + 11, + 16 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_50_-2", + "target_tile": [ + 11, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_2_50_-1", + "line": 1271, + "source_tile": [ + 11, + 17 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_50_-2", + "target_tile": [ + 11, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_2_50_-2", + "line": 104, + "source_tile": [ + 11, + 16 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_50_-1", + "target_tile": [ + 11, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_2_50_-2", + "line": 117, + "source_tile": [ + 11, + 17 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_50_-1", + "target_tile": [ + 11, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-1", + "line": 742, + "source_tile": [ + 6, + 6 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 7, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-1", + "line": 864, + "source_tile": [ + 7, + 5 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 7, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-1", + "line": 885, + "source_tile": [ + 7, + 6 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 7, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1829, + "source_tile": [ + 21, + 4 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1852, + "source_tile": [ + 21, + 5 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1870, + "source_tile": [ + 21, + 6 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 20, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1900, + "source_tile": [ + 21, + 7 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 20, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1922, + "source_tile": [ + 21, + 8 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 20, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1950, + "source_tile": [ + 21, + 9 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 1973, + "source_tile": [ + 21, + 10 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 2179, + "source_tile": [ + 22, + 11 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 20, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_2_51_-2", + "line": 2201, + "source_tile": [ + 22, + 12 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_2_51_-2", + "target_tile": [ + 20, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-2", + "line": 250, + "source_tile": [ + 17, + 7 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-3", + "target_tile": [ + 12, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-2", + "line": 268, + "source_tile": [ + 17, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-3", + "target_tile": [ + 12, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-2", + "line": 286, + "source_tile": [ + 17, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-3", + "target_tile": [ + 12, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-3", + "line": 1234, + "source_tile": [ + 13, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-2", + "target_tile": [ + 18, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-3", + "line": 1253, + "source_tile": [ + 13, + 9 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-2", + "target_tile": [ + 18, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_2_52_-3", + "line": 1272, + "source_tile": [ + 13, + 10 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_2_52_-2", + "target_tile": [ + 18, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_53", + "line": 295, + "source_tile": [ + 2, + 3 + ], + "arch": "ladder_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_2_60", + "line": 955, + "source_tile": [ + 7, + 22 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60_-1", + "target_tile": [ + 7, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_60", + "line": 1638, + "source_tile": [ + 12, + 22 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60_-1", + "target_tile": [ + 12, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_60", + "line": 2334, + "source_tile": [ + 17, + 22 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60_-1", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_60_-1", + "line": 110, + "source_tile": [ + 7, + 22 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60", + "target_tile": [ + 7, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_60_-1", + "line": 342, + "source_tile": [ + 12, + 22 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60", + "target_tile": [ + 12, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_60_-1", + "line": 530, + "source_tile": [ + 17, + 22 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_60", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_2_61", + "line": 919, + "source_tile": [ + 6, + 15 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61_-1", + "target_tile": [ + 6, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_61", + "line": 1761, + "source_tile": [ + 11, + 13 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61_-1", + "target_tile": [ + 11, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_2_61", + "line": 2458, + "source_tile": [ + 16, + 14 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61_-1", + "target_tile": [ + 16, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_2_61_-1", + "line": 78, + "source_tile": [ + 6, + 15 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61", + "target_tile": [ + 6, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_61_-1", + "line": 387, + "source_tile": [ + 11, + 13 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61", + "target_tile": [ + 11, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_2_61_-1", + "line": 621, + "source_tile": [ + 16, + 14 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_61", + "target_tile": [ + 16, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_2_68", + "line": 280, + "source_tile": [ + 1, + 15 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_68", + "line": 289, + "source_tile": [ + 1, + 16 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_2_68", + "line": 1011, + "source_tile": [ + 5, + 9 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 6, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_68", + "line": 1199, + "source_tile": [ + 6, + 9 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 6, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_68", + "line": 2167, + "source_tile": [ + 12, + 21 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_-1", + "target_tile": [ + 12, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_-1", + "line": 1127, + "source_tile": [ + 12, + 21 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 12, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 159, + "source_tile": [ + 1, + 15 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 1, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 168, + "source_tile": [ + 1, + 16 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 1, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 607, + "source_tile": [ + 5, + 5 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_2", + "target_tile": [ + 5, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 627, + "source_tile": [ + 5, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 5, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 712, + "source_tile": [ + 5, + 21 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_2", + "target_tile": [ + 5, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 760, + "source_tile": [ + 6, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68", + "target_tile": [ + 5, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_1", + "line": 825, + "source_tile": [ + 6, + 21 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_2", + "target_tile": [ + 5, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_2", + "line": 499, + "source_tile": [ + 4, + 5 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_3", + "target_tile": [ + 4, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_2", + "line": 609, + "source_tile": [ + 5, + 5 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 5, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_2", + "line": 701, + "source_tile": [ + 5, + 21 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 6, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_2", + "line": 850, + "source_tile": [ + 6, + 21 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_1", + "target_tile": [ + 6, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_2_68_3", + "line": 282, + "source_tile": [ + 4, + 5 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_2_68_2", + "target_tile": [ + 4, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_2_69", + "line": 48, + "source_tile": [ + 0, + 6 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", + "target_tile": [ + 23, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_2_69", + "line": 58, + "source_tile": [ + 0, + 7 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_2_69_-1", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_2_69_-1", + "line": 61, + "source_tile": [ + 0, + 7 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_2_69", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_2_70", + "line": 789, + "source_tile": [ + 5, + 12 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_2", + "target_tile": [ + 1, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_2_74", + "line": 1365, + "source_tile": [ + 9, + 21 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 6, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_2_83", + "line": 2231, + "source_tile": [ + 16, + 12 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_2_87", + "line": 1138, + "source_tile": [ + 7, + 7 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_3_43", + "line": 374, + "source_tile": [ + 2, + 20 + ], + "arch": "ladder_down3", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_3_44", + "line": 1926, + "source_tile": [ + 9, + 18 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_44_-1", + "target_tile": [ + 9, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_3_44", + "line": 1937, + "source_tile": [ + 9, + 19 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_44_-1", + "target_tile": [ + 9, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_3_44_-1", + "line": 486, + "source_tile": [ + 9, + 18 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_3_44", + "target_tile": [ + 9, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_3_44_-1", + "line": 495, + "source_tile": [ + 9, + 19 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_3_44", + "target_tile": [ + 9, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_3_46", + "line": 2212, + "source_tile": [ + 16, + 14 + ], + "arch": "ladder_down3", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_3_56", + "line": 2326, + "source_tile": [ + 13, + 22 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/world_3_56_-2", + "target_tile": [ + 13, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_3_56_-2", + "line": 698, + "source_tile": [ + 13, + 22 + ], + "arch": "well_bucket", + "form": "explicit", + "target_map": "/shattered_islands/world_3_56", + "target_tile": [ + 13, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_3_59", + "line": 1504, + "source_tile": [ + 10, + 21 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_3_60", + "line": 2207, + "source_tile": [ + 17, + 5 + ], + "arch": "hole", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_3_66_-2", + "line": 4164, + "source_tile": [ + 23, + 12 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", + "target_tile": [ + 19, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_3_70", + "line": 648, + "source_tile": [ + 6, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/dark_cave/dark_cave_0102", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2076, + "source_tile": [ + 13, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 13, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2092, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 13, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2265, + "source_tile": [ + 14, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 14, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2277, + "source_tile": [ + 14, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 14, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2454, + "source_tile": [ + 15, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 15, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82", + "line": 2466, + "source_tile": [ + 15, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82_-1", + "target_tile": [ + 15, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 663, + "source_tile": [ + 13, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 673, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 773, + "source_tile": [ + 14, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 14, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 783, + "source_tile": [ + 14, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 14, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 862, + "source_tile": [ + 15, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 15, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_82_-1", + "line": 872, + "source_tile": [ + 15, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_3_82", + "target_tile": [ + 15, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_3_83", + "line": 2555, + "source_tile": [ + 15, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_83_-2", + "target_tile": [ + 15, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_3_83", + "line": 3068, + "source_tile": [ + 18, + 2 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_83_-2", + "target_tile": [ + 18, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_3_83_-2", + "line": 516, + "source_tile": [ + 14, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_83", + "target_tile": [ + 14, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_3_83_-2", + "line": 591, + "source_tile": [ + 14, + 9 + ], + "arch": "ladder_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_3_83_-2", + "line": 1289, + "source_tile": [ + 18, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_83", + "target_tile": [ + 18, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_3_83_-2", + "line": 1467, + "source_tile": [ + 19, + 1 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_3_83", + "target_tile": [ + 19, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_3_85_-1", + "line": 201, + "source_tile": [ + 8, + 8 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_85_-2", + "target_tile": [ + 8, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_3_85_-1", + "line": 210, + "source_tile": [ + 8, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_3_85_-2", + "target_tile": [ + 8, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_3_85_-2", + "line": 1279, + "source_tile": [ + 8, + 8 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_3_85_-1", + "target_tile": [ + 8, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_3_85_-2", + "line": 1288, + "source_tile": [ + 8, + 9 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_3_85_-1", + "target_tile": [ + 8, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_4_45", + "line": 3213, + "source_tile": [ + 18, + 4 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_4_45", + "target_tile": [ + 19, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_48", + "line": 3054, + "source_tile": [ + 22, + 11 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_48_1", + "target_tile": [ + 22, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_4_48", + "line": 3063, + "source_tile": [ + 22, + 12 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_48_1", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_4_48_1", + "line": 96, + "source_tile": [ + 22, + 11 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_48", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_4_48_1", + "line": 105, + "source_tile": [ + 22, + 12 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_48", + "target_tile": [ + 22, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_4_50", + "line": 800, + "source_tile": [ + 5, + 4 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_4_50", + "line": 837, + "source_tile": [ + 5, + 6 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_4_50", + "line": 1318, + "source_tile": [ + 8, + 4 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_4_50", + "line": 1355, + "source_tile": [ + 8, + 6 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_4_52", + "line": 1250, + "source_tile": [ + 8, + 12 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_4_52", + "line": 1613, + "source_tile": [ + 10, + 16 + ], + "arch": "ladder_tall_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_52_1", + "target_tile": [ + 9, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_4_52", + "line": 1626, + "source_tile": [ + 10, + 17 + ], + "arch": "ladder_tall_up", + "form": "explicit", + "target_map": "/shattered_islands/world_4_52_1", + "target_tile": [ + 9, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_52_1", + "line": 947, + "source_tile": [ + 10, + 15 + ], + "arch": "invis_exit", + "form": "tiled", + "target_map": "/shattered_islands/world_4_52", + "target_tile": [ + 10, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_4_52_1", + "line": 956, + "source_tile": [ + 10, + 16 + ], + "arch": "invis_exit", + "form": "tiled", + "target_map": "/shattered_islands/world_4_52", + "target_tile": [ + 10, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_4_52_1", + "line": 965, + "source_tile": [ + 10, + 17 + ], + "arch": "invis_exit", + "form": "tiled", + "target_map": "/shattered_islands/world_4_52", + "target_tile": [ + 10, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_52_1", + "line": 974, + "source_tile": [ + 10, + 18 + ], + "arch": "invis_exit", + "form": "tiled", + "target_map": "/shattered_islands/world_4_52", + "target_tile": [ + 10, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 634, + "source_tile": [ + 3, + 18 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 1, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 1879, + "source_tile": [ + 10, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 1891, + "source_tile": [ + 10, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 2065, + "source_tile": [ + 11, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 11, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 2077, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 11, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 2289, + "source_tile": [ + 12, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 12, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53", + "line": 2301, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 12, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 462, + "source_tile": [ + 4, + 21 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 5, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1202, + "source_tile": [ + 10, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 10, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1215, + "source_tile": [ + 10, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 10, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1350, + "source_tile": [ + 11, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1363, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1507, + "source_tile": [ + 12, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-1", + "line": 1520, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 144, + "source_tile": [ + 1, + 12 + ], + "arch": "well_bucket", + "form": "explicit", + "target_map": "/shattered_islands/world_4_53", + "target_tile": [ + 3, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 558, + "source_tile": [ + 4, + 21 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_4_53_-1", + "target_tile": [ + 4, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 951, + "source_tile": [ + 7, + 4 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 9, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 1045, + "source_tile": [ + 7, + 14 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 7, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 1090, + "source_tile": [ + 7, + 20 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 7, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_4_53_-2", + "line": 1132, + "source_tile": [ + 8, + 3 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_4_53_-2", + "target_tile": [ + 7, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_4_58", + "line": 2033, + "source_tile": [ + 13, + 10 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_4_58", + "line": 3402, + "source_tile": [ + 21, + 12 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_4_58", + "target_tile": [ + 20, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_4_59", + "line": 3269, + "source_tile": [ + 20, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_4_67", + "line": 1682, + "source_tile": [ + 12, + 4 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_67_-1", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_67", + "line": 1758, + "source_tile": [ + 12, + 17 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_67_-1", + "target_tile": [ + 12, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_67_-1", + "line": 1090, + "source_tile": [ + 7, + 17 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_2_-1", + "target_tile": [ + 2, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_4_67_-1", + "line": 1729, + "source_tile": [ + 12, + 4 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_67", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_4_67_-1", + "line": 1810, + "source_tile": [ + 12, + 17 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_67", + "target_tile": [ + 12, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_73", + "line": 2257, + "source_tile": [ + 19, + 13 + ], + "arch": "exit", + "form": "explicit", + "target_map": "/shattered_islands/world_1_73_-2", + "target_tile": [ + 14, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_4_83", + "line": 56, + "source_tile": [ + 0, + 7 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_83_-1", + "target_tile": [ + 1, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_83", + "line": 212, + "source_tile": [ + 1, + 7 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_83_-1", + "target_tile": [ + 1, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_83", + "line": 1148, + "source_tile": [ + 6, + 17 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/world_4_83_-2", + "target_tile": [ + 6, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_83", + "line": 2229, + "source_tile": [ + 13, + 12 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/incuna/ellamin", + "target_tile": [ + 4, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_4_83", + "line": 2550, + "source_tile": [ + 15, + 8 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/incuna/ellamin", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_4_83_-1", + "line": 37, + "source_tile": [ + 0, + 7 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_83", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_83_-1", + "line": 126, + "source_tile": [ + 1, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_83", + "target_tile": [ + 0, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_83_-2", + "line": 365, + "source_tile": [ + 7, + 18 + ], + "arch": "well_bucket", + "form": "explicit", + "target_map": "/shattered_islands/world_4_83", + "target_tile": [ + 6, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 292, + "source_tile": [ + 1, + 15 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_1", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 1229, + "source_tile": [ + 6, + 14 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 7, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 1457, + "source_tile": [ + 7, + 14 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 7, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 1812, + "source_tile": [ + 9, + 7 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 9, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 1821, + "source_tile": [ + 9, + 8 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 9, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 1999, + "source_tile": [ + 10, + 7 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 10, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 2008, + "source_tile": [ + 10, + 8 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 10, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 3382, + "source_tile": [ + 17, + 18 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 10, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 4329, + "source_tile": [ + 22, + 10 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 23, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 4337, + "source_tile": [ + 22, + 11 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 23, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 4479, + "source_tile": [ + 23, + 10 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 23, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_4_84", + "line": 4487, + "source_tile": [ + 23, + 11 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 23, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 916, + "source_tile": [ + 6, + 14 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 6, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 1065, + "source_tile": [ + 7, + 14 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 6, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 1270, + "source_tile": [ + 9, + 7 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 9, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 1279, + "source_tile": [ + 9, + 8 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 9, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 1391, + "source_tile": [ + 10, + 7 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 10, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 1400, + "source_tile": [ + 10, + 8 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 10, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 2844, + "source_tile": [ + 20, + 5 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 20, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 3144, + "source_tile": [ + 22, + 5 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_4_84_-1", + "target_tile": [ + 22, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 3177, + "source_tile": [ + 22, + 10 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 22, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 3185, + "source_tile": [ + 22, + 11 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 22, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 3233, + "source_tile": [ + 23, + 10 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 22, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-1", + "line": 3241, + "source_tile": [ + 23, + 11 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 22, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_4_84_-2", + "line": 766, + "source_tile": [ + 16, + 6 + ], + "arch": "exit_green", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_4_84_1", + "line": 115, + "source_tile": [ + 1, + 15 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_4_84", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_4_85", + "line": 1130, + "source_tile": [ + 7, + 13 + ], + "arch": "stairs_wood_down", + "form": "same-map", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 7, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_4_85", + "line": 3052, + "source_tile": [ + 20, + 13 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_4_85_-1", + "target_tile": [ + 20, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_4_85_-1", + "line": 578, + "source_tile": [ + 20, + 13 + ], + "arch": "rope_up", + "form": "tiled", + "target_map": "/shattered_islands/world_4_85", + "target_tile": [ + 20, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_5_42", + "line": 3015, + "source_tile": [ + 18, + 14 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_5_42", + "line": 3054, + "source_tile": [ + 18, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_5_42", + "line": 3588, + "source_tile": [ + 21, + 14 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_5_42", + "line": 3627, + "source_tile": [ + 21, + 16 + ], + "arch": "ladder_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_5_43_1", + "line": 146, + "source_tile": [ + 19, + 15 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_43_2", + "target_tile": [ + 19, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_5_43_2", + "line": 228, + "source_tile": [ + 19, + 15 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_43_1", + "target_tile": [ + 19, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_5_44_1", + "line": 337, + "source_tile": [ + 19, + 18 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_44_2", + "target_tile": [ + 19, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_5_44_2", + "line": 354, + "source_tile": [ + 19, + 18 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_44_1", + "target_tile": [ + 19, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_5_45", + "line": 2351, + "source_tile": [ + 14, + 9 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_45_1", + "target_tile": [ + 15, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_45", + "line": 2544, + "source_tile": [ + 15, + 9 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_45_1", + "target_tile": [ + 15, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_45_1", + "line": 158, + "source_tile": [ + 14, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_45", + "target_tile": [ + 14, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_45_1", + "line": 237, + "source_tile": [ + 15, + 9 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_45", + "target_tile": [ + 14, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_46", + "line": 2999, + "source_tile": [ + 17, + 10 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_5_46", + "target_tile": [ + 17, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_5_46", + "line": 3388, + "source_tile": [ + 19, + 10 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_5_46", + "target_tile": [ + 19, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_5_48", + "line": 1119, + "source_tile": [ + 6, + 16 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48_-1", + "target_tile": [ + 6, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_5_48", + "line": 1131, + "source_tile": [ + 6, + 17 + ], + "arch": "stairs_down", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48_-1", + "target_tile": [ + 6, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_5_48", + "line": 3718, + "source_tile": [ + 22, + 22 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48_-1", + "target_tile": [ + 22, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_5_48_-1", + "line": 144, + "source_tile": [ + 6, + 16 + ], + "arch": "stairs_stone_up2", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48", + "target_tile": [ + 6, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_5_48_-1", + "line": 156, + "source_tile": [ + 6, + 17 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48", + "target_tile": [ + 6, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_5_48_-1", + "line": 906, + "source_tile": [ + 22, + 22 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_48", + "target_tile": [ + 22, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_5_57", + "line": 1429, + "source_tile": [ + 7, + 3 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_5_57", + "line": 1619, + "source_tile": [ + 8, + 3 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "target_tile": [ + 2, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_5_57", + "line": 1805, + "source_tile": [ + 9, + 3 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_5_57_-2", + "line": 708, + "source_tile": [ + 14, + 14 + ], + "arch": "well_bucket", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0204", + "target_tile": [ + 8, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_5_58", + "line": 570, + "source_tile": [ + 3, + 17 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_5_58", + "target_tile": [ + 3, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_5_58", + "line": 869, + "source_tile": [ + 5, + 17 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_5_58", + "target_tile": [ + 5, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_5_65", + "line": 308, + "source_tile": [ + 2, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_0", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_5_65", + "line": 1745, + "source_tile": [ + 11, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_65_1", + "target_tile": [ + 11, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_5_65_1", + "line": 1019, + "source_tile": [ + 11, + 2 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_65", + "target_tile": [ + 11, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_5_65_1", + "line": 1224, + "source_tile": [ + 13, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_65_2", + "target_tile": [ + 13, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_5_65_2", + "line": 869, + "source_tile": [ + 13, + 2 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_65_1", + "target_tile": [ + 13, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_5_67", + "line": 1083, + "source_tile": [ + 6, + 9 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_67_1", + "target_tile": [ + 6, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_67", + "line": 1258, + "source_tile": [ + 7, + 9 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_67_1", + "target_tile": [ + 6, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_67_1", + "line": 250, + "source_tile": [ + 6, + 9 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_67", + "target_tile": [ + 7, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_67_1", + "line": 309, + "source_tile": [ + 7, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_67", + "target_tile": [ + 7, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_5_68", + "line": 1378, + "source_tile": [ + 9, + 5 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_68_-1", + "target_tile": [ + 9, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_5_68", + "line": 1550, + "source_tile": [ + 10, + 5 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_68_-1", + "target_tile": [ + 10, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_5_68", + "line": 3377, + "source_tile": [ + 22, + 9 + ], + "arch": "well_stone", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_5_68_-1", + "line": 87, + "source_tile": [ + 9, + 4 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_68", + "target_tile": [ + 9, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_5_68_-1", + "line": 120, + "source_tile": [ + 10, + 4 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_68", + "target_tile": [ + 10, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_5_71", + "line": 1157, + "source_tile": [ + 8, + 15 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_6_72", + "target_tile": [ + 17, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 479, + "source_tile": [ + 2, + 7 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 2, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 488, + "source_tile": [ + 2, + 8 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 2, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 524, + "source_tile": [ + 2, + 13 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 2, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 532, + "source_tile": [ + 2, + 14 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 2, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 806, + "source_tile": [ + 3, + 7 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 3, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 815, + "source_tile": [ + 3, + 8 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 3, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 851, + "source_tile": [ + 3, + 13 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 3, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 859, + "source_tile": [ + 3, + 14 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-3", + "target_tile": [ + 3, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-2", + "line": 2093, + "source_tile": [ + 9, + 8 + ], + "arch": "ladder_tall_up", + "form": "explicit", + "target_map": "/shattered_islands/world_5_84", + "target_tile": [ + 9, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 113, + "source_tile": [ + 2, + 7 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 2, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 148, + "source_tile": [ + 2, + 8 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 2, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 303, + "source_tile": [ + 2, + 13 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 2, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 338, + "source_tile": [ + 2, + 14 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 2, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 389, + "source_tile": [ + 3, + 7 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 3, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 424, + "source_tile": [ + 3, + 8 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 3, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 579, + "source_tile": [ + 3, + 13 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 3, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_5_84_-3", + "line": 614, + "source_tile": [ + 3, + 14 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_5_84_-2", + "target_tile": [ + 3, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_5_85_-1", + "line": 1523, + "source_tile": [ + 18, + 20 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_5_85_-2", + "target_tile": [ + 18, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_5_85_-2", + "line": 965, + "source_tile": [ + 18, + 20 + ], + "arch": "rope_up", + "form": "tiled", + "target_map": "/shattered_islands/world_5_85_-1", + "target_tile": [ + 18, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_6_42", + "line": 439, + "source_tile": [ + 2, + 17 + ], + "arch": "ladder_down", + "form": "tiled-unresolved" + }, + { + "source_map": "/shattered_islands/world_6_43_-1", + "line": 1168, + "source_tile": [ + 17, + 22 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_6_43_-2", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_6_43_-2", + "line": 396, + "source_tile": [ + 17, + 22 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_43_-1", + "target_tile": [ + 17, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 1205, + "source_tile": [ + 7, + 15 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44_1", + "target_tile": [ + 7, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 1351, + "source_tile": [ + 8, + 12 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44_1", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 2452, + "source_tile": [ + 14, + 12 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44_1", + "target_tile": [ + 14, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 2649, + "source_tile": [ + 15, + 15 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44_1", + "target_tile": [ + 15, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 3183, + "source_tile": [ + 18, + 13 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44_-1", + "target_tile": [ + 18, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_6_44", + "line": 3602, + "source_tile": [ + 21, + 2 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44_-1", + "target_tile": [ + 21, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_-1", + "line": 2831, + "source_tile": [ + 18, + 13 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 18, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_-1", + "line": 3220, + "source_tile": [ + 21, + 2 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 21, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_1", + "line": 793, + "source_tile": [ + 7, + 15 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 7, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_1", + "line": 859, + "source_tile": [ + 8, + 12 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_1", + "line": 1174, + "source_tile": [ + 11, + 15 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44_2", + "target_tile": [ + 11, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_1", + "line": 1610, + "source_tile": [ + 14, + 12 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 14, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_1", + "line": 1750, + "source_tile": [ + 15, + 15 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_44", + "target_tile": [ + 15, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_44_2", + "line": 1024, + "source_tile": [ + 11, + 15 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_6_44_1", + "target_tile": [ + 11, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_45", + "line": 446, + "source_tile": [ + 2, + 9 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_6_45_1", + "target_tile": [ + 2, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_6_45", + "line": 604, + "source_tile": [ + 3, + 9 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_45_1", + "target_tile": [ + 2, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_6_45_1", + "line": 171, + "source_tile": [ + 2, + 9 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_45", + "target_tile": [ + 3, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_6_45_1", + "line": 232, + "source_tile": [ + 3, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_45", + "target_tile": [ + 3, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 240, + "source_tile": [ + 1, + 1 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_6_46_-1", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 542, + "source_tile": [ + 2, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 2, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 941, + "source_tile": [ + 4, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 4, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 3569, + "source_tile": [ + 19, + 1 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 19, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 3631, + "source_tile": [ + 19, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 19, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 3921, + "source_tile": [ + 21, + 1 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 21, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_46", + "line": 4017, + "source_tile": [ + 21, + 11 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 21, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_6_46_-1", + "line": 49, + "source_tile": [ + 1, + 1 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_46", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_48", + "line": 452, + "source_tile": [ + 3, + 1 + ], + "arch": "ladder_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48_-1", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_48", + "line": 704, + "source_tile": [ + 4, + 12 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48_1", + "target_tile": [ + 4, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_48", + "line": 861, + "source_tile": [ + 5, + 12 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48_1", + "target_tile": [ + 4, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_48_-1", + "line": 370, + "source_tile": [ + 3, + 1 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48", + "target_tile": [ + 3, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_6_48_1", + "line": 492, + "source_tile": [ + 4, + 12 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48", + "target_tile": [ + 5, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_48_1", + "line": 590, + "source_tile": [ + 5, + 12 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_48", + "target_tile": [ + 5, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_6_51", + "line": 2019, + "source_tile": [ + 14, + 17 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_51_-1", + "target_tile": [ + 14, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_6_51", + "line": 2385, + "source_tile": [ + 15, + 17 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_6_51_-1", + "target_tile": [ + 14, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_6_51", + "line": 2481, + "source_tile": [ + 16, + 6 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_2", + "target_tile": [ + 6, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_6_51", + "line": 2948, + "source_tile": [ + 18, + 18 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/world_6_51_-2", + "target_tile": [ + 18, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-1", + "line": 332, + "source_tile": [ + 14, + 17 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_51", + "target_tile": [ + 15, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-1", + "line": 413, + "source_tile": [ + 15, + 17 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_6_51", + "target_tile": [ + 15, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-1", + "line": 748, + "source_tile": [ + 18, + 21 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_51_-1", + "target_tile": [ + 18, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-1", + "line": 997, + "source_tile": [ + 20, + 21 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_6_51_-1", + "target_tile": [ + 20, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-1", + "line": 1127, + "source_tile": [ + 22, + 11 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_6_51_-2", + "target_tile": [ + 22, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-2", + "line": 1450, + "source_tile": [ + 18, + 18 + ], + "arch": "well_bucket", + "form": "explicit", + "target_map": "/shattered_islands/world_6_51", + "target_tile": [ + 18, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_6_51_-2", + "line": 1508, + "source_tile": [ + 19, + 16 + ], + "arch": "invis_exit", + "form": "same-map", + "target_map": "/shattered_islands/world_6_51_-2", + "target_tile": [ + 18, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_6_54_-1", + "line": 96, + "source_tile": [ + 5, + 5 + ], + "arch": "hole2", + "form": "tiled", + "target_map": "/shattered_islands/world_6_54_-2", + "target_tile": [ + 5, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_6_54_-2", + "line": 352, + "source_tile": [ + 5, + 5 + ], + "arch": "rope_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_54_-1", + "target_tile": [ + 5, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_6_57", + "line": 1611, + "source_tile": [ + 9, + 14 + ], + "arch": "well_stone", + "form": "same-map", + "target_map": "/shattered_islands/world_6_57", + "target_tile": [ + 9, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_6_57_-1", + "line": 1598, + "source_tile": [ + 9, + 14 + ], + "arch": "well_bucket", + "form": "tiled", + "target_map": "/shattered_islands/world_6_57", + "target_tile": [ + 9, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_6_66", + "line": 1406, + "source_tile": [ + 18, + 15 + ], + "arch": "hole", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_6_66_-1", + "line": 1216, + "source_tile": [ + 18, + 15 + ], + "arch": "rope_up", + "form": "explicit", + "target_map": "/shattered_islands/world_6_66", + "target_tile": [ + 19, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_6_67", + "line": 653, + "source_tile": [ + 4, + 14 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_6_67", + "target_tile": [ + 4, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_6_67_-1", + "line": 253, + "source_tile": [ + 4, + 14 + ], + "arch": "rope_up", + "form": "tiled", + "target_map": "/shattered_islands/world_6_67", + "target_tile": [ + 4, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_6_67_-1", + "line": 665, + "source_tile": [ + 8, + 9 + ], + "arch": "portal_yellow", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1_-2", + "target_tile": [ + 22, + 22 + ] + }, + { + "source_map": "/shattered_islands/world_6_72", + "line": 998, + "source_tile": [ + 6, + 11 + ], + "arch": "portal_green", + "form": "explicit", + "target_map": "/shattered_islands/world_8_73", + "target_tile": [ + 11, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_6_78", + "line": 2073, + "source_tile": [ + 19, + 21 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 6, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_7_43_1", + "line": 96, + "source_tile": [ + 1, + 15 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_43_2", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_7_43_2", + "line": 93, + "source_tile": [ + 1, + 15 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_43_1", + "target_tile": [ + 1, + 15 + ] + }, + { + "source_map": "/shattered_islands/world_7_44", + "line": 2381, + "source_tile": [ + 13, + 13 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_7_44_1", + "target_tile": [ + 13, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_7_44_1", + "line": 227, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_spiral_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_44_2", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_7_44_1", + "line": 677, + "source_tile": [ + 13, + 12 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_7_44", + "target_tile": [ + 13, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_44_2", + "line": 183, + "source_tile": [ + 1, + 18 + ], + "arch": "stairs_spiral_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_44_1", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_7_45", + "line": 821, + "source_tile": [ + 4, + 9 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_45_1", + "target_tile": [ + 4, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_45", + "line": 1006, + "source_tile": [ + 5, + 9 + ], + "arch": "stairs_wood_up", + "form": "explicit", + "target_map": "/shattered_islands/world_7_45_1", + "target_tile": [ + 5, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_-1", + "line": 1160, + "source_tile": [ + 20, + 16 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_7_45_-1", + "target_tile": [ + 23, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_-1", + "line": 1253, + "source_tile": [ + 21, + 16 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_7_45_-1", + "target_tile": [ + 21, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_-1", + "line": 1425, + "source_tile": [ + 23, + 13 + ], + "arch": "hole", + "form": "same-map", + "target_map": "/shattered_islands/world_7_45_-1", + "target_tile": [ + 20, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_-1", + "line": 1457, + "source_tile": [ + 23, + 16 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_7_45_-1", + "target_tile": [ + 23, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_1", + "line": 363, + "source_tile": [ + 4, + 9 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_45", + "target_tile": [ + 5, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_45_1", + "line": 433, + "source_tile": [ + 5, + 9 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_45", + "target_tile": [ + 5, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_46", + "line": 1407, + "source_tile": [ + 6, + 13 + ], + "arch": "sewer_hole1", + "form": "tiled", + "target_map": "/shattered_islands/world_7_46_-1", + "target_tile": [ + 6, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_46", + "line": 2764, + "source_tile": [ + 14, + 5 + ], + "arch": "stairs_wood_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_7_46_-1", + "line": 385, + "source_tile": [ + 6, + 13 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_46", + "target_tile": [ + 6, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_47", + "line": 262, + "source_tile": [ + 1, + 19 + ], + "arch": "sewer_hole2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_47_-1", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_7_47", + "line": 1483, + "source_tile": [ + 9, + 9 + ], + "arch": "stairs_stone_down", + "form": "explicit", + "target_map": "/shattered_islands/world_7_47_-1", + "target_tile": [ + 9, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_47_-1", + "line": 279, + "source_tile": [ + 1, + 19 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_47", + "target_tile": [ + 1, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_7_47_-1", + "line": 1202, + "source_tile": [ + 9, + 3 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_47", + "target_tile": [ + 9, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_7_47_-1", + "line": 1240, + "source_tile": [ + 9, + 5 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_47", + "target_tile": [ + 9, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_7_47_-1", + "line": 1272, + "source_tile": [ + 9, + 9 + ], + "arch": "stairs_stone_up", + "form": "explicit", + "target_map": "/shattered_islands/world_7_47", + "target_tile": [ + 9, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_7_48", + "line": 497, + "source_tile": [ + 3, + 10 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_7_48_-1", + "target_tile": [ + 3, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_7_48_-1", + "line": 390, + "source_tile": [ + 3, + 10 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_48", + "target_tile": [ + 3, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_7_60", + "line": 2260, + "source_tile": [ + 17, + 8 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_7_60_-1", + "target_tile": [ + 17, + 8 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 2767, + "source_tile": [ + 18, + 7 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_1", + "target_tile": [ + 18, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 2948, + "source_tile": [ + 19, + 7 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_1", + "target_tile": [ + 18, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 3373, + "source_tile": [ + 21, + 13 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 21, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 3389, + "source_tile": [ + 21, + 14 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 21, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 3564, + "source_tile": [ + 22, + 13 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 22, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_64", + "line": 3573, + "source_tile": [ + 22, + 14 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 22, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1705, + "source_tile": [ + 20, + 7 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-2", + "target_tile": [ + 21, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1790, + "source_tile": [ + 21, + 7 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-2", + "target_tile": [ + 21, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1831, + "source_tile": [ + 21, + 13 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 21, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1840, + "source_tile": [ + 21, + 14 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 21, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1905, + "source_tile": [ + 22, + 13 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 22, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-1", + "line": 1914, + "source_tile": [ + 22, + 14 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 22, + 14 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-2", + "line": 1182, + "source_tile": [ + 20, + 7 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 20, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_-2", + "line": 1258, + "source_tile": [ + 21, + 7 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_-1", + "target_tile": [ + 20, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_1", + "line": 727, + "source_tile": [ + 14, + 7 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_2", + "target_tile": [ + 14, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_1", + "line": 804, + "source_tile": [ + 15, + 7 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_2", + "target_tile": [ + 14, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_1", + "line": 1037, + "source_tile": [ + 18, + 7 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 19, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_1", + "line": 1133, + "source_tile": [ + 19, + 7 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64", + "target_tile": [ + 19, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_2", + "line": 888, + "source_tile": [ + 14, + 7 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_1", + "target_tile": [ + 15, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_64_2", + "line": 985, + "source_tile": [ + 15, + 7 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_7_64_1", + "target_tile": [ + 15, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_65", + "line": 918, + "source_tile": [ + 8, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_7_65_-1", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_7_65", + "line": 1083, + "source_tile": [ + 9, + 12 + ], + "arch": "invis_exit", + "form": "explicit", + "target_map": "/shattered_islands/world_7_65_-1", + "target_tile": [ + 9, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_7_66_-1", + "line": 938, + "source_tile": [ + 13, + 15 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", + "target_tile": [ + 2, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_7_66_-1", + "line": 1146, + "source_tile": [ + 16, + 15 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", + "target_tile": [ + 2, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_7_66_-1", + "line": 1345, + "source_tile": [ + 19, + 15 + ], + "arch": "hole", + "form": "explicit", + "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", + "target_tile": [ + 4, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_7_68", + "line": 1752, + "source_tile": [ + 12, + 20 + ], + "arch": "hole2", + "form": "tiled", + "target_map": "/shattered_islands/world_7_68_-1", + "target_tile": [ + 12, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_7_68_-1", + "line": 127, + "source_tile": [ + 12, + 20 + ], + "arch": "rope_up", + "form": "tiled", + "target_map": "/shattered_islands/world_7_68", + "target_tile": [ + 12, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_7_71", + "line": 2430, + "source_tile": [ + 19, + 22 + ], + "arch": "exit_green", + "form": "explicit", + "target_map": "/shattered_islands/world_6_72", + "target_tile": [ + 17, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_7_77", + "line": 2287, + "source_tile": [ + 18, + 21 + ], + "arch": "ladder_up", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", + "target_tile": [ + 4, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_7_78", + "line": 1354, + "source_tile": [ + 9, + 7 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_7_78", + "target_tile": [ + 9, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_7_78", + "line": 1646, + "source_tile": [ + 11, + 7 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_7_78", + "target_tile": [ + 11, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-1", + "line": 361, + "source_tile": [ + 3, + 12 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45_-2", + "target_tile": [ + 3, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-1", + "line": 370, + "source_tile": [ + 3, + 13 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45_-2", + "target_tile": [ + 3, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-1", + "line": 959, + "source_tile": [ + 8, + 23 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45", + "target_tile": [ + 8, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-1", + "line": 1158, + "source_tile": [ + 10, + 20 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45", + "target_tile": [ + 10, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-1", + "line": 1221, + "source_tile": [ + 10, + 23 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45", + "target_tile": [ + 10, + 23 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-2", + "line": 137, + "source_tile": [ + 3, + 12 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45_-1", + "target_tile": [ + 3, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_8_45_-2", + "line": 146, + "source_tile": [ + 3, + 13 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_8_45_-1", + "target_tile": [ + 3, + 13 + ] + }, + { + "source_map": "/shattered_islands/world_8_46", + "line": 212, + "source_tile": [ + 1, + 5 + ], + "arch": "stairs_stone_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_8_46", + "line": 1649, + "source_tile": [ + 9, + 5 + ], + "arch": "stairs_stone_up", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_8_46_-1", + "line": 240, + "source_tile": [ + 6, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_46", + "target_tile": [ + 6, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_8_46_-1", + "line": 334, + "source_tile": [ + 8, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_46", + "target_tile": [ + 8, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_8_46_-1", + "line": 428, + "source_tile": [ + 10, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_46", + "target_tile": [ + 10, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_8_58", + "line": 1400, + "source_tile": [ + 7, + 11 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58_-1", + "target_tile": [ + 7, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_58", + "line": 1409, + "source_tile": [ + 7, + 12 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58_-1", + "target_tile": [ + 7, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_58", + "line": 1602, + "source_tile": [ + 8, + 11 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58_-1", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_58", + "line": 1611, + "source_tile": [ + 8, + 12 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58_-1", + "target_tile": [ + 8, + 12 + ] + }, + { + "source_map": "/shattered_islands/world_8_58_-1", + "line": 294, + "source_tile": [ + 7, + 11 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58", + "target_tile": [ + 7, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_8_58_-1", + "line": 307, + "source_tile": [ + 7, + 12 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58", + "target_tile": [ + 7, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_8_58_-1", + "line": 344, + "source_tile": [ + 8, + 11 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58", + "target_tile": [ + 8, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_8_58_-1", + "line": 357, + "source_tile": [ + 8, + 12 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_58", + "target_tile": [ + 8, + 11 + ] + }, + { + "source_map": "/shattered_islands/world_8_63", + "line": 3083, + "source_tile": [ + 21, + 3 + ], + "arch": "hole", + "form": "tiled", + "target_map": "/shattered_islands/world_8_63_-1", + "target_tile": [ + 21, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_8_63_-1", + "line": 68, + "source_tile": [ + 2, + 4 + ], + "arch": "hole2", + "form": "unresolved" + }, + { + "source_map": "/shattered_islands/world_8_63_-1", + "line": 1966, + "source_tile": [ + 21, + 3 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_63", + "target_tile": [ + 21, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_8_68", + "line": 2344, + "source_tile": [ + 17, + 6 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_8_68_-1", + "target_tile": [ + 17, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_8_68", + "line": 2493, + "source_tile": [ + 17, + 17 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_8_68_-1", + "target_tile": [ + 17, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_8_68_-1", + "line": 1363, + "source_tile": [ + 17, + 6 + ], + "arch": "ladder_tall_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_68", + "target_tile": [ + 17, + 6 + ] + }, + { + "source_map": "/shattered_islands/world_8_68_-1", + "line": 1446, + "source_tile": [ + 17, + 17 + ], + "arch": "ladder_tall_up", + "form": "tiled", + "target_map": "/shattered_islands/world_8_68", + "target_tile": [ + 17, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_8_78", + "line": 989, + "source_tile": [ + 6, + 23 + ], + "arch": "well_stone", + "form": "explicit", + "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", + "target_tile": [ + 1, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_8_79", + "line": 2081, + "source_tile": [ + 13, + 7 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_8_79", + "target_tile": [ + 13, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_8_79", + "line": 2097, + "source_tile": [ + 13, + 9 + ], + "arch": "shop_mat", + "form": "shop-mat", + "target_map": "/shattered_islands/world_8_79", + "target_tile": [ + 13, + 9 + ] + }, + { + "source_map": "/shattered_islands/world_9_41", + "line": 1499, + "source_tile": [ + 12, + 3 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 12, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_9_42", + "line": 1680, + "source_tile": [ + 12, + 2 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 12, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_42", + "line": 3221, + "source_tile": [ + 21, + 19 + ], + "arch": "stairs_wood_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42_1", + "target_tile": [ + 21, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_9_42", + "line": 3231, + "source_tile": [ + 21, + 20 + ], + "arch": "stairs_wood_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42_1", + "target_tile": [ + 21, + 19 + ] + }, + { + "source_map": "/shattered_islands/world_9_42_1", + "line": 112, + "source_tile": [ + 21, + 19 + ], + "arch": "stairs_wood_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42", + "target_tile": [ + 21, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_9_42_1", + "line": 121, + "source_tile": [ + 21, + 20 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42", + "target_tile": [ + 21, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_9_42_2", + "line": 234, + "source_tile": [ + 23, + 17 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42_3", + "target_tile": [ + 23, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_9_42_3", + "line": 207, + "source_tile": [ + 23, + 17 + ], + "arch": "ladder_down3", + "form": "tiled", + "target_map": "/shattered_islands/world_9_42_2", + "target_tile": [ + 23, + 17 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 1788, + "source_tile": [ + 9, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 1797, + "source_tile": [ + 9, + 2 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 1806, + "source_tile": [ + 9, + 3 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 2007, + "source_tile": [ + 10, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 2016, + "source_tile": [ + 10, + 2 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44", + "line": 2025, + "source_tile": [ + 10, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 9, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1355, + "source_tile": [ + 9, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1363, + "source_tile": [ + 9, + 2 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1371, + "source_tile": [ + 9, + 3 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1477, + "source_tile": [ + 10, + 1 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1491, + "source_tile": [ + 10, + 2 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1505, + "source_tile": [ + 10, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44", + "target_tile": [ + 10, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1660, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1669, + "source_tile": [ + 11, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1816, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1825, + "source_tile": [ + 12, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1948, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_1", + "line": 1957, + "source_tile": [ + 13, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 710, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 718, + "source_tile": [ + 5, + 2 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 726, + "source_tile": [ + 5, + 3 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 838, + "source_tile": [ + 6, + 1 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 846, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 854, + "source_tile": [ + 6, + 3 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 5, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 1696, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 11, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 1705, + "source_tile": [ + 11, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 1869, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 12, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 1878, + "source_tile": [ + 12, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 12, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 2020, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 13, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_2", + "line": 2029, + "source_tile": [ + 13, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_1", + "target_tile": [ + 13, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 790, + "source_tile": [ + 5, + 1 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 798, + "source_tile": [ + 5, + 2 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 806, + "source_tile": [ + 5, + 3 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 915, + "source_tile": [ + 6, + 1 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 1 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 930, + "source_tile": [ + 6, + 2 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 945, + "source_tile": [ + 6, + 3 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_2", + "target_tile": [ + 6, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 1776, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 1785, + "source_tile": [ + 11, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 11, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 1933, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 1942, + "source_tile": [ + 12, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 12, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 2073, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_up2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_3", + "line": 2082, + "source_tile": [ + 13, + 5 + ], + "arch": "stairs_stone_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_4", + "target_tile": [ + 13, + 4 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 1768, + "source_tile": [ + 11, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 1777, + "source_tile": [ + 11, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 11, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 1931, + "source_tile": [ + 12, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 12, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 1940, + "source_tile": [ + 12, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 12, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 2078, + "source_tile": [ + 13, + 4 + ], + "arch": "stairs_stone_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 13, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_44_4", + "line": 2087, + "source_tile": [ + 13, + 5 + ], + "arch": "stairs_down", + "form": "tiled", + "target_map": "/shattered_islands/world_9_44_3", + "target_tile": [ + 13, + 5 + ] + }, + { + "source_map": "/shattered_islands/world_9_56", + "line": 2546, + "source_tile": [ + 17, + 17 + ], + "arch": "stairs_wood_down", + "form": "explicit", + "target_map": "/shattered_islands/ships/ship_1", + "target_tile": [ + 1, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_9_65", + "line": 925, + "source_tile": [ + 6, + 10 + ], + "arch": "well_stone", + "form": "tiled", + "target_map": "/shattered_islands/world_9_65_-1", + "target_tile": [ + 6, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_9_65_-1", + "line": 860, + "source_tile": [ + 6, + 10 + ], + "arch": "well_bucket", + "form": "tiled", + "target_map": "/shattered_islands/world_9_65", + "target_tile": [ + 6, + 10 + ] + }, + { + "source_map": "/shattered_islands/world_9_65_-1", + "line": 1500, + "source_tile": [ + 11, + 18 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_9_65_-1", + "target_tile": [ + 11, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_9_65_-1", + "line": 1517, + "source_tile": [ + 11, + 19 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_9_65_-1", + "target_tile": [ + 11, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_9_65_-1", + "line": 1534, + "source_tile": [ + 11, + 20 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_9_65_-1", + "target_tile": [ + 11, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_9_65_-1", + "line": 1556, + "source_tile": [ + 11, + 21 + ], + "arch": "bollard_tied_s_2", + "form": "same-map", + "target_map": "/shattered_islands/world_9_65_-1", + "target_tile": [ + 11, + 18 + ] + }, + { + "source_map": "/shattered_islands/world_9_68", + "line": 229, + "source_tile": [ + 1, + 2 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_-1", + "target_tile": [ + 1, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_68", + "line": 238, + "source_tile": [ + 1, + 3 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_1", + "target_tile": [ + 1, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_68", + "line": 425, + "source_tile": [ + 1, + 20 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_1", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_9_68", + "line": 434, + "source_tile": [ + 1, + 21 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_-1", + "target_tile": [ + 1, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_-1", + "line": 174, + "source_tile": [ + 1, + 2 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68", + "target_tile": [ + 1, + 2 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_-1", + "line": 275, + "source_tile": [ + 1, + 21 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68", + "target_tile": [ + 1, + 21 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_1", + "line": 167, + "source_tile": [ + 1, + 3 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68", + "target_tile": [ + 1, + 3 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_1", + "line": 198, + "source_tile": [ + 1, + 7 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_2", + "target_tile": [ + 1, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_1", + "line": 251, + "source_tile": [ + 1, + 16 + ], + "arch": "ladder_up", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_2", + "target_tile": [ + 1, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_1", + "line": 282, + "source_tile": [ + 1, + 20 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68", + "target_tile": [ + 1, + 20 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_2", + "line": 273, + "source_tile": [ + 1, + 7 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_1", + "target_tile": [ + 1, + 7 + ] + }, + { + "source_map": "/shattered_islands/world_9_68_2", + "line": 334, + "source_tile": [ + 1, + 16 + ], + "arch": "ladder_down2", + "form": "tiled", + "target_map": "/shattered_islands/world_9_68_1", + "target_tile": [ + 1, + 16 + ] + }, + { + "source_map": "/shattered_islands/world_9_69", + "line": 2455, + "source_tile": [ + 17, + 13 + ], + "arch": "exit", + "form": "same-map", + "target_map": "/shattered_islands/world_9_69", + "target_tile": [ + 18, + 12 + ] + } + ], "groups": [ { "id": "/emergency", @@ -118269,6 +135664,8 @@ "stairwell_exits_required": 89, "stairwell_intermediate_tiles": 76, "exit_endpoints_checked": 1456, + "exit_inventory_objects": 1200, + "exit_record_copies_checked": 1007, "relayering": { "overworld_tiles": 242824, "tile_heights": 82133, diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index bcdecd5..26049f2 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -298,6 +298,43 @@ def occupied_rows(rec) -> list: return ["".join("#" if (x, y) in rec.occupied else "." for x in range(rec.width)) for y in range(rec.height)] +def placement_failed(res) -> bool: + return res is None or bool(res.get("failed")) + + +def place_pool(pool, connected, blockers, depth, place): + """Place the groups of `pool` one at a time, in reach order; return (order, failed, unplaced). + + connected(gid): gid has an exit to something already placed. blockers(gid, + remaining): unplaced groups that must be placed before gid. depth(gid): reach + depth, the first sort key. place(gid): the placement record, or None or + {"failed": True} when gid cannot be placed. A failed group stays unplaced: it + is not in `order`, it is never tried again, and nothing is placed through it, + so groups reachable only through it end up in `unplaced`. + """ + remaining = sorted(pool) + order, failed = [], [] + while remaining: + ready = [gid for gid in remaining if connected(gid)] + if not ready: + break + ok = [gid for gid in ready if not blockers(gid, set(remaining))] + pick = min(ok or ready, key=lambda gid: (depth(gid), gid)) + remaining.remove(pick) + (failed if placement_failed(place(pick)) else order).append(pick) + return order, failed, remaining + + +def inventory_entry(e: dict) -> dict: + """One exit object as read from the content, before placement or wiring.""" + rec = {"source_map": e["source_map"], "line": e["line"], "source_tile": [e["source_x"], e["source_y"]], + "arch": e["arch"], "form": e["form"]} + if e.get("resolved_map"): + rec["target_map"] = e["resolved_map"] + rec["target_tile"] = [e["resolved_x"], e["resolved_y"]] + return rec + + # --------------------------------------------------------------------- main def build(content: Path, allow_dirty: bool = False): revision = content_revision(content) @@ -1085,20 +1122,13 @@ def above_below_blockers(gid, pool): return blockers def run_pool(pool, tentative): - remaining = sorted(pool) - order = [] - while remaining: - ready = [gid for gid in remaining if connections_to_placed(gid)] - if not ready: - break - ok = [gid for gid in ready if not above_below_blockers(gid, set(remaining))] - pick_from = ok or ready - pick = min(pick_from, key=lambda gid: (G[gid]["reach_depth"] if G[gid]["reach_depth"] is not None else 99, gid)) - res = place_group(pick, tentative=tentative) - S.placements[pick] = res - order.append(pick) - remaining.remove(pick) - return order, remaining + def place(gid): + res = place_group(gid, tentative=tentative) + S.placements[gid] = {"failed": True, "ideal": None} if res is None else res + return res + + return place_pool(pool, lambda gid: bool(connections_to_placed(gid)), above_below_blockers, + lambda gid: G[gid]["reach_depth"] if G[gid]["reach_depth"] is not None else 99, place) ow_relay = relay[ow_id] ow_pieces = ow_relay.pieces # map -> final layer -> plane tiles @@ -1172,17 +1202,20 @@ def run_placement(radius): if ts: S.occ.add(m, L, functools.partial(_piece, m, L), bbox=fp.bbox_of(ts)) build_relayer_stairwells() - o1, l1 = run_pool(to_place, False) - o2, l2 = run_pool(tentative_ids, True) - o3 = [] + o1, f1, l1 = run_pool(to_place, False) + o2, f2, l2 = run_pool(tentative_ids, True) + o3, f3 = [], [] for gid, (anchor, dl, why) in sorted(TENTATIVE_ALIGN.items()): if gid in G and gid not in S.placed and anchor in S.placed: a = S.placed[anchor] res = place_group(gid, tentative=True, forced={"dx": a["dx"], "dy": a["dy"], "L": a["L"] + dl}) - if res: - res["tentative_reason"] = why - S.placements[gid] = res - o3.append(gid) + if placement_failed(res): + S.placements[gid] = {"failed": True, "ideal": (res or {}).get("ideal")} + f3.append(gid) + continue + res["tentative_reason"] = why + S.placements[gid] = res + o3.append(gid) # view conflicts against the final state (stairwell tiles count as their owner's tiles); # every stairwell is drawn only on the player's layer (owner decision 2026-10-18), so # the conflicts are listed but hidden in the engine. @@ -1209,7 +1242,8 @@ def run_placement(radius): for sw in S.stairwells: n[sw["kind"]] += 1 sw["id"] = "{}{}".format(prefix[sw["kind"]], n[sw["kind"]]) - return o1, l1, o2 + o3, l2 + # A failed group stays unplaced (and fails verification, so the plan is not published). + return o1, sorted(l1 + f1), o2 + o3, sorted(l2 + f2), sorted(f1 + f2 + f3) def becomes_counts(): c = collections.Counter() @@ -1305,7 +1339,7 @@ def exit_record(e, with_eval=True): return r # Sensitivity: the same placement with the shipped client's view radius. - run_placement(CLIENT_VIEW_RADIUS) + _o, sens_left1, _t, sens_left2, sens_failed = run_placement(CLIENT_VIEW_RADIUS) sens_placed = {gid: dict(p) for gid, p in S.placed.items()} sensitivity = { "view_radius_tiles": CLIENT_VIEW_RADIUS, @@ -1314,8 +1348,9 @@ def exit_record(e, with_eval=True): "crossing_exit_objects_after_placement": dict(sorted(becomes_counts().items())), "stairwells": len(S.stairwells), "stairwell_sites_blocked": len(S.blocked_sites), + "groups_unplaced": sorted(set(sens_left1) | set(sens_left2) | set(sens_failed)), } - order1, left1, order_t, left2 = run_placement(VIEW_RADIUS) + order1, left1, order_t, left2, failed_placements = run_placement(VIEW_RADIUS) place_index = {gid: i for i, gid in enumerate(S.order)} placements = S.placements sensitivity["groups_placed_differently"] = sorted( @@ -1427,6 +1462,9 @@ def tile_objects(m, x, y): e2 = dict(e, form="wired", resolved_map=tm, resolved_x=tx, resolved_y=ty, target_group=gof[tm]) e2["wired"] = evidence wired.append(e2) + if sg == gof[tm] == ow_id: + ow_internal.append(e2) # wired to another overworld map: an overworld exit, not a crossing + continue cross.append(e2) G[sg]["exits_out"].append(e2) G[gof[tm]]["entrances"].append(e2) @@ -1959,6 +1997,9 @@ def bbox_layers(gid, dx=0, dy=0, L=0): "layer above or below (see its 'wired' evidence)", "exits_kept_as_decoration": "dangling exits the owner keeps as decoration (no exit): not wired, with the " "landing they would have had", + "exit_inventory": "every exit object read from the content (inventory exits excluded), keyed by " + "source_map and line, with its legacy form and resolved target (none for an exit " + "that leads nowhere); the exit records in the groups are checked against it", }, "summary": {"maps_total": len(maps), "groups_total": len(groups), "non_overworld_groups": len(groups) - 1, "by_classification": dict(sorted(counts.items())), @@ -1980,7 +2021,8 @@ def bbox_layers(gid, dx=0, dy=0, L=0): "seams": len(seams_out), "new_stairs": len(new_stairs), "steep_steps": len(steep_out), "new_contacts": len(contacts_out)}, "placement_order": order1, "tentative_placement_order": order_t, - "unplaced_place_groups": left1, "unplaced_uncertain_groups": left2}, + "unplaced_place_groups": left1, "unplaced_uncertain_groups": left2, + "failed_placements": failed_placements}, "start_map": start_map, "view_radius_sensitivity": sensitivity, "stairwells": S.stairwells, @@ -1988,6 +2030,7 @@ def bbox_layers(gid, dx=0, dy=0, L=0): "sloped_stairwell_sites_blocked": S.slope_failed, "placement_changes_from_v1": changes_v1, "exits_kept_as_decoration": kept_decoration, + "exit_inventory": sorted((inventory_entry(e) for e in exits), key=lambda r: (r["source_map"], r["line"])), "groups": groups_out, "relayering": relayering, "data_problems": problems, diff --git a/migration/placement-plan/scripts/report.py b/migration/placement-plan/scripts/report.py index 5f5c93f..8bf94cb 100644 --- a/migration/placement-plan/scripts/report.py +++ b/migration/placement-plan/scripts/report.py @@ -197,11 +197,14 @@ def write_report(plan, out: Path): vr = ver.get("relayering", {}) w("- Independent check (`scripts/verify_plan.py`, JSON only): **{}**: {} placed member maps, {} nearby owner " "pairs measured (closest unrelated pair {} tiles apart, gap rule needs more than {}), {} stairwells with " - "{} intermediate tiles, {} exit endpoints re-derived; relayering re-derived from {} overworld tiles and {} " + "{} intermediate tiles, {} exit record copies matched against the inventory of {} exit objects, {} exit " + "endpoints re-derived; relayering re-derived from {} overworld tiles and {} " "tile heights ({} regions, {} units, {} moved tiles, {} cut edges in {} seam pieces).".format( "PASS" if ver.get("ok") else "FAIL", ver.get("member_maps"), ver.get("owner_pairs_checked"), (ver.get("closest_pairs", {}).get("unrelated") or ["-"])[0], R, ver.get("stairwells_checked"), - ver.get("stairwell_intermediate_tiles"), ver.get("exit_endpoints_checked"), num(vr.get("overworld_tiles", 0)), + ver.get("stairwell_intermediate_tiles"), num(ver.get("exit_record_copies_checked", 0)), + num(ver.get("exit_inventory_objects", 0)), ver.get("exit_endpoints_checked"), + num(vr.get("overworld_tiles", 0)), num(vr.get("tile_heights", 0)), vr.get("regions"), vr.get("units"), num(vr.get("moved_tiles", 0)), vr.get("cut_edges"), vr.get("seam_pieces"))) w("") @@ -284,9 +287,12 @@ def write_report(plan, out: Path): "(`client/src/client/commands.c:139-140`). That is **10 tiles** each way.") w("- Replacement stack: neither `{}` nor `{}` defines a view radius yet.".format(CLIENT, SERVER)) w("- **The plan uses {} tiles**, the server's bound. Comparison run with the shipped client's {} tiles " - "(`view_radius_sensitivity` in the JSON): {}; {} stairwells; groups that would sit elsewhere: {}.".format( + "(`view_radius_sensitivity` in the JSON): {}; {} stairwells; groups that would sit elsewhere: {}{}.".format( R, sens["view_radius_tiles"], fmt_counts(sens["crossing_exit_objects_after_placement"]), - sens["stairwells"], ", ".join("`{}`".format(base(x)) for x in sens["groups_placed_differently"]) or "none")) + sens["stairwells"], ", ".join("`{}`".format(base(x)) for x in sens["groups_placed_differently"]) or "none", + "; groups that could not be placed: {}".format(", ".join("`{}`".format(base(x)) for x in + sens["groups_unplaced"])) + if sens.get("groups_unplaced") else "")) w("- Other layers: classic also streams the linked levels from 6 below to 6 above " "(`MAP2_MAX_DEPTH 6`, `libatrinik/socket.h:87-89`), but levels hidden from the camera are pruned before " "they are sent (`server/src/server/los.c:737`); a level below the player is hidden by the floor of the " @@ -1090,6 +1096,7 @@ def write_reproducing(plan, w): w("cd migration/placement-plan") w("python3 -I scripts/build_plan.py --content {} --out .".format(plan["content_root"])) w("python3 -I scripts/verify_plan.py placement-plan.json") + w("python3 -I scripts/test_placement_plan.py") w("```") w("") w("`--content` must be a clean checkout of `atrinik/content` at `{}` (the shared checkout was behind `main`, " @@ -1097,7 +1104,10 @@ def write_reproducing(plan, w): "library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and " "`maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging " "directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the " - "PNGs replaced; a failed run leaves the old artifacts in place.".format(plan["content_git_head"][:12])) + "PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced " + "and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of " + "the committed JSON and checks that the verifier rejects each one; it needs no content checkout.".format( + plan["content_git_head"][:12])) w("") w("The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content " "checkout; rerunning with the same arguments on the same content rewrites `placement-plan.json`, " diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py new file mode 100644 index 0000000..f20104e --- /dev/null +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +"""Negative tests for the placement-plan verifier and generator (standard library only). + + python3 -I scripts/test_placement_plan.py + +Each verifier test tampers with a copy of the committed placement-plan.json +and checks that verify_plan.verify() rejects it with the expected error. The +generator tests drive build_plan.place_pool() with stand-in callbacks, so they +need no content checkout. +""" + +from __future__ import annotations + +import json +import sys +import unittest +from pathlib import Path + +sys.dont_write_bytecode = True +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import build_plan # noqa: E402 +import verify_plan # noqa: E402 + +PLAN = Path(__file__).resolve().parents[1] / "placement-plan.json" +INDEX = ("entrances_from_overworld", "exits_to_overworld", "entrances_from_other_groups", "exits_to_other_groups", + "internal_exits") +MINE = "/shattered_islands/eld_woods_island/clearhaven/mine/" +CROSSING = (MINE + "mine_entrance_a", 641) # ladder_up to mine_entrance: one copy in each group + + +def group(plan, gid): + return next(g for g in plan["groups"] if g["id"] == gid) + + +def copies(plan, key): + """(group, list name, record) for every copy of exit `key` in the exit index.""" + return [(g, lst, e) for g in plan["groups"] for lst in INDEX for e in g.get(lst, []) + if (e["source_map"], e["line"]) == key] + + +def delete_copies(plan, key, only_group=None): + for g, lst, e in copies(plan, key): + if only_group is None or g["id"] == only_group: + g[lst].remove(e) + + +class VerifierTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.text = PLAN.read_text(encoding="utf-8") + + def plan(self): + return json.loads(self.text) + + def assertCaught(self, plan, *needles): + res = verify_plan.verify(plan) + self.assertFalse(res["ok"], "tampered plan verified") + for needle in needles: + self.assertTrue(any(needle in e for e in res["errors"]), + "no error mentions {!r}; errors: {}".format(needle, res["errors"][:8])) + + +class CommittedPlan(VerifierTest): + def test_committed_plan_verifies(self): + res = verify_plan.verify(self.plan()) + self.assertTrue(res["ok"], res["errors"][:8]) + self.assertEqual(res["exit_inventory_objects"], len(self.plan()["exit_inventory"])) + + +class ExitInventory(VerifierTest): + """Fix 1: the exit records are checked against the complete source exit inventory.""" + + def test_deleting_every_copy_of_a_crossing_exit(self): + p = self.plan() + self.assertEqual(len(copies(p, CROSSING)), 2) + delete_copies(p, CROSSING) + self.assertCaught(p, "exit record(s) missing, e.g. exit {}:{}".format(*CROSSING), + "summary crossing exits", "crossing exits of {}".format(MINE + "mine_entrance_a")) + + def test_deleting_one_copy_of_a_crossing_exit(self): + p = self.plan() + delete_copies(p, CROSSING, only_group=MINE + "mine_entrance") + self.assertCaught(p, "exit record(s) missing, e.g. exit {}:{} from entrances_from_other_groups of {}".format( + CROSSING[0], CROSSING[1], MINE + "mine_entrance")) + + def test_deleting_an_overworld_entrance(self): + p = self.plan() + e = group(p, MINE + "mine_entrance")["entrances_from_overworld"][0] + delete_copies(p, (e["source_map"], e["line"])) + self.assertCaught(p, "exit record(s) missing, e.g. exit {}:{} from entrances_from_overworld".format( + e["source_map"], e["line"])) + + def test_deleting_an_overworld_exit(self): + p = self.plan() + ow = next(g for g in p["groups"] if g["classification"] == "overworld") + e = ow["internal_exits"].pop(0) + self.assertCaught(p, "exit record(s) missing, e.g. exit {}:{} from internal_exits".format( + e["source_map"], e["line"])) + + def test_deleting_an_exit_from_the_inventory_and_the_index(self): + p = self.plan() + p["exit_inventory"] = [x for x in p["exit_inventory"] if (x["source_map"], x["line"]) != CROSSING] + delete_copies(p, CROSSING) + self.assertCaught(p, "the exit inventory lists {} exit objects, the summary {}".format( + len(p["exit_inventory"]), len(p["exit_inventory"]) + 1)) + + def test_deleting_an_exit_from_the_inventory_only(self): + p = self.plan() + p["exit_inventory"] = [x for x in p["exit_inventory"] if (x["source_map"], x["line"]) != CROSSING] + p["summary"]["exit_objects"] -= 1 + self.assertCaught(p, "exit record(s) the exit inventory does not call for, e.g. exit {}:{}".format(*CROSSING)) + + def test_listing_an_exit_twice_in_the_inventory(self): + p = self.plan() + p["exit_inventory"].append(dict(p["exit_inventory"][0])) + p["summary"]["exit_objects"] += 1 + self.assertCaught(p, "is listed twice in the exit inventory") + + def test_a_copy_in_the_wrong_list(self): + p = self.plan() + g = group(p, MINE + "mine_entrance") + e = next(e for e in g["entrances_from_other_groups"] if (e["source_map"], e["line"]) == CROSSING) + g["entrances_from_other_groups"].remove(e) + g["entrances_from_overworld"].append(e) + self.assertCaught(p, "exit record(s) missing", "exit record(s) the exit inventory does not call for") + + def test_an_extra_copy(self): + p = self.plan() + e = copies(p, CROSSING)[0][2] + group(p, MINE + "mine_unique")["entrances_from_other_groups"].append(dict(e)) + self.assertCaught(p, "exit record(s) the exit inventory does not call for, e.g. exit {}:{}".format(*CROSSING)) + + def test_a_record_that_differs_from_the_inventory(self): + for field, value in (("source_tile", [0, 0]), ("target_tile", [0, 0]), ("arch", "hole"), + ("target_map", MINE + "mine_unique"), ("form", "tiled")): + with self.subTest(field=field): + p = self.plan() + for _g, _lst, e in copies(p, CROSSING): + e[field] = value + self.assertCaught(p, "record of exit {}:{} differs from its inventory entry in {}".format( + CROSSING[0], CROSSING[1], field)) + + def test_dropping_the_plane_endpoints_of_a_placed_exit(self): + p = self.plan() + for _g, _lst, e in copies(p, CROSSING): + for k in ("plane_source", "plane_target", "xy_distance", "layer_delta", "becomes"): + e.pop(k) + self.assertCaught(p, "exit {}:{} lacks plane endpoints".format(*CROSSING)) + + def test_a_wrong_overworld_plane_end(self): + p = self.plan() + e = group(p, MINE + "mine_entrance")["entrances_from_overworld"][0] + e["overworld_plane_source"] = [0, 0, 0] + self.assertCaught(p, "exit {}:{} overworld_plane_source [0, 0, 0]".format(e["source_map"], e["line"])) + + def test_summary_and_group_counts(self): + p = self.plan() + p["summary"]["crossing_exit_objects_after_placement"]["stairs"] += 1 + self.assertCaught(p, "summary crossing exits") + p = self.plan() + p["summary"]["crossing_exit_objects_into_separate_planes"] -= 1 + self.assertCaught(p, "summary crossing exits into separate planes") + p = self.plan() + p["summary"]["portal_reasons"]["slope-blocked"] += 1 + self.assertCaught(p, "summary portal reasons") + p = self.plan() + group(p, MINE + "mine_entrance_a")["placement"]["crossing_exits_after_placement"]["stairs"] -= 1 + self.assertCaught(p, "crossing exits of {}".format(MINE + "mine_entrance_a")) + + def test_deleting_a_wired_exit(self): + p = self.plan() + w = p["data_problems"]["dangling_exits_wired"][0] + delete_copies(p, (w["source_map"], w["line"])) + self.assertCaught(p, "exit record(s) missing, e.g. exit {}:{}".format(w["source_map"], w["line"]), + "wired exit {}:{} has no exit record".format(w["source_map"], w["line"])) + p = self.plan() + w = p["data_problems"]["dangling_exits_wired"].pop(0) + delete_copies(p, (w["source_map"], w["line"])) + self.assertCaught(p, "wired exits listed, the summary says") + + def test_a_wired_copy_that_differs(self): + p = self.plan() + p["data_problems"]["dangling_exits_wired"][0]["target_tile"] = [0, 0] + self.assertCaught(p, "differs from its inventory entry in target_tile") + + def test_a_main_connection_that_is_no_record(self): + p = self.plan() + group(p, MINE + "mine_entrance_a")["placement"]["main_connection"]["source_tile"] = [0, 0] + self.assertCaught(p, "main connection of {} is not one of its exit records".format(MINE + "mine_entrance_a")) + + def test_a_data_problem_that_differs_from_the_inventory(self): + p = self.plan() + p["data_problems"]["exits"][0]["source_tile"] = [99, 99] + self.assertCaught(p, "data problem of exit") + + +class StairwellReplaces(VerifierTest): + """Fix 2: every stairwell replaces entry equals its indexed exit record.""" + + def test_each_copied_field(self): + for field, value in (("source_tile", [0, 0]), ("target_map", "/shattered_islands/world_0_0"), + ("target_tile", [0, 0]), ("arch", "hole"), ("plane_source", [0, 0, 0]), + ("plane_target", [0, 0, 0])): + with self.subTest(field=field): + p = self.plan() + r = p["stairwells"][0]["replaces"][0] + r[field] = value + self.assertCaught(p, "SW1 replaces entry of exit {}:{} differs from its exit record in {}".format( + r["source_map"], r["line"], field)) + + def test_all_fields_at_once(self): + p = self.plan() + for r in p["stairwells"][0]["replaces"]: + r.update(source_tile=[0, 0], target_map="/shattered_islands/world_0_0", target_tile=[0, 0], + plane_source=[0, 0, 0], plane_target=[0, 0, 0]) + self.assertCaught(p, "SW1 replaces entry") + + def test_direction(self): + p = self.plan() + r = p["stairwells"][0]["replaces"][0] + r["goes"] = "up" if r["goes"] == "down" else "down" + self.assertCaught(p, "SW1 exit {}:{} goes {}, but its record goes".format(r["source_map"], r["line"], r["goes"])) + + def test_other_copies(self): + p = self.plan() + p["stairwell_sites_blocked"][0]["exits"][0]["source_tile"] = [0, 0] + self.assertCaught(p, "blocked column site entry of exit") + p = self.plan() + p["sloped_stairwell_sites_blocked"][0]["exits"][0]["arch"] = "hole" + self.assertCaught(p, "blocked sloped site entry of exit") + p = self.plan() + p["relayering"]["exits"][0]["target_tile"] = [0, 0] + self.assertCaught(p, "relayering exit of exit") + p = self.plan() + p["relayering"]["exits"][0]["crosses_to_group"] = "/emergency" + self.assertCaught(p, "crosses to /emergency") + + +class FailedPlacement(VerifierTest): + """Fix 3: a plan with a failed group does not verify, so it is not published.""" + + GID = "/shattered_islands/eld_woods_island/clearhaven/treehome_4" + + def fail(self, p, gid): + g = group(p, gid) + g["placement"] = {"status": "failed", "ideal": g["placement"]["ideal"]} + return g + + def test_failed_group_left_in_the_placement_order(self): + # what the generator used to publish: the failed group stayed in placement_order + p = self.plan() + self.fail(p, self.GID) + self.assertCaught(p, "placement of {} failed; a plan with a failed group must not be published".format( + self.GID), "place group {} has no proposed placement".format(self.GID)) + + def test_failed_group_kept_unplaced(self): + # what the generator writes now: unplaced and listed as failed; still refused + p = self.plan() + idx = group(p, self.GID)["placement"]["placement_index"] + self.fail(p, self.GID) + s = p["summary"] + s["placement_order"].remove(self.GID) + s["unplaced_place_groups"] = [self.GID] + s["failed_placements"] = [self.GID] + for g in p["groups"]: + pl = g.get("placement") or {} + if pl.get("placement_index") is not None and pl["placement_index"] > idx: + pl["placement_index"] -= 1 + res = verify_plan.verify(p) + self.assertFalse(res["ok"]) + self.assertIn("placement of {} failed; a plan with a failed group must not be published".format(self.GID), + res["errors"]) + self.assertFalse(any("summary unplaced" in e or "failed_placements" in e or "placement order" in e + for e in res["errors"]), res["errors"][:8]) + + def test_failed_tentative_group(self): + p = self.plan() + gid = p["summary"]["tentative_placement_order"][0] + self.fail(p, gid) + self.assertCaught(p, "placement of {} failed".format(gid)) + + def test_place_group_without_a_placement(self): + p = self.plan() + group(p, self.GID).pop("placement") + self.assertCaught(p, "place group {} has no proposed placement".format(self.GID)) + + def test_summary_lists(self): + p = self.plan() + p["summary"]["unplaced_place_groups"] = [self.GID] + self.assertCaught(p, "summary unplaced_place_groups") + p = self.plan() + p["summary"]["failed_placements"] = [self.GID] + self.assertCaught(p, "summary failed_placements") + p = self.plan() + p["summary"]["placement_order"].reverse() + self.assertCaught(p, "placement order in the summary differs") + p = self.plan() + p["summary"].pop("failed_placements") + self.assertCaught(p, "summary failed_placements") + + +class PlacePool(unittest.TestCase): + """Fix 3 in the generator: a failed group stays unplaced and nothing is placed through it.""" + + # a -> b -> c, and d reachable from the overworld directly + LINKS = {"a": {"ow"}, "b": {"a"}, "c": {"b"}, "d": {"ow"}} + + def run_pool(self, fails, pool=("a", "b", "c", "d")): + placed, calls = {"ow"}, [] + + def place(gid): + calls.append(gid) + if gid in fails: + return fails[gid] + placed.add(gid) + return {"chosen": gid} + + res = build_plan.place_pool(pool, lambda gid: bool(self.LINKS[gid] & placed), lambda gid, rem: set(), + lambda gid: 0, place) + return res, calls + + def test_all_placed(self): + (order, failed, left), calls = self.run_pool({}) + self.assertEqual((order, failed, left), (["a", "b", "c", "d"], [], [])) + self.assertEqual(calls, order) + + def test_failed_group_stays_unplaced(self): + (order, failed, left), calls = self.run_pool({"b": {"failed": True, "ideal": {}}}) + self.assertEqual(order, ["a", "d"]) + self.assertEqual(failed, ["b"]) + self.assertEqual(left, ["c"]) # reachable only through b + self.assertEqual(calls.count("b"), 1) # never retried + + def test_no_placement_record_is_a_failure(self): + (order, failed, left), _calls = self.run_pool({"a": None}) + self.assertEqual((order, failed, left), (["d"], ["a"], ["b", "c"])) + + +if __name__ == "__main__": + unittest.main() diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index fb1fb8e..e38ebf1 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -4,6 +4,9 @@ python3 -I scripts/verify_plan.py [placement-plan.json] Placement (every placed group, proposed and tentative): + * every place group has a proposed placement and every uncertain group a + tentative one; a failed placement fails the plan; the summary's placement + order, unplaced groups and failed placements are re-derived * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -28,12 +31,23 @@ stairwell carries the engine rule segments_drawn_only_on_player_layer * a stairwell's direction matches the exits it replaces, and each end holds exactly the exit objects (archetype, exit flag) that start there (a - one-way stairwell's far end is a landing) + one-way stairwell's far end is a landing); every replaces entry equals its + exit record (tiles, target, archetype, plane endpoints, direction) Exits: + * exit_inventory lists every exit object of the content once; the exit + records are exactly the copies it calls for (one per group a crossing exit + touches, the overworld's own exits in internal_exits; a wired exit as + data_problems lists it) and each equals its inventory entry; the summary + and per-group crossing counts are re-derived from the records + * every other copy of an exit (blocked sites, relayering exits, wired and + kept dangling exits, data problems, main connections) equals its record + or inventory entry * the records of one exit (one per group it touches) are identical; an exit labelled stairwell is listed in exactly that stairwell's replaces - * every exit record's plane_source / plane_target equals origin + tile on - the tile's final layer, and its "becomes" label matches its geometry; + * an exit record has plane_source / plane_target exactly when both ends + are placed (or overworld); they and overworld_plane_source / _target + equal origin + tile on the tile's final layer, and its "becomes" label + matches its geometry; wired exits are plain stairs and their verdict follows from their evidence; exits the owner keeps as decoration have no exit record * every exit 2+ layers and at most one tile per layer apart is a stairwell @@ -189,6 +203,38 @@ def put(owner, mapname, layer, x0, y0, rows): for layer in owners.values(): for rr in layer.values(): rr.done() + placed_ids = {g["id"] for g in placed} + summ = plan["summary"] + + # ----------------------------------------------------- placement status + # A failed placement must not be published; every place group is placed + # (proposed) and every uncertain group tentatively. The summary's order and + # unplaced lists are re-derived from the placement records. + others = [g for g in plan["groups"] if g["classification"] != "overworld"] + status = {g["id"]: (g.get("placement") or {}).get("status") for g in others} + for g in others: + gid, s = g["id"], status[g["id"]] + if s == "failed": + errors.append("placement of {} failed; a plan with a failed group must not be published".format(gid)) + elif s not in (None, "proposed", "tentative"): + errors.append("placement of {} has an unknown status {}".format(gid, s)) + want = {"place": "proposed", "uncertain": "tentative"}.get(g["classification"]) + if want is not None and s != want: + errors.append("{} group {} has no {} placement".format(g["classification"], gid, want)) + if s == "proposed" and g["classification"] != "place": + errors.append("{} group {} has a proposed placement".format(g["classification"], gid)) + index = {g["id"]: g["placement"].get("placement_index") for g in placed} + order = summ["placement_order"] + summ["tentative_placement_order"] + if sorted(order) != sorted(index) or [index[gid] for gid in order] != list(range(len(order))) or \ + sorted(summ["placement_order"]) != sorted(gid for gid, s in status.items() if s == "proposed"): + errors.append("placement order in the summary differs from the placed groups") + for key, cls, s_ok in (("unplaced_place_groups", "place", "proposed"), + ("unplaced_uncertain_groups", "uncertain", "tentative")): + want = sorted(g["id"] for g in others if g["classification"] == cls and status[g["id"]] != s_ok) + if summ.get(key) != want: + errors.append("summary {} {} != {}".format(key, summ.get(key), want)) + if summ.get("failed_placements") != sorted(gid for gid, s in status.items() if s == "failed"): + errors.append("summary failed_placements differs from the failed placement records") # rows coverage near every placed or stairwell tile used_cells = collections.defaultdict(set) @@ -314,15 +360,120 @@ def hit(r): # ----------------------------------------------------------- exit index records = {} + copies = collections.Counter() # (group, list, exit key) -> copies for g in plan["groups"]: for key in ("entrances_from_overworld", "exits_to_overworld", "entrances_from_other_groups", "exits_to_other_groups", "internal_exits"): for e in g.get(key, []): k = (e["source_map"], e["line"]) + copies[(g["id"], key, k)] += 1 if k in records and records[k] != e: errors.append("exit {}:{} has two different records".format(*k)) records[k] = e + def same_as(what, copy, ref, fields, ref_name="exit record"): + """Every listed field of a copy of an exit equals the field of its reference.""" + bad = [f for f in fields if copy.get(f) != ref.get(f)] + if bad: + errors.append("{} of exit {}:{} differs from its {} in {}".format( + what, ref["source_map"], ref["line"], ref_name, ", ".join(bad))) + + # The exit inventory is the expected set: every exit object read from the + # content. Each one with a destination has a record in every group it touches + # (the overworld's own exits in its internal_exits, none for an exit inside + # one other group), and nothing else has a record. + group_of = {} + for g in others: + for m in g["maps"]: + if group_of.setdefault(m["path"], g["id"]) != g["id"]: + errors.append("map {} is a member of two groups".format(m["path"])) + for z, cells in ow["cells_by_layer"].items(): + for X, Y in cells: + group_of.setdefault("{}{}_{}{}".format(prefix, X, Y, "" if int(z) == 0 else "_{}".format(z)), ow_id) + inv = {} + for x in plan["exit_inventory"]: + k = (x["source_map"], x["line"]) + if k in inv: + errors.append("exit {}:{} is listed twice in the exit inventory".format(*k)) + inv[k] = x + if len(plan["exit_inventory"]) != summ["exit_objects"]: + errors.append("the exit inventory lists {} exit objects, the summary {}".format( + len(plan["exit_inventory"]), summ["exit_objects"])) + wired_dp = {} + for d in plan["data_problems"]["dangling_exits_wired"]: + k = (d["source_map"], d["line"]) + if k in wired_dp: + errors.append("wired exit {}:{} is listed twice".format(*k)) + wired_dp[k] = d + if len(plan["data_problems"]["dangling_exits_wired"]) != summ["wired_exits"]: + errors.append("{} wired exits listed, the summary says {}".format( + len(plan["data_problems"]["dangling_exits_wired"]), summ["wired_exits"])) + expected_copies = set() + want_rec = {} # exit key -> the record fields the inventory (and wiring) fix + for k, x in sorted(inv.items()): + want = {f: x.get(f) for f in ("source_map", "line", "source_tile", "arch", "form", "target_map", "target_tile")} + if k in wired_dp: + if x["form"] != "tiled-unresolved" or "target_map" in x: + errors.append("wired exit {}:{} is not a legacy exit that leads nowhere".format(*k)) + want.update(form="wired", target_map=wired_dp[k].get("target_map"), + target_tile=wired_dp[k].get("target_tile")) + if want["target_map"] is None: + continue + sg, tg = group_of.get(x["source_map"]), group_of.get(want["target_map"]) + if sg is None or tg is None: + errors.append("exit {}:{} joins a map that is in no group".format(*k)) + continue + want.update(source_group=sg, target_group=tg) + want_rec[k] = want + if sg == tg: + if sg == ow_id: + expected_copies.add((ow_id, "internal_exits", k)) + continue + if sg != ow_id: + expected_copies.add((sg, "exits_to_overworld" if tg == ow_id else "exits_to_other_groups", k)) + if tg != ow_id: + expected_copies.add((tg, "entrances_from_overworld" if sg == ow_id else "entrances_from_other_groups", k)) + missing = sorted(expected_copies - set(copies)) + extra = sorted(c for c, n in copies.items() if c not in expected_copies or n > 1) + if missing: + errors.append("{} exit record(s) missing, e.g. exit {}:{} from {} of {}".format( + len(missing), missing[0][2][0], missing[0][2][1], missing[0][1], missing[0][0])) + if extra: + errors.append("{} exit record(s) the exit inventory does not call for, e.g. exit {}:{} in {} of {}".format( + len(extra), extra[0][2][0], extra[0][2][1], extra[0][1], extra[0][0])) + for k, e in sorted(records.items()): + if k in want_rec: + same_as("record", e, want_rec[k], ("source_tile", "arch", "form", "target_map", "target_tile", + "source_group", "target_group"), "inventory entry") + if (e.get("form") == "wired") != ("wired" in e): + errors.append("exit {}:{} has form {} but {} wiring evidence".format( + k[0], k[1], e.get("form"), "carries" if "wired" in e else "no")) + for k, d in sorted(wired_dp.items()): + e = records.get(k) + if e is None: + errors.append("wired exit {}:{} has no exit record".format(*k)) + continue + same_as("wired exit", d, e, ("source_tile", "arch", "target_map", "target_tile")) + if d.get("verdict") != (e.get("wired") or {}).get("verdict"): + errors.append("wired exit {}:{} verdict differs from its record".format(*k)) + # data problems and kept decoration describe exits without a record: compare with the inventory + for what, lst in (("data problem", plan["data_problems"]["exits"]), + ("kept decoration", plan.get("exits_kept_as_decoration", [])), + ("kept decoration problem", plan["data_problems"].get("dangling_exits_kept_as_decoration", []))): + for d in lst: + x = inv.get((d["source_map"], d["line"])) + if x is None: + errors.append("{} names exit {}:{}, which is not in the exit inventory".format( + what, d["source_map"], d["line"])) + continue + same_as(what, d, x, ("source_tile", "arch"), "inventory entry") + if what != "data problem" and x["form"] != "tiled-unresolved": + errors.append("{} exit {}:{} is not a legacy exit that leads nowhere".format( + what, d["source_map"], d["line"])) + if {(d["source_map"], d["line"]) for d in plan["data_problems"]["exits"] if d["kind"] == "tiled-exit-without-link"} \ + != {k for k, x in inv.items() if x["form"] == "tiled-unresolved"}: + errors.append("tiled exits without a link in the data problems differ from the exit inventory") + # ----------------------------------------------------------- stairwells chain = collections.defaultdict(dict) # layer -> owner -> Rows sw_by_id = {} @@ -372,7 +523,15 @@ def hit(r): continue if e.get("becomes") != "stairwell" or e.get("stairwell") != sw["id"]: errors.append("{} exit {}:{} is not marked as this stairwell".format(sw["id"], r["source_map"], r["line"])) - s, d = e["plane_source"], e["plane_target"] + # the copy in replaces is what the migration reads: it must be the record + same_as("{} replaces entry".format(sw["id"]), r, e, + ("source_tile", "target_map", "target_tile", "arch", "plane_source", "plane_target")) + s, d = e.get("plane_source"), e.get("plane_target") + if s is None or d is None: + continue # reported with the exit records + if r.get("goes") != ("down" if s[2] > d[2] else "up"): + errors.append("{} exit {}:{} goes {}, but its record goes {}".format( + sw["id"], r["source_map"], r["line"], r.get("goes"), "down" if s[2] > d[2] else "up")) up, low = (s, d) if s[2] > d[2] else (d, s) if up[2] != top[2] or low[2] != bot[2] or cheb(up, top) > tol or cheb(low, bot) > tol: errors.append("{} ends do not match exit {}:{}".format(sw["id"], r["source_map"], r["line"])) @@ -428,18 +587,34 @@ def hit(r): replaced_by[(r["source_map"], r["line"])].append(sw["id"]) exits_checked = 0 for (src_map, line), e in sorted(records.items()): + live = all(x == ow_id or x in placed_ids for x in (e["source_group"], e["target_group"])) + if live != ("plane_source" in e and "plane_target" in e): + errors.append("exit {}:{} {} plane endpoints, but {} of its ends is placed".format( + src_map, line, "lacks" if live else "has", "each" if live else "not each")) + continue ends = {} - for end, mkey, tkey, gkey in (("plane_source", "source_map", "source_tile", "source_group"), - ("plane_target", "target_map", "target_tile", "target_group")): - if end not in e: - continue + for end, ow_end, mkey, tkey, gkey in ( + ("plane_source", "overworld_plane_source", "source_map", "source_tile", "source_group"), + ("plane_target", "overworld_plane_target", "target_map", "target_tile", "target_group")): mp, (tx, ty) = e[mkey], e[tkey] + if (e[gkey] == ow_id) != (ow_end in e): + errors.append("exit {}:{} {} {}".format(src_map, line, "lacks" if ow_end not in e else "has", ow_end)) if e[gkey] == ow_id: + if world_cell(mp) is None: + errors.append("exit {}:{} has an overworld end on {}, which is not an overworld map".format( + src_map, line, mp)) + continue X, Y, _Z = world_cell(mp) exp = [24 * X + tx, 24 * Y + ty, ow_layer(mp, tx, ty)] - else: + if ow_end in e and e[ow_end] != exp: + errors.append("exit {}:{} {} {} != {}".format(src_map, line, ow_end, e[ow_end], exp)) + elif mp in origin and e[gkey] in placed_ids: o = origin[mp] exp = [o[0] + tx, o[1] + ty, o[2]] + else: + continue + if not live: + continue exits_checked += 1 ends[end] = exp if exp != e[end]: @@ -495,6 +670,37 @@ def hit(r): if not d.get("decision") or "no exit" not in d.get("migrate_as", ""): errors.append("exit {}:{} is kept as decoration without its decision".format(d["source_map"], d["line"])) + # crossing counts, re-derived from the records (one per exit object) + def counted(es): + return dict(sorted(collections.Counter(str(e.get("becomes")) for e in es).items())) + + crossing = [e for e in records.values() if e["source_group"] != e["target_group"]] + live_crossing = [e for e in crossing if "plane_source" in e and "plane_target" in e] + if counted(live_crossing) != summ["crossing_exit_objects_after_placement"]: + errors.append("summary crossing exits {} differ from the exit records {}".format( + summ["crossing_exit_objects_after_placement"], counted(live_crossing))) + if len(crossing) - len(live_crossing) != summ["crossing_exit_objects_into_separate_planes"]: + errors.append("summary crossing exits into separate planes {} differ from the exit records {}".format( + summ["crossing_exit_objects_into_separate_planes"], len(crossing) - len(live_crossing))) + reasons = collections.Counter(str(e.get("portal_reason")) for e in live_crossing if e.get("becomes") == "portal") + if dict(sorted(reasons.items())) != summ["portal_reasons"]: + errors.append("summary portal reasons differ from the exit records") + for g in placed: + mine = counted(e for e in live_crossing if g["id"] in (e["source_group"], e["target_group"])) + if mine != g["placement"].get("crossing_exits_after_placement"): + errors.append("crossing exits of {} {} differ from its exit records {}".format( + g["id"], g["placement"].get("crossing_exits_after_placement"), mine)) + # the main connection is one of the group's exit records + mc = g["placement"].get("main_connection") + if mc is not None: + keys = ("entrances_from_overworld", "entrances_from_other_groups") if mc.get("kind") == "in" else \ + ("exits_to_overworld", "exits_to_other_groups") + if not any([e["source_map"], e["source_tile"], e["target_map"], e["target_tile"], e["arch"], + e["vertical"]] == [mc.get("source_map"), mc.get("source_tile"), mc.get("target_map"), + mc.get("target_tile"), mc.get("arch"), mc.get("vdir")] + for key in keys for e in g.get(key, [])): + errors.append("main connection of {} is not one of its exit records".format(g["id"])) + # ------------------------------------------------- required stairwells # Every exit whose ends are 2+ layers apart and at most one tile apart per # layer must be a stairwell, unless a blocked site lists it: crossing exits @@ -505,7 +711,14 @@ def hit(r): ("relayering", rl["stairwells_blocked"])): for b in sites: for x in b["exits"]: - blocked_keys[(x["source_map"], x["line"])] = (key_, b) + k = (x["source_map"], x["line"]) + if k in blocked_keys: + errors.append("exit {}:{} is listed at two blocked stairwell sites".format(*k)) + if k not in records: + errors.append("blocked {} stairwell site lists exit {}:{}, which has no exit record".format(key_, *k)) + else: + same_as("blocked {} site entry".format(key_), x, records[k], ("source_tile", "arch")) + blocked_keys[k] = (key_, b) required = 0 for (src_map, line), e in sorted(records.items()): if "plane_source" not in e or "plane_target" not in e: @@ -868,6 +1081,8 @@ def moved_end(g, mp, t): moved_end(r["source_group"], r["source_map"], r["source_tile"]) or moved_end(r["target_group"], r["target_map"], r["target_tile"]))} listed = {(e["source_map"], e["line"]) for e in rl["exits"]} + if len(listed) != len(rl["exits"]): + errors.append("an exit is listed twice in the relayering exits") if expected != listed: errors.append("relayering exits differ: {} expected but not listed, {} listed but not expected".format( len(expected - listed), len(listed - expected))) @@ -881,6 +1096,13 @@ def moved_end(g, mp, t): if e["layer_delta_before"] != ls[1] - ls[0] or e["layer_delta_after"] != r["layer_delta"] or \ e["becomes"] != r["becomes"]: errors.append("relayering exit {}:{} before/after does not match".format(e["source_map"], e["line"])) + same_as("relayering exit", e, r, ("source_tile", "target_map", "target_tile", "arch", "xy_distance", + "stairwell", "portal_reason")) + crosses = None if r["source_group"] == r["target_group"] else ( + r["target_group"] if r["source_group"] == ow_id else r["source_group"]) + if e.get("crosses_to_group") != crosses: + errors.append("relayering exit {}:{} crosses to {}, its record to {}".format( + e["source_map"], e["line"], e.get("crosses_to_group"), crosses)) for g in placed: if any(r["relayered"] for r in rl["group_regions"].get(g["id"], [])): errors.append("placed group {} has a relayered region, but its maps are recorded on one layer " @@ -892,6 +1114,7 @@ def moved_end(g, mp, t): "stairwells_checked": len(plan["stairwells"]), "stairwells_by_kind": dict(sorted(kinds.items())), "stairwell_exits_required": required, "stairwell_intermediate_tiles": intermediate, "exit_endpoints_checked": exits_checked, + "exit_inventory_objects": len(inv), "exit_record_copies_checked": sum(copies.values()), "relayering": {"overworld_tiles": len(leg), "tile_heights": len(heights), "regions": len(got_r), "kept_regions": len(got_k), "units": len(units), "moved_units": len(got_u), "moved_tiles": len(moved), "cut_edges": len(edges), "seam_pieces": len(pieces), From e36e51eaa540cf4125d2532412708fe2750dc898 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 12:43:29 -0500 Subject: [PATCH 02/20] fix(placement-plan): verify every exit record field and data problem Address the first Codex review round on #47. - Exit records and the exit inventory now share exit_source_fields(), and every content field of a record (tiles, target, archetype, form, direction, flags) must equal its inventory entry. A record may hold no field the verifier does not know. Wired exits no longer carry an authored_target of nulls. - A record has all of its placement fields exactly when both of its ends are placed, and none of them otherwise, so a partial endpoint pair is rejected. xy_distance and layer_delta must follow from the plane ends. The stairwell label must match becomes, and a portal's reason and slope_reachable are re-derived the way the generator decides them. - The exit data problems (kind and severity per exit) are re-derived from the inventory, which now also carries the reader's target problem and an explicit last_heal 0. - A blocked stairwell site may list only exits that need a stairwell, so the existing end check covers every listed exit. - A tentative placement needs an uncertain group, or an unreachable group with an alignment hint. - The alignment-hint retry runs only for groups no pool attempted, so a failed group cannot come back as both placed and failed. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/placement-plan.json | 10706 +++++++++------- .../placement-plan/scripts/build_plan.py | 62 +- .../scripts/test_placement_plan.py | 74 +- .../placement-plan/scripts/verify_plan.py | 163 +- 4 files changed, 6375 insertions(+), 4630 deletions(-) diff --git a/migration/placement-plan/placement-plan.json b/migration/placement-plan/placement-plan.json index 6a1c0a4..c9b9a3d 100644 --- a/migration/placement-plan/placement-plan.json +++ b/migration/placement-plan/placement-plan.json @@ -6560,17392 +6560,18987 @@ "exit_inventory": [ { "source_map": "/emergency", - "line": 17, "source_tile": [ 0, 0 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 3, 7 - ] + ], + "arch": "exit", + "line": 17, + "form": "explicit", + "vertical": "none" }, { "source_map": "/guilds/quick_wolves/guild", - "line": 171, "source_tile": [ 1, 12 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/guilds/quick_wolves/storage", "target_tile": [ 19, 23 - ] + ], + "arch": "stairs_wood_up", + "line": 171, + "form": "explicit", + "vertical": "up" }, { "source_map": "/guilds/quick_wolves/guild", - "line": 202, "source_tile": [ 1, 15 ], - "arch": "stairs_red_up", - "form": "explicit", "target_map": "/guilds/quick_wolves/oracle", "target_tile": [ 10, 2 + ], + "arch": "stairs_red_up", + "line": 202, + "form": "explicit", + "vertical": "up", + "event_scripts": [ + "/python/guilds/oracle_entrance.py" ] }, { "source_map": "/guilds/quick_wolves/guild", - "line": 2078, "source_tile": [ 18, 20 ], "arch": "stairs_wood_up", - "form": "tiled-unresolved" + "line": 2078, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/guilds/quick_wolves/guild", - "line": 2631, "source_tile": [ 22, 12 ], "arch": "stairs_wood_down", - "form": "tiled-unresolved" + "line": 2631, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/guilds/quick_wolves/guild", - "line": 2681, "source_tile": [ 22, 20 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_4_65", "target_tile": [ 12, 6 - ] + ], + "arch": "exit", + "line": 2681, + "form": "explicit", + "vertical": "none" }, { "source_map": "/guilds/quick_wolves/oracle", - "line": 830, "source_tile": [ 10, 2 ], - "arch": "stairs_red_down", - "form": "explicit", "target_map": "/guilds/quick_wolves/guild", "target_tile": [ 1, 15 - ] + ], + "arch": "stairs_red_down", + "line": 830, + "form": "explicit", + "vertical": "down" }, { "source_map": "/guilds/quick_wolves/storage", - "line": 3780, "source_tile": [ 19, 23 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/guilds/quick_wolves/guild", "target_tile": [ 1, 12 - ] + ], + "arch": "stairs_wood_down", + "line": 3780, + "form": "explicit", + "vertical": "down" }, { "source_map": "/hall_of_dms", - "line": 629, "source_tile": [ 10, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/guilds/quick_wolves/oracle", "target_tile": [ 7, 7 - ] + ], + "arch": "exit", + "line": 629, + "form": "explicit", + "vertical": "none" }, { "source_map": "/hall_of_dms", - "line": 748, "source_tile": [ 12, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/planes/creation/creation_0808", "target_tile": [ 11, 12 - ] + ], + "arch": "exit", + "line": 748, + "form": "explicit", + "vertical": "none" }, { "source_map": "/hall_of_dms", - "line": 876, "source_tile": [ 14, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_1_68", "target_tile": [ 12, 19 - ] + ], + "arch": "exit", + "line": 876, + "form": "explicit", + "vertical": "none" }, { "source_map": "/hall_of_dms", - "line": 995, "source_tile": [ 16, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_9_68", "target_tile": [ 16, 11 - ] + ], + "arch": "exit", + "line": 995, + "form": "explicit", + "vertical": "none" }, { "source_map": "/hall_of_dms", - "line": 1114, "source_tile": [ 18, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_7_78", "target_tile": [ 11, 19 - ] + ], + "arch": "exit", + "line": 1114, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", - "line": 1676, "source_tile": [ 9, 1 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1676, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", - "line": 1845, "source_tile": [ 10, 1 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1845, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", - "line": 1939, "source_tile": [ 10, 17 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_3_3_-2", "target_tile": [ 1, 2 - ] + ], + "arch": "exit_green", + "line": 1939, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", - "line": 2009, "source_tile": [ 11, 1 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 2009, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/planes/creation/creation_0501", - "line": 427, "source_tile": [ 2, 1 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/planes/creation/creation_0601", "target_tile": [ 3, 1 - ] + ], + "arch": "exit_green", + "line": 427, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 996, "source_tile": [ 6, 1 ], - "arch": "exit", - "form": "same-map", "target_map": "/planes/creation/creation_0501", "target_tile": [ 13, 1 - ] + ], + "arch": "exit", + "line": 996, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 1706, "source_tile": [ 12, 1 ], - "arch": "exit", - "form": "same-map", "target_map": "/planes/creation/creation_0501", "target_tile": [ 5, 1 - ] + ], + "arch": "exit", + "line": 1706, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 2234, "source_tile": [ 16, 1 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/planes/creation/creation_0501", "target_tile": [ 16, 1 - ] + ], + "arch": "shop_mat", + "line": 2234, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 2455, "source_tile": [ 17, 13 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/planes/creation/creation_0601", "target_tile": [ 1, 11 - ] + ], + "arch": "exit_green", + "line": 2455, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 2736, "source_tile": [ 19, 20 ], - "arch": "exit", - "form": "explicit", "target_map": "/planes/creation/creation_0601", "target_tile": [ 19, 6 - ] + ], + "arch": "exit", + "line": 2736, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0501", - "line": 2916, "source_tile": [ 21, 1 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/planes/creation/creation_0501", "target_tile": [ 21, 1 - ] + ], + "arch": "shop_mat", + "line": 2916, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0601", - "line": 304, "source_tile": [ 1, 12 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/planes/creation/creation_0501", "target_tile": [ 17, 12 - ] + ], + "arch": "exit_green", + "line": 304, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0601", - "line": 376, "source_tile": [ 2, 1 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/planes/creation/creation_0501", "target_tile": [ 2, 2 - ] + ], + "arch": "exit_green", + "line": 376, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0601", - "line": 2825, "source_tile": [ 20, 6 ], - "arch": "exit", - "form": "explicit", "target_map": "/planes/creation/creation_0501", "target_tile": [ 19, 19 - ] + ], + "arch": "exit", + "line": 2825, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0601", - "line": 2949, "source_tile": [ 21, 1 ], - "arch": "portal_azure", - "form": "same-map", "target_map": "/planes/creation/creation_0601", "target_tile": [ 16, 1 - ] + ], + "arch": "portal_azure", + "line": 2949, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0801", - "line": 1199, "source_tile": [ 7, 1 ], - "arch": "teleporter", - "form": "same-map", "target_map": "/planes/creation/creation_0801", "target_tile": [ 1, 4 - ] + ], + "arch": "teleporter", + "line": 1199, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0801", - "line": 3227, "source_tile": [ 21, 3 ], - "arch": "exit", - "form": "same-map", "target_map": "/planes/creation/creation_0801", "target_tile": [ 18, 5 - ] + ], + "arch": "exit", + "line": 3227, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0801", - "line": 3241, "source_tile": [ 21, 5 ], - "arch": "exit", - "form": "same-map", "target_map": "/planes/creation/creation_0801", "target_tile": [ 18, 5 - ] + ], + "arch": "exit", + "line": 3241, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0801", - "line": 3257, "source_tile": [ 21, 7 ], - "arch": "teleporter", - "form": "same-map", "target_map": "/planes/creation/creation_0801", "target_tile": [ 18, 5 - ] + ], + "arch": "teleporter", + "line": 3257, + "form": "same-map", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0808", - "line": 1488, "source_tile": [ 11, 11 ], - "arch": "exit", - "form": "explicit", "target_map": "/hall_of_dms", "target_tile": [ 12, 17 - ] + ], + "arch": "exit", + "line": 1488, + "form": "explicit", + "vertical": "none" }, { "source_map": "/planes/creation/creation_0907", - "line": 999, "source_tile": [ 4, 15 ], - "arch": "exit", - "form": "same-map", "target_map": "/planes/creation/creation_0907", "target_tile": [ 4, 21 - ] + ], + "arch": "exit", + "line": 999, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/desert_plane_0101", - "line": 2345, "source_tile": [ 23, 0 ], - "arch": "ladder_down2", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/unusual_garden", "target_tile": [ 15, 10 + ], + "arch": "ladder_down2", + "line": 2345, + "form": "explicit", + "vertical": "down", + "event_scripts": [ + "scripts/ladder.py" ] }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 787, "source_tile": [ 8, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 787, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 798, "source_tile": [ 8, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 798, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 809, "source_tile": [ 8, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 809, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 820, "source_tile": [ 8, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 820, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 831, "source_tile": [ 8, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 831, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 842, "source_tile": [ 8, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 842, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 853, "source_tile": [ 8, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 853, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 931, "source_tile": [ 9, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 931, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 962, "source_tile": [ 9, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 962, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1040, "source_tile": [ 10, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1040, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1071, "source_tile": [ 10, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1071, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1149, "source_tile": [ 11, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1149, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1187, "source_tile": [ 11, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1187, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1265, "source_tile": [ 12, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1265, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1296, "source_tile": [ 12, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1296, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1374, "source_tile": [ 13, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1374, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1405, "source_tile": [ 13, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1405, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1483, "source_tile": [ 14, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1483, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1494, "source_tile": [ 14, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1494, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1505, "source_tile": [ 14, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1505, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1516, "source_tile": [ 14, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1516, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1527, "source_tile": [ 14, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1527, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1538, "source_tile": [ 14, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1538, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", - "line": 1549, "source_tile": [ 14, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 11, 5 - ] + ], + "arch": "invis_exit", + "line": 1549, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", - "line": 71, "source_tile": [ 1, 2 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", "target_tile": [ 3, 2 - ] + ], + "arch": "invis_exit", + "line": 71, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/passage_0101", - "line": 295, "source_tile": [ 6, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/desert_plane_0101", "target_tile": [ 17, 22 - ] + ], + "arch": "invis_exit", + "line": 295, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/unusual_garden", - "line": 1613, "source_tile": [ 12, 15 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/world_12_59", "target_tile": [ 12, 15 - ] + ], + "arch": "hole", + "line": 1613, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", - "line": 1165, "source_tile": [ 8, 1 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", "target_tile": [ 15, 10 - ] + ], + "arch": "exit", + "line": 1165, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", - "line": 1699, "source_tile": [ 11, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/desert_plane/entrance", "target_tile": [ 11, 11 - ] + ], + "arch": "exit", + "line": 1699, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", - "line": 2935, "source_tile": [ 19, 16 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", "target_tile": [ 22, 22 - ] + ], + "arch": "exit", + "line": 2935, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", - "line": 3517, "source_tile": [ 23, 5 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", "target_tile": [ 22, 22 - ] + ], + "arch": "exit", + "line": 3517, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", - "line": 441, "source_tile": [ 4, 10 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_11_61", "target_tile": [ 10, 10 - ] + ], + "arch": "stairs_stone_up", + "line": 441, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", - "line": 740, "source_tile": [ 7, 1 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0101", "target_tile": [ 15, 10 - ] + ], + "arch": "exit", + "line": 740, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", - "line": 605, "source_tile": [ 11, 3 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_76", "target_tile": [ 13, 21 - ] + ], + "arch": "invis_exit", + "line": 605, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 64, "source_tile": [ 0, 4 ], "arch": "hole2", - "form": "unresolved" + "line": 64, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 148, "source_tile": [ 1, 0 ], "arch": "hole2", - "form": "unresolved" + "line": 148, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 206, "source_tile": [ 1, 6 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_-7_76", "target_tile": [ 8, 14 - ] + ], + "arch": "stairs_wood_up2", + "line": 206, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 345, "source_tile": [ 2, 6 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_-7_76", "target_tile": [ 8, 14 - ] + ], + "arch": "stairs_wood_up", + "line": 345, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 595, "source_tile": [ 4, 4 ], "arch": "hole2", - "form": "unresolved" + "line": 595, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "line": 638, "source_tile": [ 4, 8 ], "arch": "hole2", - "form": "unresolved" + "line": 638, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_cheap", - "line": 79, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 79, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_expensive", - "line": 97, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 97, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/eld_woods_island/apartments/apartment_normal", - "line": 85, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 85, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/eld_woods_island/cave_morlarg", - "line": 2637, "source_tile": [ 22, 4 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_11_79", "target_tile": [ 8, 7 - ] + ], + "arch": "invis_exit", + "line": 2637, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/eld_woods_island/cave_morlarg", - "line": 2652, "source_tile": [ 22, 5 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_11_79", "target_tile": [ 8, 8 - ] + ], + "arch": "invis_exit", + "line": 2652, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 64, "source_tile": [ 1, 1 ], "arch": "ladder_down", - "form": "tiled-unresolved" + "line": 64, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 71, "source_tile": [ 1, 1 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/world_8_78", "target_tile": [ 6, 23 - ] + ], + "arch": "ladder_up", + "line": 71, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 181, "source_tile": [ 3, 3 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 181, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 537, "source_tile": [ 8, 3 ], "arch": "ladder_down", - "form": "tiled-unresolved" + "line": 537, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 732, "source_tile": [ 10, 5 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 732, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", - "line": 1196, "source_tile": [ 15, 5 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", "target_tile": [ 17, 14 - ] + ], + "arch": "ladder_down", + "line": 1196, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", - "line": 175, "source_tile": [ 8, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", "target_tile": [ 37, 16 - ] + ], + "arch": "invis_exit", + "line": 175, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", - "line": 358, "source_tile": [ 14, 13 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", "target_tile": [ 17, 4 - ] + ], + "arch": "ladder_down", + "line": 358, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", - "line": 641, "source_tile": [ 17, 14 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", "target_tile": [ 15, 5 - ] + ], + "arch": "ladder_up", + "line": 641, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", - "line": 1017, "source_tile": [ 8, 22 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_a", "target_tile": [ 21, 2 - ] + ], + "arch": "ladder_down", + "line": 1017, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", - "line": 1092, "source_tile": [ 9, 11 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", "target_tile": [ 14, 13 - ] + ], + "arch": "ladder_up", + "line": 1092, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_a", - "line": 3226, "source_tile": [ 21, 2 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique", "target_tile": [ 8, 22 - ] + ], + "arch": "ladder_up", + "line": 3226, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", - "line": 102, "source_tile": [ 2, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_8_78", "target_tile": [ 6, 23 - ] + ], + "arch": "invis_exit", + "line": 102, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", - "line": 560, "source_tile": [ 17, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_8_78", "target_tile": [ 6, 23 - ] + ], + "arch": "invis_exit", + "line": 560, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_unique_tipper", - "line": 590, "source_tile": [ 17, 4 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance_a", "target_tile": [ 14, 13 - ] + ], + "arch": "ladder_up", + "line": 590, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", - "line": 136, "source_tile": [ 2, 2 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", "target_tile": [ 1, 1 - ] + ], + "arch": "ladder_up", + "line": 136, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", - "line": 263, "source_tile": [ 4, 4 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/world_7_77", "target_tile": [ 18, 21 - ] + ], + "arch": "ladder_down", + "line": 263, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", - "line": 76, "source_tile": [ 1, 1 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", "target_tile": [ 2, 2 - ] + ], + "arch": "ladder_down", + "line": 76, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", - "line": 199, "source_tile": [ 3, 3 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", "target_tile": [ 4, 4 - ] + ], + "arch": "ladder_up", + "line": 199, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", - "line": 140, "source_tile": [ 2, 2 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", "target_tile": [ 3, 3 - ] + ], + "arch": "ladder_up", + "line": 140, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", - "line": 267, "source_tile": [ 4, 4 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_2", "target_tile": [ 3, 3 - ] + ], + "arch": "ladder_down", + "line": 267, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", - "line": 238, "source_tile": [ 3, 3 ], - "arch": "ladder_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_3", "target_tile": [ 2, 2 - ] + ], + "arch": "ladder_down", + "line": 238, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", - "line": 327, "source_tile": [ 4, 7 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_4", "target_tile": [ 4, 6 + ], + "arch": "exit", + "line": 327, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", - "line": 298, "source_tile": [ 2, 22 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_14_71", "target_tile": [ 14, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 298, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", - "line": 444, "source_tile": [ 3, 22 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_14_71", "target_tile": [ 15, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 444, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", - "line": 1503, "source_tile": [ 9, 22 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_19_72", "target_tile": [ 0, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1503, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", - "line": 1666, "source_tile": [ 10, 22 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_19_72", "target_tile": [ 1, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1666, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/everlink/everlink_0102", - "line": 564, "source_tile": [ 5, 7 ], "arch": "exit", - "form": "unresolved" + "line": 564, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/exit.py" + ] }, { "source_map": "/shattered_islands/everlink/everlink_0301", - "line": 3009, "source_tile": [ 20, 12 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/everlink/everlink_0301", "target_tile": [ 20, 12 - ] + ], + "arch": "shop_mat", + "line": 3009, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/everlink/everlink_0301", - "line": 3027, "source_tile": [ 20, 14 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/everlink/everlink_0301", "target_tile": [ 20, 14 - ] + ], + "arch": "shop_mat", + "line": 3027, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/everlink/everlink_0302", - "line": 1734, "source_tile": [ 15, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/everlink/everlink_0302", "target_tile": [ 15, 11 - ] + ], + "arch": "shop_mat", + "line": 1734, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/everlink/everlink_0302", - "line": 2052, "source_tile": [ 17, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/everlink/everlink_0302", "target_tile": [ 17, 11 - ] + ], + "arch": "shop_mat", + "line": 2052, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/incuna/apartments/beach_nook", - "line": 1295, "source_tile": [ 10, 13 ], "arch": "exit", - "form": "unresolved" + "line": 1295, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/incuna/ellamin", - "line": 460, "source_tile": [ 3, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_4_83", "target_tile": [ 15, 7 - ] + ], + "arch": "invis_exit", + "line": 460, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/incuna/ellamin", - "line": 737, "source_tile": [ 4, 13 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_83", "target_tile": [ 13, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 737, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck", - "line": 284, "source_tile": [ 6, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 7, 13 - ] + ], + "arch": "stairs_wood_up", + "line": 284, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck", - "line": 301, "source_tile": [ 6, 3 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 7, 13 - ] + ], + "arch": "stairs_wood_up2", + "line": 301, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", - "line": 272, "source_tile": [ 6, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 7, 13 - ] + ], + "arch": "stairs_wood_up", + "line": 272, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", - "line": 284, "source_tile": [ 6, 3 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 7, 13 - ] + ], + "arch": "stairs_wood_up2", + "line": 284, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", - "line": 333, "source_tile": [ 2, 7 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_70", "target_tile": [ 1, 13 - ] + ], + "arch": "stairs_wood_up", + "line": 333, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", - "line": 465, "source_tile": [ 3, 7 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_0_70", "target_tile": [ 1, 13 - ] + ], + "arch": "stairs_wood_up2", + "line": 465, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", - "line": 1477, "source_tile": [ 8, 25 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_13_7", "target_tile": [ 12, 8 - ] + ], + "arch": "stairs_stone_up", + "line": 1477, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", - "line": 1654, "source_tile": [ 9, 25 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_13_7", "target_tile": [ 13, 8 - ] + ], + "arch": "stairs_stone_up", + "line": 1654, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", - "line": 1904, "source_tile": [ 10, 25 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_13_7", "target_tile": [ 14, 8 - ] + ], + "arch": "stairs_stone_up", + "line": 1904, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 122, "source_tile": [ 1, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/emergency", "target_tile": [ 0, 0 - ] + ], + "arch": "stairs_wood_up", + "line": 122, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 165, "source_tile": [ 1, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_70", "target_tile": [ 20, 16 - ] + ], + "arch": "stairs_wood_up", + "line": 165, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 208, "source_tile": [ 1, 18 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_9_56", "target_tile": [ 17, 17 - ] + ], + "arch": "stairs_wood_up", + "line": 208, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 624, "source_tile": [ 6, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/emergency", "target_tile": [ 0, 0 - ] + ], + "arch": "stairs_wood_up", + "line": 624, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 668, "source_tile": [ 6, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_78", "target_tile": [ 19, 21 - ] + ], + "arch": "stairs_wood_up", + "line": 668, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 712, "source_tile": [ 6, 18 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_2_74", "target_tile": [ 9, 21 - ] + ], + "arch": "stairs_wood_up", + "line": 712, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 990, "source_tile": [ 12, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_9_42", "target_tile": [ 12, 2 - ] + ], + "arch": "stairs_wood_up", + "line": 990, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 1033, "source_tile": [ 12, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_12_68", "target_tile": [ 20, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 1033, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 1076, "source_tile": [ 12, 18 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_9_41", "target_tile": [ 12, 3 - ] + ], + "arch": "stairs_wood_up", + "line": 1076, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 1492, "source_tile": [ 17, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_17_3", "target_tile": [ 11, 18 - ] + ], + "arch": "stairs_wood_up", + "line": 1492, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 1536, "source_tile": [ 17, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_14_64", "target_tile": [ 20, 11 - ] + ], + "arch": "stairs_wood_up", + "line": 1536, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_1", - "line": 1580, "source_tile": [ 17, 18 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_12_68", "target_tile": [ 10, 22 - ] + ], + "arch": "stairs_wood_up", + "line": 1580, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_2", - "line": 96, "source_tile": [ 1, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_2_70", "target_tile": [ 5, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 96, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_2", - "line": 139, "source_tile": [ 1, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_15_4", "target_tile": [ 15, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 139, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_2", - "line": 481, "source_tile": [ 6, 2 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_51", "target_tile": [ 16, 6 - ] + ], + "arch": "stairs_wood_up", + "line": 481, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/ships/ship_2", - "line": 527, "source_tile": [ 6, 10 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_18_3", "target_tile": [ 6, 17 - ] + ], + "arch": "stairs_wood_up", + "line": 527, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/apartments/apartment_cheap", - "line": 59, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 59, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/apartments/apartment_expensive", - "line": 83, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 83, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/apartments/apartment_luxurious", - "line": 117, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 117, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/apartments/apartment_normal", - "line": 75, "source_tile": [ 1, 1 ], "arch": "exit", - "form": "unresolved" + "line": 75, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_out.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 191, "source_tile": [ 2, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 191, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 212, "source_tile": [ 2, 27 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 212, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 309, "source_tile": [ 3, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 309, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 330, "source_tile": [ 3, 27 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 330, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 367, "source_tile": [ 3, 31 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 367, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 388, "source_tile": [ 3, 32 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 388, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 409, "source_tile": [ 3, 33 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 409, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 502, "source_tile": [ 4, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 502, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 523, "source_tile": [ 4, 27 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 523, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 552, "source_tile": [ 4, 30 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 552, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 569, "source_tile": [ 4, 31 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 569, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 586, "source_tile": [ 4, 32 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 586, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 607, "source_tile": [ 4, 33 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 607, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 693, "source_tile": [ 5, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 693, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 714, "source_tile": [ 5, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 714, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 735, "source_tile": [ 5, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 735, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 780, "source_tile": [ 5, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 780, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 823, "source_tile": [ 5, 30 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 823, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 840, "source_tile": [ 5, 31 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 840, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 861, "source_tile": [ 5, 32 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 861, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 882, "source_tile": [ 5, 33 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 882, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 985, "source_tile": [ 6, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 985, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1006, "source_tile": [ 6, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1006, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1157, "source_tile": [ 6, 30 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1157, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1178, "source_tile": [ 6, 31 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1178, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1199, "source_tile": [ 6, 32 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1199, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1292, "source_tile": [ 7, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1292, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1374, "source_tile": [ 7, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1374, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1391, "source_tile": [ 7, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1391, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1408, "source_tile": [ 7, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1408, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1618, "source_tile": [ 8, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1618, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1635, "source_tile": [ 8, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1635, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1674, "source_tile": [ 8, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1674, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1731, "source_tile": [ 8, 34 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1731, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1752, "source_tile": [ 8, 35 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1752, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1835, "source_tile": [ 9, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1835, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1856, "source_tile": [ 9, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1856, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1877, "source_tile": [ 9, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1877, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1918, "source_tile": [ 9, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1918, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1953, "source_tile": [ 9, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1953, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 1974, "source_tile": [ 9, 27 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 1974, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2015, "source_tile": [ 9, 33 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2015, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2032, "source_tile": [ 9, 34 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2032, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2049, "source_tile": [ 9, 35 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2049, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2132, "source_tile": [ 10, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2132, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2149, "source_tile": [ 10, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2149, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2166, "source_tile": [ 10, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2166, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2183, "source_tile": [ 10, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2183, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2204, "source_tile": [ 10, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2204, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2255, "source_tile": [ 10, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2255, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2276, "source_tile": [ 10, 27 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2276, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2317, "source_tile": [ 10, 33 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2317, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2334, "source_tile": [ 10, 34 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2334, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2351, "source_tile": [ 10, 35 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2351, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2442, "source_tile": [ 11, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2442, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2459, "source_tile": [ 11, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2459, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2476, "source_tile": [ 11, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2476, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2497, "source_tile": [ 11, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2497, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2518, "source_tile": [ 11, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2518, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2581, "source_tile": [ 11, 26 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2581, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2623, "source_tile": [ 11, 30 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2623, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2799, "source_tile": [ 12, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2799, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2820, "source_tile": [ 12, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2820, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2841, "source_tile": [ 12, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] - }, - { + ], + "arch": "invis_exit", + "line": 2841, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true + }, + { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2965, "source_tile": [ 12, 30 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2965, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 2986, "source_tile": [ 12, 31 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 2986, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3332, "source_tile": [ 14, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3332, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3353, "source_tile": [ 14, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3353, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3506, "source_tile": [ 15, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3506, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3523, "source_tile": [ 15, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3523, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3544, "source_tile": [ 15, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3544, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3714, "source_tile": [ 16, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3714, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3731, "source_tile": [ 16, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3731, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3752, "source_tile": [ 16, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3752, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3799, "source_tile": [ 16, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3799, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 3820, "source_tile": [ 16, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 3820, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4046, "source_tile": [ 17, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4046, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4067, "source_tile": [ 17, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4067, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4108, "source_tile": [ 17, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4108, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4125, "source_tile": [ 17, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4125, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4142, "source_tile": [ 17, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4142, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4358, "source_tile": [ 18, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4358, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 4615, "source_tile": [ 19, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 20, 43 - ] + ], + "arch": "invis_exit", + "line": 4615, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", - "line": 5252, "source_tile": [ 21, 43 ], - "arch": "rope_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_66", "target_tile": [ 18, 19 - ] + ], + "arch": "rope_up", + "line": 5252, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", - "line": 2090, "source_tile": [ 11, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", "target_tile": [ 23, 13 - ] + ], + "arch": "exit", + "line": 2090, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", - "line": 3596, "source_tile": [ 19, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_4_66_-2", "target_tile": [ 0, 12 - ] + ], + "arch": "exit", + "line": 3596, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", - "line": 1777, "source_tile": [ 11, 12 ], - "arch": "portal_green", - "form": "automatic-link", "target_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", "target_tile": [ 11, 12 - ] + ], + "arch": "portal_green", + "line": 1777, + "form": "automatic-link", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab_nyhelobo", - "line": 2890, "source_tile": [ 19, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_4_66_-2", "target_tile": [ 0, 12 - ] + ], + "arch": "exit", + "line": 2890, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/dark_cave/dark_cave_0102", - "line": 165, "source_tile": [ 1, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_70", "target_tile": [ 6, 0 - ] + ], + "arch": "invis_exit", + "line": 165, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", - "line": 2999, "source_tile": [ 21, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_0_65", "target_tile": [ 17, 10 - ] + ], + "arch": "invis_exit", + "line": 2999, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", - "line": 3112, "source_tile": [ 22, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_0_65", "target_tile": [ 18, 10 - ] + ], + "arch": "invis_exit", + "line": 3112, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 1549, "source_tile": [ 8, 12 ], "arch": "invis_exit", - "form": "unresolved" + "line": 1549, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 1792, "source_tile": [ 9, 12 ], "arch": "invis_exit", - "form": "unresolved" + "line": 1792, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 1833, "source_tile": [ 9, 17 ], "arch": "invis_exit", - "form": "unresolved" + "line": 1833, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 1895, "source_tile": [ 9, 22 ], "arch": "invis_exit", - "form": "unresolved" + "line": 1895, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 2113, "source_tile": [ 10, 22 ], "arch": "invis_exit", - "form": "unresolved" + "line": 2113, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 2284, "source_tile": [ 11, 17 ], "arch": "invis_exit", - "form": "unresolved" + "line": 2284, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 2365, "source_tile": [ 11, 27 ], "arch": "exit_green", - "form": "unresolved" + "line": 2365, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/exit.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 2634, "source_tile": [ 12, 22 ], "arch": "invis_exit", - "form": "unresolved" + "line": 2634, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 2887, "source_tile": [ 13, 22 ], "arch": "invis_exit", - "form": "unresolved" + "line": 2887, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/fees_checker.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/house/luxury_house_0_0", - "line": 11690, "source_tile": [ 46, 26 ], "arch": "exit", - "form": "unresolved" + "line": 11690, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/exit.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 351, "source_tile": [ 1, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 16, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 351, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 362, "source_tile": [ 1, 20 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 16, 8 - ] + ], + "arch": "stairs_stone_up", + "line": 362, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 415, "source_tile": [ 2, 1 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 415, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 476, "source_tile": [ 2, 7 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 476, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 541, "source_tile": [ 2, 13 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 541, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 2557, "source_tile": [ 12, 19 ], - "arch": "stairs_red_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", "target_tile": [ 5, 11 - ] + ], + "arch": "stairs_red_down", + "line": 2557, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 2619, "source_tile": [ 13, 1 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 2619, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 2688, "source_tile": [ 13, 7 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 2688, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 2757, "source_tile": [ 13, 13 ], "arch": "ladder_up", - "form": "tiled-unresolved" + "line": 2757, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", - "line": 2806, "source_tile": [ 13, 19 ], - "arch": "stairs_red_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", "target_tile": [ 6, 11 - ] + ], + "arch": "stairs_red_down", + "line": 2806, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", - "line": 149, "source_tile": [ 1, 11 ], "arch": "exit", - "form": "unresolved" + "line": 149, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/generic/jail_exit.py" + ] }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", - "line": 518, "source_tile": [ 5, 11 ], - "arch": "stairs_red_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", "target_tile": [ 12, 19 - ] + ], + "arch": "stairs_red_up", + "line": 518, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail_a", - "line": 649, "source_tile": [ 6, 11 ], - "arch": "stairs_red_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", "target_tile": [ 13, 19 - ] + ], + "arch": "stairs_red_up", + "line": 649, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", - "line": 181, "source_tile": [ 1, 1 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_57", "target_tile": [ 7, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 181, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", - "line": 372, "source_tile": [ 2, 1 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_57", "target_tile": [ 8, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 372, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", - "line": 503, "source_tile": [ 3, 1 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_57", "target_tile": [ 9, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 503, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", - "line": 1505, "source_tile": [ 10, 18 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_stone_down", + "line": 1505, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", - "line": 1517, "source_tile": [ 10, 19 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", "target_tile": [ 1, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 1517, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", - "line": 296, "source_tile": [ 1, 18 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", "target_tile": [ 10, 18 - ] + ], + "arch": "stairs_stone_up", + "line": 296, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0202", - "line": 307, "source_tile": [ 1, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0202", "target_tile": [ 10, 19 - ] + ], + "arch": "stairs_stone_up", + "line": 307, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", - "line": 2595, "source_tile": [ 17, 2 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", "target_tile": [ 21, 2 - ] + ], + "arch": "exit", + "line": 2595, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", - "line": 3018, "source_tile": [ 20, 1 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0203", "target_tile": [ 18, 11 - ] + ], + "arch": "exit", + "line": 3018, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0204", - "line": 778, "source_tile": [ 8, 19 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/world_5_57_-2", "target_tile": [ 14, 14 - ] + ], + "arch": "well_stone", + "line": 778, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_5_57_-2 (14,14)" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 135, "source_tile": [ 6, 7 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 6, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 135, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 154, "source_tile": [ 6, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 6, 8 - ] + ], + "arch": "invis_exit", + "line": 154, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 229, "source_tile": [ 7, 7 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 7, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 229, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 242, "source_tile": [ 7, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 7, 8 - ] + ], + "arch": "invis_exit", + "line": 242, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 941, "source_tile": [ 11, 21 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 22, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 941, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 954, "source_tile": [ 11, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 954, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 959, "source_tile": [ 11, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 22, 2 - ] + ], + "arch": "invis_exit", + "line": 959, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1148, "source_tile": [ 12, 21 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 23, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 1148, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1161, "source_tile": [ 12, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1161, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1166, "source_tile": [ 12, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 23, 2 - ] + ], + "arch": "invis_exit", + "line": 1166, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1727, "source_tile": [ 16, 7 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 16, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 1727, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1746, "source_tile": [ 16, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 16, 8 - ] + ], + "arch": "invis_exit", + "line": 1746, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1792, "source_tile": [ 17, 7 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 17, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 1792, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", - "line": 1805, "source_tile": [ 17, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 17, 8 - ] + ], + "arch": "invis_exit", + "line": 1805, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 343, "source_tile": [ 6, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 343, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 348, "source_tile": [ 6, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 6, 7 - ] + ], + "arch": "invis_exit", + "line": 348, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 363, "source_tile": [ 6, 8 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 6, 7 - ] - }, - { - "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 506, + ], + "arch": "stairs_stone_up", + "line": 363, + "form": "explicit", + "vertical": "up" + }, + { + "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "source_tile": [ 7, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 506, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 511, "source_tile": [ 7, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 7, 7 - ] + ], + "arch": "invis_exit", + "line": 511, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 526, "source_tile": [ 7, 8 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 7, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 526, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 578, "source_tile": [ 7, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 18, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 578, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 591, "source_tile": [ 7, 20 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 591, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 596, "source_tile": [ 7, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 18, 2 - ] + ], + "arch": "invis_exit", + "line": 596, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 608, "source_tile": [ 7, 21 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 608, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 629, "source_tile": [ 7, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 629, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 722, "source_tile": [ 8, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 19, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 722, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 735, "source_tile": [ 8, 20 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 735, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 740, "source_tile": [ 8, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-1_57", "target_tile": [ 19, 2 - ] + ], + "arch": "invis_exit", + "line": 740, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 759, "source_tile": [ 8, 21 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 759, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 787, "source_tile": [ 8, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 787, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1412, "source_tile": [ 15, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_57", "target_tile": [ 2, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 1412, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1425, "source_tile": [ 15, 20 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1425, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1430, "source_tile": [ 15, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_0_57", "target_tile": [ 2, 2 - ] + ], + "arch": "invis_exit", + "line": 1430, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1442, "source_tile": [ 15, 21 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1442, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1470, "source_tile": [ 15, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1470, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1504, "source_tile": [ 16, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1504, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1509, "source_tile": [ 16, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 16, 7 - ] + ], + "arch": "invis_exit", + "line": 1509, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1524, "source_tile": [ 16, 8 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 16, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 1524, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1576, "source_tile": [ 16, 19 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_57", "target_tile": [ 3, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 1576, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1589, "source_tile": [ 16, 20 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1589, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1594, "source_tile": [ 16, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_0_57", "target_tile": [ 3, 2 - ] + ], + "arch": "invis_exit", + "line": 1594, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1613, "source_tile": [ 16, 21 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1613, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1641, "source_tile": [ 16, 22 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1641, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1675, "source_tile": [ 17, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1675, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1680, "source_tile": [ 17, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 17, 7 - ] + ], + "arch": "invis_exit", + "line": 1680, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", - "line": 1695, "source_tile": [ 17, 8 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 17, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 1695, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", - "line": 1397, "source_tile": [ 12, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", "target_tile": [ 12, 15 - ] + ], + "arch": "invis_exit", + "line": 1397, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", - "line": 1408, "source_tile": [ 12, 16 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", "target_tile": [ 12, 15 - ] + ], + "arch": "stairs_stone_down", + "line": 1408, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", - "line": 1527, "source_tile": [ 13, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", "target_tile": [ 13, 15 - ] + ], + "arch": "invis_exit", + "line": 1527, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", - "line": 1538, "source_tile": [ 13, 16 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", "target_tile": [ 13, 15 - ] + ], + "arch": "stairs_stone_down", + "line": 1538, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", - "line": 204, "source_tile": [ 2, 5 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", "target_tile": [ 2, 6 - ] + ], + "arch": "stairs_stone_down", + "line": 204, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", - "line": 217, "source_tile": [ 2, 6 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", "target_tile": [ 2, 6 - ] + ], + "arch": "invis_exit", + "line": 217, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", - "line": 2704, "source_tile": [ 17, 14 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", "target_tile": [ 17, 16 + ], + "arch": "exit", + "line": 2704, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", - "line": 1104, "source_tile": [ 12, 15 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", "target_tile": [ 12, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 1104, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", - "line": 1117, "source_tile": [ 12, 16 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", "target_tile": [ 12, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 1117, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", - "line": 1209, "source_tile": [ 13, 15 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", "target_tile": [ 13, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 1209, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_0104", - "line": 1222, "source_tile": [ 13, 16 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0104", "target_tile": [ 13, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 1222, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", - "line": 214, "source_tile": [ 2, 5 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", "target_tile": [ 2, 5 - ] + ], + "arch": "stairs_stone_up", + "line": 214, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_b_aa04", - "line": 228, "source_tile": [ 2, 6 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_aa04", "target_tile": [ 2, 5 - ] + ], + "arch": "stairs_stone_up", + "line": 228, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_0", - "line": 193, "source_tile": [ 1, 1 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_65", "target_tile": [ 2, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 193, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", - "line": 2972, "source_tile": [ 22, 12 ], - "arch": "portal_green", - "form": "explicit", "target_map": "/shattered_islands/world_2_69", "target_tile": [ 0, 7 + ], + "arch": "portal_green", + "line": 2972, + "form": "explicit", + "vertical": "none", + "event_scripts": [ + "scripts/lake_portal.py" ] }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1_-2", - "line": 3718, "source_tile": [ 22, 21 ], - "arch": "portal_yellow", - "form": "explicit", "target_map": "/shattered_islands/world_6_67_-1", "target_tile": [ 7, 9 - ] + ], + "arch": "portal_yellow", + "line": 3718, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", - "line": 267, "source_tile": [ 4, 1 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", "target_tile": [ 4, 1 - ] + ], + "arch": "stairs_down", + "line": 267, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", - "line": 275, "source_tile": [ 4, 2 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", "target_tile": [ 4, 2 - ] + ], + "arch": "stairs_down", + "line": 275, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", - "line": 336, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", "target_tile": [ 4, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 336, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", - "line": 345, "source_tile": [ 5, 2 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", "target_tile": [ 4, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 345, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", - "line": 414, "source_tile": [ 4, 1 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 414, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", - "line": 423, "source_tile": [ 4, 2 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", "target_tile": [ 5, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 423, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", - "line": 518, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 518, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-3", - "line": 527, "source_tile": [ 5, 2 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_1_-2_-2", "target_tile": [ 5, 2 - ] + ], + "arch": "stairs_stone_up2", + "line": 527, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", - "line": 2656, "source_tile": [ 17, 2 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", "target_tile": [ 18, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 2656, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", - "line": 2664, "source_tile": [ 17, 3 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", "target_tile": [ 18, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2664, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", - "line": 2803, "source_tile": [ 18, 2 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", "target_tile": [ 18, 2 - ] + ], + "arch": "stairs_down", + "line": 2803, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", - "line": 2811, "source_tile": [ 18, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", "target_tile": [ 18, 3 - ] + ], + "arch": "stairs_down", + "line": 2811, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", - "line": 443, "source_tile": [ 2, 20 ], - "arch": "exit_green", - "form": "same-map", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", "target_tile": [ 11, 20 - ] + ], + "arch": "exit_green", + "line": 443, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", - "line": 3605, "source_tile": [ 17, 2 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", "target_tile": [ 17, 2 - ] + ], + "arch": "stairs_stone_up2", + "line": 3605, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", - "line": 3613, "source_tile": [ 17, 3 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", "target_tile": [ 17, 3 - ] + ], + "arch": "stairs_stone_up2", + "line": 3613, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", - "line": 3751, "source_tile": [ 18, 2 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", "target_tile": [ 17, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 3751, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-2", - "line": 3759, "source_tile": [ 18, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_2_3_-1", "target_tile": [ 17, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 3759, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_3_3_-2", - "line": 33, "source_tile": [ 0, 2 ], - "arch": "exit", - "form": "explicit", "target_map": "/planes/bone_cairn/bone_cairn_0_0", "target_tile": [ 10, 15 - ] + ], + "arch": "exit", + "line": 33, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", - "line": 1679, "source_tile": [ 13, 16 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", "target_tile": [ 13, 16 - ] + ], + "arch": "stairs_down", + "line": 1679, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", - "line": 1693, "source_tile": [ 13, 17 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", "target_tile": [ 13, 17 - ] + ], + "arch": "stairs_down", + "line": 1693, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", - "line": 1815, "source_tile": [ 14, 16 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", "target_tile": [ 13, 16 - ] + ], + "arch": "stairs_stone_down", + "line": 1815, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", - "line": 1824, "source_tile": [ 14, 17 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", "target_tile": [ 13, 17 - ] + ], + "arch": "stairs_stone_down", + "line": 1824, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", - "line": 1604, "source_tile": [ 13, 16 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", "target_tile": [ 14, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 1604, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", - "line": 1613, "source_tile": [ 13, 17 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", "target_tile": [ 14, 17 - ] + ], + "arch": "stairs_stone_up", + "line": 1613, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", - "line": 1721, "source_tile": [ 14, 16 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", "target_tile": [ 14, 16 - ] + ], + "arch": "stairs_stone_up2", + "line": 1721, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4_-1", - "line": 1734, "source_tile": [ 14, 17 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_4_4", "target_tile": [ 14, 17 - ] + ], + "arch": "stairs_stone_up2", + "line": 1734, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_2_-1", - "line": 192, "source_tile": [ 2, 3 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_67_-1", "target_tile": [ 7, 17 - ] + ], + "arch": "stairs_stone_up", + "line": 192, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_3_-1", - "line": 247, "source_tile": [ 3, 13 ], - "arch": "exit_green", - "form": "same-map", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_3_-1", "target_tile": [ 4, 8 - ] + ], + "arch": "exit_green", + "line": 247, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0", - "line": 1011, "source_tile": [ 7, 7 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_7_67_-1", "target_tile": [ 16, 9 - ] + ], + "arch": "exit_green", + "line": 1011, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", - "line": 433, "source_tile": [ 4, 2 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-3", "target_tile": [ 17, 22 - ] - }, + ], + "arch": "exit_green", + "line": 433, + "form": "explicit", + "vertical": "none" + }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 311, "source_tile": [ 3, 0 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 3, 0 - ] + ], + "arch": "stairs_down", + "line": 311, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 318, "source_tile": [ 3, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 3, 0 - ] + ], + "arch": "stairs_stone_down", + "line": 318, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 379, "source_tile": [ 4, 0 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 4, 0 - ] + ], + "arch": "stairs_down", + "line": 379, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 386, "source_tile": [ 4, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 4, 0 - ] + ], + "arch": "stairs_stone_down", + "line": 386, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 446, "source_tile": [ 5, 0 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 5, 0 - ] + ], + "arch": "stairs_down", + "line": 446, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", - "line": 453, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", "target_tile": [ 5, 0 - ] + ], + "arch": "stairs_stone_down", + "line": 453, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 245, "source_tile": [ 3, 0 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 3, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 245, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 253, "source_tile": [ 3, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 3, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 253, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 401, "source_tile": [ 4, 0 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 4, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 401, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 412, "source_tile": [ 4, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 4, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 412, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 544, "source_tile": [ 5, 0 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 544, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-3", - "line": 552, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_1_-2", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 552, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", - "line": 293, "source_tile": [ 2, 1 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", "target_tile": [ 2, 1 - ] + ], + "arch": "stairs_down", + "line": 293, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", - "line": 437, "source_tile": [ 3, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", "target_tile": [ 2, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 437, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", - "line": 377, "source_tile": [ 2, 1 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", "target_tile": [ 3, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 377, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", - "line": 509, "source_tile": [ 3, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-1", "target_tile": [ 3, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 509, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-3", - "line": 2634, "source_tile": [ 17, 21 ], - "arch": "grave_cross2.111", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", "target_tile": [ 4, 3 - ] + ], + "arch": "grave_cross2.111", + "line": 2634, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", - "line": 66, "source_tile": [ 0, 18 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", "target_tile": [ 0, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 66, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", - "line": 73, "source_tile": [ 0, 19 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", "target_tile": [ 0, 19 - ] + ], + "arch": "stairs_down", + "line": 73, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", - "line": 172, "source_tile": [ 1, 18 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", "target_tile": [ 1, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 172, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", - "line": 181, "source_tile": [ 1, 19 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", "target_tile": [ 1, 19 - ] + ], + "arch": "stairs_down", + "line": 181, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", - "line": 88, "source_tile": [ 0, 18 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", "target_tile": [ 0, 18 - ] + ], + "arch": "stairs_stone_up2", + "line": 88, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", - "line": 95, "source_tile": [ 0, 19 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", "target_tile": [ 0, 18 - ] + ], + "arch": "stairs_stone_up", + "line": 95, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", - "line": 196, "source_tile": [ 1, 18 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_stone_up2", + "line": 196, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", - "line": 205, "source_tile": [ 1, 19 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_stone_up", + "line": 205, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_-10_54", - "line": 2097, "source_tile": [ 14, 12 ], "arch": "hole", - "form": "unresolved" + "line": 2097, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-10_54", - "line": 2207, "source_tile": [ 15, 6 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 2207, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-1_44", - "line": 1592, "source_tile": [ 10, 6 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1592, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_-1_44", - "line": 1607, "source_tile": [ 10, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1607, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_-1_44", - "line": 1733, "source_tile": [ 11, 6 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1733, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_-1_44", - "line": 1748, "source_tile": [ 11, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 1748, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 2800, "source_tile": [ 18, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 7, 19 - ] + ], + "arch": "invis_exit", + "line": 2800, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 2811, "source_tile": [ 18, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 7, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 2811, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 2950, "source_tile": [ 19, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 8, 19 - ] + ], + "arch": "invis_exit", + "line": 2950, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 2961, "source_tile": [ 19, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 8, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 2961, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 3405, "source_tile": [ 22, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 11, 21 - ] + ], + "arch": "invis_exit", + "line": 3405, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 3416, "source_tile": [ 22, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 11, 21 - ] + ], + "arch": "stairs_stone_down", + "line": 3416, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 3547, "source_tile": [ 23, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 12, 21 - ] + ], + "arch": "invis_exit", + "line": 3547, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_-1_57", - "line": 3558, "source_tile": [ 23, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", "target_tile": [ 12, 21 - ] + ], + "arch": "stairs_stone_down", + "line": 3558, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-4_56_-1", - "line": 2495, "source_tile": [ 14, 8 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/world_-4_56_-2", "target_tile": [ 14, 8 - ] + ], + "arch": "hole", + "line": 2495, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-4_56_-2", - "line": 1537, "source_tile": [ 17, 23 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-1", "target_tile": [ 12, 7 - ] + ], + "arch": "exit", + "line": 1537, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-1", - "line": 1648, "source_tile": [ 9, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-2", "target_tile": [ 9, 11 - ] + ], + "arch": "invis_exit", + "line": 1648, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-1", - "line": 1666, "source_tile": [ 9, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-2", "target_tile": [ 9, 12 - ] + ], + "arch": "invis_exit", + "line": 1666, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-1", - "line": 3712, "source_tile": [ 20, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-2", "target_tile": [ 20, 11 - ] + ], + "arch": "invis_exit", + "line": 3712, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-1", - "line": 3730, "source_tile": [ 20, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-2", "target_tile": [ 20, 12 - ] + ], + "arch": "invis_exit", + "line": 3730, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-2", - "line": 56, "source_tile": [ 8, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-1", "target_tile": [ 8, 12 - ] + ], + "arch": "invis_exit", + "line": 56, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-2", - "line": 74, "source_tile": [ 8, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-1", "target_tile": [ 8, 13 - ] + ], + "arch": "invis_exit", + "line": 74, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-2", - "line": 1058, "source_tile": [ 21, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-1", "target_tile": [ 21, 11 - ] + ], + "arch": "invis_exit", + "line": 1058, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_56_-2", - "line": 1076, "source_tile": [ 21, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-5_56_-1", "target_tile": [ 21, 12 - ] + ], + "arch": "invis_exit", + "line": 1076, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-5_76", - "line": 2436, "source_tile": [ 12, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", "target_tile": [ 10, 3 - ] + ], + "arch": "invis_exit", + "line": 2436, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_-5_76", - "line": 2454, "source_tile": [ 12, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", "target_tile": [ 10, 3 - ] + ], + "arch": "invis_exit", + "line": 2454, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_-5_76", - "line": 2472, "source_tile": [ 12, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/deserted_tutorial_island/mushroom_cavern", "target_tile": [ 10, 3 - ] + ], + "arch": "invis_exit", + "line": 2472, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_-6_56_-1", - "line": 1066, "source_tile": [ 7, 17 ], "arch": "hole", - "form": "unresolved" + "line": 1066, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-6_58", - "line": 1201, "source_tile": [ 7, 23 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_-6_58_-1", "target_tile": [ 19, 22 - ] + ], + "arch": "exit", + "line": 1201, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-6_58_-1", - "line": 3170, "source_tile": [ 19, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-6_58", "target_tile": [ 5, 20 - ] + ], + "arch": "invis_exit", + "line": 3170, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-6_58_-1", - "line": 3377, "source_tile": [ 20, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-6_58", "target_tile": [ 5, 20 - ] + ], + "arch": "invis_exit", + "line": 3377, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-6_79", - "line": 1139, "source_tile": [ 7, 7 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/incuna/ship_lower_deck", "target_tile": [ 2, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 1139, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-7_54", - "line": 1105, "source_tile": [ 7, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-7_54_-1", "target_tile": [ 8, 10 - ] + ], + "arch": "invis_exit", + "line": 1105, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-7_54_-1", - "line": 921, "source_tile": [ 7, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_-7_54", "target_tile": [ 8, 10 - ] + ], + "arch": "invis_exit", + "line": 921, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_-7_76", - "line": 1192, "source_tile": [ 8, 14 ], "arch": "stairs_wood_down", - "form": "tiled-unresolved" + "line": 1192, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-7_76", - "line": 1456, "source_tile": [ 9, 18 ], "arch": "hole2", - "form": "unresolved" + "line": 1456, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-7_76", - "line": 1563, "source_tile": [ 10, 11 ], "arch": "hole2", - "form": "unresolved" + "line": 1563, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_-7_76", - "line": 1747, "source_tile": [ 11, 12 ], "arch": "hole2", - "form": "unresolved" + "line": 1747, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_51", - "line": 1411, "source_tile": [ 10, 21 ], - "arch": "ladder_tall_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_51_1", "target_tile": [ 10, 21 - ] + ], + "arch": "ladder_tall_up", + "line": 1411, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_51", - "line": 2485, "source_tile": [ 18, 23 ], - "arch": "ladder_tall_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_51_1", "target_tile": [ 18, 23 - ] + ], + "arch": "ladder_tall_up", + "line": 2485, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_51_1", - "line": 83, "source_tile": [ 10, 21 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_0_51", "target_tile": [ 10, 21 - ] + ], + "arch": "ladder_down2", + "line": 83, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_51_1", - "line": 174, "source_tile": [ 12, 20 ], "arch": "ladder_up", - "form": "unresolved" + "line": 174, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_51_1", - "line": 286, "source_tile": [ 18, 23 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_0_51", "target_tile": [ 18, 23 - ] + ], + "arch": "ladder_down2", + "line": 286, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_51_1", - "line": 353, "source_tile": [ 20, 22 ], "arch": "ladder_up", - "form": "unresolved" + "line": 353, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_52", - "line": 790, "source_tile": [ 4, 16 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 4, 16 - ] + ], + "arch": "shop_mat", + "line": 790, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_0_52", - "line": 1231, "source_tile": [ 6, 16 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 6, 16 - ] + ], + "arch": "shop_mat", + "line": 1231, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_0_52", - "line": 1788, "source_tile": [ 10, 3 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/world_0_52_-1", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_wood_down", + "line": 1788, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_52", - "line": 1839, "source_tile": [ 10, 12 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_52_1", "target_tile": [ 11, 11 - ] + ], + "arch": "stairs_wood_up", + "line": 1839, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_52", - "line": 1948, "source_tile": [ 11, 3 ], - "arch": "stairs_down", - "form": "explicit", "target_map": "/shattered_islands/world_0_52_-1", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_down", + "line": 1948, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_52", - "line": 1999, "source_tile": [ 11, 12 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_0_52_1", "target_tile": [ 11, 11 - ] + ], + "arch": "stairs_wood_up2", + "line": 1999, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_52_-1", - "line": 1212, "source_tile": [ 11, 3 ], - "arch": "stairs_wood_up2", - "form": "explicit", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_wood_up2", + "line": 1212, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_52_-1", - "line": 1228, "source_tile": [ 11, 4 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_wood_up", + "line": 1228, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_52_1", - "line": 1473, "source_tile": [ 11, 11 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 10, 12 - ] + ], + "arch": "stairs_wood_down", + "line": 1473, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_52_1", - "line": 1485, "source_tile": [ 11, 12 ], - "arch": "stairs_down", - "form": "explicit", "target_map": "/shattered_islands/world_0_52", "target_tile": [ 10, 12 - ] + ], + "arch": "stairs_down", + "line": 1485, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_57", - "line": 319, "source_tile": [ 2, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 15, 19 - ] + ], + "arch": "invis_exit", + "line": 319, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_0_57", - "line": 330, "source_tile": [ 2, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 15, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 330, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_57", - "line": 474, "source_tile": [ 3, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 16, 19 - ] + ], + "arch": "invis_exit", + "line": 474, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_0_57", - "line": 485, "source_tile": [ 3, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", "target_tile": [ 16, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 485, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_65", - "line": 2258, "source_tile": [ 17, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/giant_stronghold/giant_stronghold_0201", "target_tile": [ 21, 22 - ] + ], + "arch": "invis_exit", + "line": 2258, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_0_66", - "line": 902, "source_tile": [ 6, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 902, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 919, "source_tile": [ 6, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 919, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 936, "source_tile": [ 6, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 936, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1055, "source_tile": [ 7, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1055, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1072, "source_tile": [ 7, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1072, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1085, "source_tile": [ 7, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1085, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1102, "source_tile": [ 7, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1102, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1119, "source_tile": [ 7, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1119, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1227, "source_tile": [ 8, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1227, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1240, "source_tile": [ 8, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1240, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1253, "source_tile": [ 8, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1253, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1266, "source_tile": [ 8, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1266, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1283, "source_tile": [ 8, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1283, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1300, "source_tile": [ 8, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1300, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1427, "source_tile": [ 9, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1427, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1444, "source_tile": [ 9, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1444, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1457, "source_tile": [ 9, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1457, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1470, "source_tile": [ 9, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1470, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1483, "source_tile": [ 9, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1483, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1500, "source_tile": [ 9, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1500, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1517, "source_tile": [ 9, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1517, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1640, "source_tile": [ 10, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1640, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1657, "source_tile": [ 10, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1657, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1670, "source_tile": [ 10, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1670, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1683, "source_tile": [ 10, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1683, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1696, "source_tile": [ 10, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1696, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1713, "source_tile": [ 10, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1713, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1730, "source_tile": [ 10, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1730, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1838, "source_tile": [ 11, 11 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1838, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1855, "source_tile": [ 11, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1855, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1868, "source_tile": [ 11, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1868, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1881, "source_tile": [ 11, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1881, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1894, "source_tile": [ 11, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1894, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1911, "source_tile": [ 11, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1911, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 1928, "source_tile": [ 11, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 1928, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2040, "source_tile": [ 12, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2040, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2057, "source_tile": [ 12, 13 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2057, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2074, "source_tile": [ 12, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2074, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2087, "source_tile": [ 12, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2087, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2100, "source_tile": [ 12, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2100, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2117, "source_tile": [ 12, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2117, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2134, "source_tile": [ 12, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2134, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2249, "source_tile": [ 13, 14 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2249, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2266, "source_tile": [ 13, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2266, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2279, "source_tile": [ 13, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2279, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2292, "source_tile": [ 13, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2292, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2309, "source_tile": [ 13, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2309, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2326, "source_tile": [ 13, 19 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2326, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2455, "source_tile": [ 14, 15 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2455, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2472, "source_tile": [ 14, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2472, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2485, "source_tile": [ 14, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2485, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2498, "source_tile": [ 14, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2498, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2515, "source_tile": [ 14, 19 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2515, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2532, "source_tile": [ 14, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2532, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2656, "source_tile": [ 15, 16 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2656, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2673, "source_tile": [ 15, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2673, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2686, "source_tile": [ 15, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2686, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2699, "source_tile": [ 15, 19 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2699, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2716, "source_tile": [ 15, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2716, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2733, "source_tile": [ 15, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2733, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 2863, "source_tile": [ 16, 17 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2863, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 2880, "source_tile": [ 16, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2880, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 2893, "source_tile": [ 16, 19 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2893, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 2906, "source_tile": [ 16, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2906, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 2919, "source_tile": [ 16, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2919, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 2936, "source_tile": [ 16, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2936, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3048, "source_tile": [ 17, 18 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3048, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3065, "source_tile": [ 17, 19 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3065, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3082, "source_tile": [ 17, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3082, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3095, "source_tile": [ 17, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3095, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3108, "source_tile": [ 17, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3108, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3130, "source_tile": [ 17, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3130, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3247, "source_tile": [ 18, 20 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3247, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3264, "source_tile": [ 18, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3264, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3277, "source_tile": [ 18, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3277, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3294, "source_tile": [ 18, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3294, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3435, "source_tile": [ 19, 21 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3435, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "vertical_inferred_from": "reciprocal rope_up at /shattered_islands/strakewood_island/brynknot/abyss/abyss (21,43)" }, { "source_map": "/shattered_islands/world_0_66", - "line": 3452, "source_tile": [ 19, 22 - ], - "arch": "invis_exit", - "form": "explicit", + ], "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3452, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3465, "source_tile": [ 19, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3465, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3615, "source_tile": [ 20, 22 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3615, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3632, "source_tile": [ 20, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3632, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_66", - "line": 3764, "source_tile": [ 21, 23 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3764, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 1386, "source_tile": [ 8, 16 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_0_67_1", "target_tile": [ 8, 16 - ] + ], + "arch": "stairs_wood_up2", + "line": 1386, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_67", - "line": 1400, "source_tile": [ 8, 17 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_67_1", "target_tile": [ 8, 16 - ] + ], + "arch": "stairs_wood_up", + "line": 1400, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_67", - "line": 2866, "source_tile": [ 18, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 2866, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3032, "source_tile": [ 19, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3032, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3048, "source_tile": [ 19, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3048, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3205, "source_tile": [ 20, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3205, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3221, "source_tile": [ 20, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3221, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3238, "source_tile": [ 20, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3238, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3255, "source_tile": [ 20, 3 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3255, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3421, "source_tile": [ 21, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3421, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3433, "source_tile": [ 21, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3433, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3450, "source_tile": [ 21, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3450, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3467, "source_tile": [ 21, 3 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3467, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3616, "source_tile": [ 22, 0 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3616, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3632, "source_tile": [ 22, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3632, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67", - "line": 3649, "source_tile": [ 22, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/abyss/abyss", "target_tile": [ 21, 43 - ] + ], + "arch": "invis_exit", + "line": 3649, + "form": "explicit", + "vertical": "none", + "per_player_last_eat_1": true }, { "source_map": "/shattered_islands/world_0_67_1", - "line": 98, "source_tile": [ 8, 16 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_67", "target_tile": [ 8, 17 - ] + ], + "arch": "stairs_wood_down", + "line": 98, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_67_1", - "line": 107, "source_tile": [ 8, 17 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_67", "target_tile": [ 8, 17 - ] + ], + "arch": "stairs_down", + "line": 107, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_67_1", - "line": 821, "source_tile": [ 20, 16 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_67_2", "target_tile": [ 20, 16 - ] + ], + "arch": "ladder_up", + "line": 821, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_67_2", - "line": 827, "source_tile": [ 20, 16 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_0_67_1", "target_tile": [ 20, 16 - ] + ], + "arch": "ladder_down2", + "line": 827, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68", - "line": 629, "source_tile": [ 4, 9 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 4, 8 + ], + "arch": "exit", + "line": 629, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/world_0_68", - "line": 1510, "source_tile": [ 10, 21 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_-1", "target_tile": [ 10, 21 - ] + ], + "arch": "ladder_down3", + "line": 1510, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68", - "line": 2348, "source_tile": [ 16, 9 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 16, 9 - ] + ], + "arch": "stairs_stone_up2", + "line": 2348, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 2357, "source_tile": [ 16, 10 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 16, 9 - ] + ], + "arch": "stairs_stone_up", + "line": 2357, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 2521, "source_tile": [ 17, 9 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 17, 9 - ] + ], + "arch": "stairs_stone_up2", + "line": 2521, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 2530, "source_tile": [ 17, 10 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 17, 9 - ] + ], + "arch": "stairs_stone_up", + "line": 2530, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 3257, "source_tile": [ 21, 7 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 21, 7 - ] + ], + "arch": "ladder_up", + "line": 3257, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 3303, "source_tile": [ 21, 9 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 21, 9 - ] + ], + "arch": "ladder_up", + "line": 3303, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 3339, "source_tile": [ 21, 11 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 21, 11 - ] + ], + "arch": "ladder_up", + "line": 3339, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68", - "line": 3375, "source_tile": [ 21, 13 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68_1", "target_tile": [ 21, 13 - ] + ], + "arch": "ladder_up", + "line": 3375, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68_-1", - "line": 676, "source_tile": [ 10, 21 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 10, 21 - ] + ], + "arch": "ladder_up", + "line": 676, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 372, "source_tile": [ 15, 13 ], "arch": "ladder_up", - "form": "unresolved" + "line": 372, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 438, "source_tile": [ 15, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 438, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 480, "source_tile": [ 16, 9 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 16, 10 - ] + ], + "arch": "stairs_stone_down", + "line": 480, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 489, "source_tile": [ 16, 10 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 16, 10 - ] + ], + "arch": "stairs_down", + "line": 489, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 567, "source_tile": [ 17, 9 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 17, 10 - ] + ], + "arch": "stairs_stone_down", + "line": 567, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 576, "source_tile": [ 17, 10 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_68", "target_tile": [ 17, 10 - ] + ], + "arch": "stairs_down", + "line": 576, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 636, "source_tile": [ 17, 13 ], "arch": "ladder_up", - "form": "unresolved" + "line": 636, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 697, "source_tile": [ 17, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 697, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 907, "source_tile": [ 19, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 907, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 1108, "source_tile": [ 21, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 1108, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 1321, "source_tile": [ 23, 13 ], "arch": "ladder_up", - "form": "unresolved" + "line": 1321, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_68_1", - "line": 1382, "source_tile": [ 23, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 1382, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_69", - "line": 2534, "source_tile": [ 15, 9 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_0_69", "target_tile": [ 15, 9 - ] + ], + "arch": "shop_mat", + "line": 2534, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_0_69", - "line": 2550, "source_tile": [ 15, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_0_69", "target_tile": [ 15, 11 - ] + ], + "arch": "shop_mat", + "line": 2550, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_0_69_-1", - "line": 1158, "source_tile": [ 15, 1 ], - "arch": "sewer_hole2", - "form": "same-map", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 15, 1 - ] + ], + "arch": "sewer_hole2", + "line": 1158, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_69_-1", - "line": 1263, "source_tile": [ 15, 11 ], - "arch": "sewer_hole2", - "form": "tiled", "target_map": "/shattered_islands/world_0_69_-2", "target_tile": [ 15, 11 - ] + ], + "arch": "sewer_hole2", + "line": 1263, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_69_-1", - "line": 2449, "source_tile": [ 23, 1 ], - "arch": "sewer_hole2", - "form": "same-map", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 23, 1 - ] + ], + "arch": "sewer_hole2", + "line": 2449, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_69_-1", - "line": 2550, "source_tile": [ 23, 11 ], - "arch": "sewer_hole2", - "form": "same-map", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 23, 11 - ] + ], + "arch": "sewer_hole2", + "line": 2550, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_69_-2", - "line": 1598, "source_tile": [ 15, 1 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 15, 1 - ] + ], + "arch": "ladder_up", + "line": 1598, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_69_-2", - "line": 1656, "source_tile": [ 15, 11 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 15, 11 - ] + ], + "arch": "ladder_up", + "line": 1656, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_69_-2", - "line": 2650, "source_tile": [ 23, 1 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 23, 1 - ] + ], + "arch": "ladder_up", + "line": 2650, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_69_-2", - "line": 2716, "source_tile": [ 23, 11 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_69_-1", "target_tile": [ 23, 11 - ] + ], + "arch": "ladder_up", + "line": 2716, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_70", - "line": 255, "source_tile": [ 1, 13 ], - "arch": "stairs_wood_down", - "form": "same-map", "target_map": "/shattered_islands/world_0_70", "target_tile": [ 1, 13 - ] + ], + "arch": "stairs_wood_down", + "line": 255, + "form": "same-map", + "vertical": "down", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_0_70", - "line": 2835, "source_tile": [ 20, 16 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 1, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 2835, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_71_-2", - "line": 330, "source_tile": [ 1, 21 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_71_-3", "target_tile": [ 1, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 330, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_71_-2", - "line": 339, "source_tile": [ 1, 22 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_0_71_-3", "target_tile": [ 1, 22 - ] + ], + "arch": "stairs_down", + "line": 339, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_0_71_-3", - "line": 58, "source_tile": [ 1, 21 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_0_71_-2", "target_tile": [ 1, 21 - ] + ], + "arch": "stairs_stone_up2", + "line": 58, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_71_-3", - "line": 73, "source_tile": [ 1, 22 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_0_71_-2", "target_tile": [ 1, 21 - ] + ], + "arch": "stairs_stone_up", + "line": 73, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_0_83", - "line": 1452, "source_tile": [ 8, 16 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/incuna/ship_lower_deck_to_strakewood", "target_tile": [ 2, 11 - ] + ], + "arch": "stairs_wood_down", + "line": 1452, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_10_42_1", - "line": 163, "source_tile": [ 2, 19 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_10_42_2", "target_tile": [ 2, 20 - ] + ], + "arch": "stairs_wood_up", + "line": 163, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_10_42_1", - "line": 172, "source_tile": [ 2, 20 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_10_42_2", "target_tile": [ 2, 20 - ] + ], + "arch": "stairs_wood_up2", + "line": 172, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_10_42_2", - "line": 168, "source_tile": [ 2, 19 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_10_42_1", "target_tile": [ 2, 19 - ] + ], + "arch": "stairs_down", + "line": 168, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_10_42_2", - "line": 176, "source_tile": [ 2, 20 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_10_42_1", "target_tile": [ 2, 19 - ] + ], + "arch": "stairs_wood_down", + "line": 176, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_10_43", - "line": 2878, "source_tile": [ 19, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_10_43_-2", "target_tile": [ 19, 2 - ] + ], + "arch": "invis_exit", + "line": 2878, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "co-located stairs_stone_down" }, { "source_map": "/shattered_islands/world_10_43", - "line": 3024, "source_tile": [ 20, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_10_43_-2", "target_tile": [ 19, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 3024, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_10_43_-2", - "line": 696, "source_tile": [ 19, 2 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_10_43", "target_tile": [ 20, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 696, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_10_43_-2", - "line": 737, "source_tile": [ 20, 2 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 737, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_10_43_-2", - "line": 742, "source_tile": [ 20, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_10_43", "target_tile": [ 20, 2 - ] + ], + "arch": "invis_exit", + "line": 742, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "co-located stairs_stone_up" }, { "source_map": "/shattered_islands/world_10_44", - "line": 275, "source_tile": [ 1, 20 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_10_44", "target_tile": [ 1, 20 - ] + ], + "arch": "shop_mat", + "line": 275, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_44", - "line": 617, "source_tile": [ 3, 20 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_10_44", "target_tile": [ 3, 20 - ] + ], + "arch": "shop_mat", + "line": 617, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_67", - "line": 221, "source_tile": [ 1, 9 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_10_67", "target_tile": [ 1, 9 - ] + ], + "arch": "shop_mat", + "line": 221, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_67", - "line": 241, "source_tile": [ 1, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_10_67", "target_tile": [ 1, 11 - ] + ], + "arch": "shop_mat", + "line": 241, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_68", - "line": 19, "source_tile": [ 0, 0 ], "arch": "exit", - "form": "unresolved" + "line": 19, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "scripts/strakewood_island/greyton/arena_exit.py" + ] }, { "source_map": "/shattered_islands/world_10_68", - "line": 232, "source_tile": [ 2, 0 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 1, 11 - ] + ], + "arch": "exit", + "line": 232, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_68", - "line": 1447, "source_tile": [ 11, 11 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 5, 5 - ] + ], + "arch": "exit", + "line": 1447, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_10_69", - "line": 2016, "source_tile": [ 12, 15 ], "arch": "exit", - "form": "unresolved" + "line": 2016, + "form": "unresolved", + "vertical": "none", + "event_scripts": [ + "/python/houses/exit.py" + ] }, { "source_map": "/shattered_islands/world_11_61", - "line": 668, "source_tile": [ 4, 10 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/berri_lur/temple_loki/tol_0201", "target_tile": [ 4, 10 - ] + ], + "arch": "stairs_stone_down", + "line": 668, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_67", - "line": 232, "source_tile": [ 1, 12 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 1, 11 - ] + ], + "arch": "exit", + "line": 232, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_11_67", - "line": 363, "source_tile": [ 2, 7 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_11_67", "target_tile": [ 3, 7 - ] + ], + "arch": "exit", + "line": 363, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_11_67", - "line": 540, "source_tile": [ 3, 9 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 540, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_67", - "line": 673, "source_tile": [ 4, 5 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_10_68", "target_tile": [ 12, 12 - ] + ], + "arch": "exit", + "line": 673, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_11_67", - "line": 1633, "source_tile": [ 10, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_10_68", "target_tile": [ 12, 16 + ], + "arch": "exit", + "line": 1633, + "form": "explicit", + "vertical": "none", + "event_scripts": [ + "scripts/strakewood_island/greyton/greyton.arena" ] }, { "source_map": "/shattered_islands/world_11_67", - "line": 2589, "source_tile": [ 16, 7 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", "target_tile": [ 1, 19 - ] + ], + "arch": "stairs_stone_down", + "line": 2589, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_67", - "line": 2600, "source_tile": [ 16, 8 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/greyton/jail/jail", "target_tile": [ 1, 20 - ] + ], + "arch": "stairs_stone_down", + "line": 2600, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_68", - "line": 2745, "source_tile": [ 20, 3 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_1", "target_tile": [ 20, 3 - ] + ], + "arch": "ladder_up", + "line": 2745, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_11_68", - "line": 2940, "source_tile": [ 20, 20 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_1", "target_tile": [ 20, 20 - ] + ], + "arch": "ladder_up", + "line": 2940, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_11_68_1", - "line": 205, "source_tile": [ 20, 3 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_11_68", "target_tile": [ 20, 3 - ] + ], + "arch": "ladder_down2", + "line": 205, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_68_1", - "line": 236, "source_tile": [ 20, 7 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_2", "target_tile": [ 20, 7 - ] + ], + "arch": "ladder_up", + "line": 236, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_11_68_1", - "line": 289, "source_tile": [ 20, 16 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_2", "target_tile": [ 20, 16 - ] + ], + "arch": "ladder_up", + "line": 289, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_11_68_1", - "line": 320, "source_tile": [ 20, 20 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_11_68", "target_tile": [ 20, 20 - ] + ], + "arch": "ladder_down2", + "line": 320, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_68_2", - "line": 273, "source_tile": [ 20, 7 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_1", "target_tile": [ 20, 7 - ] + ], + "arch": "ladder_down2", + "line": 273, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_68_2", - "line": 334, "source_tile": [ 20, 16 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_11_68_1", "target_tile": [ 20, 16 - ] + ], + "arch": "ladder_down2", + "line": 334, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_11_79", - "line": 1130, "source_tile": [ 7, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/cave_morlarg", "target_tile": [ 21, 4 - ] + ], + "arch": "invis_exit", + "line": 1130, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_11_79", - "line": 1147, "source_tile": [ 7, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/cave_morlarg", "target_tile": [ 21, 5 - ] + ], + "arch": "invis_exit", + "line": 1147, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_12_4", - "line": 2058, "source_tile": [ 12, 10 ], "arch": "stairs_wood_down", - "form": "tiled-unresolved" + "line": 2058, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_12_4", - "line": 2742, "source_tile": [ 15, 22 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_12_4_1", "target_tile": [ 15, 22 - ] + ], + "arch": "stairs_wood_up2", + "line": 2742, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_12_4", - "line": 2903, "source_tile": [ 16, 22 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_12_4_1", "target_tile": [ 15, 22 - ] + ], + "arch": "stairs_wood_up", + "line": 2903, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_12_4_1", - "line": 694, "source_tile": [ 15, 22 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_12_4", "target_tile": [ 16, 22 - ] + ], + "arch": "stairs_wood_down", + "line": 694, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_12_4_1", - "line": 843, "source_tile": [ 16, 22 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_12_4", "target_tile": [ 16, 22 - ] + ], + "arch": "stairs_down", + "line": 843, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_12_63", - "line": 1920, "source_tile": [ 15, 13 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_12_63", "target_tile": [ 15, 13 - ] + ], + "arch": "shop_mat", + "line": 1920, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_12_63", - "line": 1940, "source_tile": [ 15, 15 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_12_63", "target_tile": [ 15, 15 - ] + ], + "arch": "shop_mat", + "line": 1940, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_12_64", - "line": 2382, "source_tile": [ 19, 7 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 2382, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_12_68", - "line": 1327, "source_tile": [ 10, 22 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 17, 18 - ] + ], + "arch": "stairs_wood_down", + "line": 1327, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_12_68", - "line": 2679, "source_tile": [ 20, 12 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 12, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 2679, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_13_6", - "line": 904, "source_tile": [ 5, 0 ], "arch": "stairs_wood_up", - "form": "unresolved" + "line": 904, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_13_6", - "line": 912, "source_tile": [ 5, 1 ], "arch": "stairs_wood_up", - "form": "unresolved" + "line": 912, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_13_62", - "line": 1148, "source_tile": [ 7, 10 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 1148, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_13_64", - "line": 885, "source_tile": [ 6, 14 ], "arch": "hole", - "form": "unresolved" + "line": 885, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_13_7", - "line": 1443, "source_tile": [ 12, 8 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", "target_tile": [ 8, 25 + ], + "arch": "stairs_stone_down", + "line": 1443, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "event_scripts": [ + "scripts/lyondale_archipelago/morliana/hierro_lair.py" ] }, { "source_map": "/shattered_islands/world_13_7", - "line": 1563, "source_tile": [ 13, 8 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", "target_tile": [ 9, 25 + ], + "arch": "stairs_stone_down", + "line": 1563, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "event_scripts": [ + "scripts/lyondale_archipelago/morliana/hierro_lair.py" ] }, { "source_map": "/shattered_islands/world_13_7", - "line": 1698, "source_tile": [ 14, 8 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/lyondale_archipelago/morliana/hierro/hierro_lair", "target_tile": [ 10, 25 + ], + "arch": "stairs_stone_down", + "line": 1698, + "form": "explicit", + "vertical": "down", + "per_player_last_eat_1": true, + "event_scripts": [ + "scripts/lyondale_archipelago/morliana/hierro_lair.py" ] }, { "source_map": "/shattered_islands/world_14_5", - "line": 2530, "source_tile": [ 14, 13 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_14_5_1", "target_tile": [ 14, 13 - ] + ], + "arch": "stairs_wood_up2", + "line": 2530, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_14_5", - "line": 2539, "source_tile": [ 14, 14 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_14_5_1", "target_tile": [ 14, 13 - ] + ], + "arch": "stairs_wood_up", + "line": 2539, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_14_5_1", - "line": 586, "source_tile": [ 14, 10 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_14_5_1", "target_tile": [ 14, 10 - ] + ], + "arch": "shop_mat", + "line": 586, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_14_5_1", - "line": 602, "source_tile": [ 14, 13 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_14_5", "target_tile": [ 14, 14 - ] + ], + "arch": "stairs_wood_down", + "line": 602, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_14_5_1", - "line": 611, "source_tile": [ 14, 14 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_14_5", "target_tile": [ 14, 14 - ] + ], + "arch": "stairs_down", + "line": 611, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_14_5_1", - "line": 769, "source_tile": [ 16, 10 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_14_5_1", "target_tile": [ 16, 10 - ] + ], + "arch": "shop_mat", + "line": 769, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_14_64", - "line": 2380, "source_tile": [ 20, 11 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 17, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 2380, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_14_71", - "line": 1908, "source_tile": [ 14, 4 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", "target_tile": [ 2, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 1908, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_14_71", - "line": 2040, "source_tile": [ 15, 4 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/hemlock_cave/hemlock_cave_0101", "target_tile": [ 3, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 2040, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_15_4", - "line": 2320, "source_tile": [ 15, 12 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_2", "target_tile": [ 1, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 2320, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_17_2", - "line": 1083, "source_tile": [ 5, 5 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_2", "target_tile": [ 4, 4 - ] + ], + "arch": "exit_green", + "line": 1083, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2", - "line": 1191, "source_tile": [ 5, 17 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_5", "target_tile": [ 4, 19 - ] + ], + "arch": "exit_green", + "line": 1191, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2", - "line": 3384, "source_tile": [ 17, 5 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_3", "target_tile": [ 19, 4 - ] + ], + "arch": "exit_green", + "line": 3384, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2", - "line": 3492, "source_tile": [ 17, 17 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_4", "target_tile": [ 19, 19 - ] + ], + "arch": "exit_green", + "line": 3492, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_2", - "line": 700, "source_tile": [ 3, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2", "target_tile": [ 6, 6 - ] + ], + "arch": "exit_green", + "line": 700, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_2", - "line": 872, "source_tile": [ 3, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_5", "target_tile": [ 4, 19 - ] + ], + "arch": "exit_green", + "line": 872, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_2", - "line": 4061, "source_tile": [ 20, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_3", "target_tile": [ 19, 4 - ] + ], + "arch": "exit_green", + "line": 4061, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_2", - "line": 4233, "source_tile": [ 20, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_4", "target_tile": [ 19, 19 - ] + ], + "arch": "exit_green", + "line": 4233, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_3", - "line": 696, "source_tile": [ 3, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_2", "target_tile": [ 4, 4 - ] + ], + "arch": "exit_green", + "line": 696, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_3", - "line": 878, "source_tile": [ 3, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_5", "target_tile": [ 4, 19 - ] + ], + "arch": "exit_green", + "line": 878, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_3", - "line": 4306, "source_tile": [ 20, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2", "target_tile": [ 16, 6 - ] + ], + "arch": "exit_green", + "line": 4306, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_3", - "line": 4488, "source_tile": [ 20, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_4", "target_tile": [ 19, 19 - ] + ], + "arch": "exit_green", + "line": 4488, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_4", - "line": 612, "source_tile": [ 3, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_2", "target_tile": [ 4, 4 - ] + ], + "arch": "exit_green", + "line": 612, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_4", - "line": 784, "source_tile": [ 3, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_5", "target_tile": [ 4, 19 - ] + ], + "arch": "exit_green", + "line": 784, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_4", - "line": 2554, "source_tile": [ 13, 5 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_19_4", "target_tile": [ 5, 22 - ] + ], + "arch": "exit", + "line": 2554, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_4", - "line": 4191, "source_tile": [ 20, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_3", "target_tile": [ 19, 4 - ] + ], + "arch": "exit_green", + "line": 4191, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_4", - "line": 4363, "source_tile": [ 20, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2", "target_tile": [ 16, 16 - ] + ], + "arch": "exit_green", + "line": 4363, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_5", - "line": 746, "source_tile": [ 3, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_2", "target_tile": [ 4, 4 - ] + ], + "arch": "exit_green", + "line": 746, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_5", - "line": 968, "source_tile": [ 3, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2", "target_tile": [ 6, 16 - ] + ], + "arch": "exit_green", + "line": 968, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_5", - "line": 3969, "source_tile": [ 20, 3 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_3", "target_tile": [ 19, 4 - ] + ], + "arch": "exit_green", + "line": 3969, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_2_5", - "line": 4119, "source_tile": [ 20, 20 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_4", "target_tile": [ 19, 19 - ] + ], + "arch": "exit_green", + "line": 4119, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_17_3", - "line": 1601, "source_tile": [ 11, 18 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 17, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 1601, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_18_3", - "line": 899, "source_tile": [ 6, 17 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_2", "target_tile": [ 6, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 899, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_18_4", - "line": 1958, "source_tile": [ 19, 19 ], "arch": "stairs_stone_down", - "form": "dynamic" + "line": 1958, + "form": "dynamic", + "vertical": "down" }, { "source_map": "/shattered_islands/world_19_4", - "line": 773, "source_tile": [ 6, 22 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_17_2_4", "target_tile": [ 13, 4 - ] + ], + "arch": "exit_green", + "line": 773, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_19_72", - "line": 40, "source_tile": [ 0, 4 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", "target_tile": [ 9, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 40, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_19_72", - "line": 142, "source_tile": [ 1, 4 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/vielumin_ruins/ruins_0201", "target_tile": [ 10, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 142, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_47", - "line": 2740, "source_tile": [ 23, 15 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_47_-1", "target_tile": [ 23, 15 - ] + ], + "arch": "stairs_down", + "line": 2740, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_47_-1", - "line": 2427, "source_tile": [ 23, 15 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_47", "target_tile": [ 0, 15 + ], + "arch": "stairs_stone_up", + "line": 2427, + "form": "tiled", + "vertical": "up", + "authored_target": [ + "/shattered_islands/world_1_47", + 24, + 15 ] }, { "source_map": "/shattered_islands/world_1_50", - "line": 2559, "source_tile": [ 19, 23 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_51_-1", "target_tile": [ 19, 0 + ], + "arch": "stairs_stone_down", + "line": 2559, + "form": "tiled", + "vertical": "down", + "authored_target": [ + "/shattered_islands/world_1_50_-1", + 19, + 24 ] }, { "source_map": "/shattered_islands/world_1_50_-1", - "line": 2305, "source_tile": [ 19, 23 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_1_50", "target_tile": [ 19, 23 - ] + ], + "arch": "stairs_stone_up2", + "line": 2305, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_51", - "line": 2218, "source_tile": [ 19, 0 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_51_-1", "target_tile": [ 19, 0 - ] + ], + "arch": "stairs_down", + "line": 2218, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_51_-1", - "line": 2310, "source_tile": [ 19, 0 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_50", "target_tile": [ 19, 23 + ], + "arch": "stairs_stone_up", + "line": 2310, + "form": "tiled", + "vertical": "up", + "authored_target": [ + "/shattered_islands/world_1_51", + 19, + -1 ] }, { "source_map": "/shattered_islands/world_1_67", - "line": 2774, "source_tile": [ 16, 16 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_1_67_1", "target_tile": [ 16, 16 - ] + ], + "arch": "stairs_wood_up2", + "line": 2774, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_67", - "line": 2783, "source_tile": [ 16, 17 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_67_1", "target_tile": [ 16, 16 - ] + ], + "arch": "stairs_wood_up", + "line": 2783, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_67_1", - "line": 1057, "source_tile": [ 16, 16 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_67", "target_tile": [ 16, 17 - ] + ], + "arch": "stairs_wood_down", + "line": 1057, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_67_1", - "line": 1066, "source_tile": [ 16, 17 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_67", "target_tile": [ 16, 17 - ] + ], + "arch": "stairs_down", + "line": 1066, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_68", - "line": 73, "source_tile": [ 0, 7 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_1", "target_tile": [ 0, 7 - ] + ], + "arch": "ladder_up", + "line": 73, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68", - "line": 103, "source_tile": [ 0, 9 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_1", "target_tile": [ 0, 9 - ] + ], + "arch": "ladder_up", + "line": 103, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68", - "line": 133, "source_tile": [ 0, 11 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_1", "target_tile": [ 0, 11 - ] + ], + "arch": "ladder_up", + "line": 133, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68", - "line": 163, "source_tile": [ 0, 13 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_1", "target_tile": [ 0, 13 - ] + ], + "arch": "ladder_up", + "line": 163, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68", - "line": 193, "source_tile": [ 0, 15 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_1", "target_tile": [ 0, 15 - ] + ], + "arch": "ladder_up", + "line": 193, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68", - "line": 766, "source_tile": [ 5, 0 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_-1", "target_tile": [ 5, 0 - ] + ], + "arch": "ladder_down3", + "line": 766, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_68", - "line": 916, "source_tile": [ 5, 21 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_-1", "target_tile": [ 5, 21 - ] + ], + "arch": "ladder_down3", + "line": 916, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_68", - "line": 3015, "source_tile": [ 22, 7 ], - "arch": "exit", - "form": "explicit", "target_map": "/hall_of_dms", "target_tile": [ 5, 19 - ] + ], + "arch": "exit", + "line": 3015, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_68_-1", - "line": 491, "source_tile": [ 5, 0 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68", "target_tile": [ 5, 0 - ] + ], + "arch": "ladder_up", + "line": 491, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_68_-1", - "line": 2785, "source_tile": [ 23, 5 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_-2", "target_tile": [ 23, 5 - ] + ], + "arch": "hole", + "line": 2785, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_68_-2", - "line": 994, "source_tile": [ 23, 5 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_68_-1", "target_tile": [ 23, 5 - ] + ], + "arch": "ladder_up", + "line": 994, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_69", - "line": 801, "source_tile": [ 5, 17 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_1_69_-1", "target_tile": [ 5, 17 - ] + ], + "arch": "ladder_down3", + "line": 801, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_69_-1", - "line": 965, "source_tile": [ 5, 17 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_69", "target_tile": [ 5, 17 - ] + ], + "arch": "ladder_up", + "line": 965, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_69_-1", - "line": 1683, "source_tile": [ 15, 16 ], "arch": "sewer_hole2", - "form": "unresolved" + "line": 1683, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_69_-2", - "line": 2029, "source_tile": [ 15, 16 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_69_-1", "target_tile": [ 15, 16 - ] + ], + "arch": "ladder_up", + "line": 2029, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_70_-2", - "line": 1674, "source_tile": [ 13, 6 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_1_70_-3", "target_tile": [ 13, 6 - ] + ], + "arch": "hole", + "line": 1674, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_70_-3", - "line": 799, "source_tile": [ 13, 6 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/world_1_70_-2", "target_tile": [ 13, 7 - ] + ], + "arch": "ladder_up", + "line": 799, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_71_-2", - "line": 1061, "source_tile": [ 7, 11 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_71_-2", "target_tile": [ 7, 13 + ], + "arch": "exit", + "line": 1061, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "strakewood_island/brynknot/sewers/scripts/rocks_puzzle.py" ] }, { "source_map": "/shattered_islands/world_1_73", - "line": 2367, "source_tile": [ 10, 7 ], "arch": "stairs_stone_up", - "form": "tiled-unresolved" + "line": 2367, + "form": "tiled-unresolved", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_73", - "line": 2384, "source_tile": [ 10, 8 ], "arch": "stairs_stone_down", - "form": "dynamic" + "line": 2384, + "form": "dynamic", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_73", - "line": 4830, "source_tile": [ 22, 13 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_73_-1", "target_tile": [ 22, 13 - ] + ], + "arch": "ladder_down", + "line": 4830, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_73_-1", - "line": 122, "source_tile": [ 1, 20 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "ladder_down", + "line": 122, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_1_73_-1", - "line": 1274, "source_tile": [ 22, 13 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_73", "target_tile": [ 22, 13 - ] + ], + "arch": "ladder_up", + "line": 1274, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 146, "source_tile": [ 1, 8 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 146, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 156, "source_tile": [ 1, 9 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 156, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 202, "source_tile": [ 1, 20 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_1_73_-1", "target_tile": [ 1, 20 - ] + ], + "arch": "ladder_up", + "line": 202, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 309, "source_tile": [ 2, 14 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 309, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 877, "source_tile": [ 6, 10 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 877, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 903, "source_tile": [ 6, 14 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 903, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 913, "source_tile": [ 6, 15 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 913, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 943, "source_tile": [ 6, 20 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 943, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 953, "source_tile": [ 6, 21 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 953, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 996, "source_tile": [ 7, 3 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 996, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1156, "source_tile": [ 8, 3 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1156, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1370, "source_tile": [ 9, 6 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1370, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1380, "source_tile": [ 9, 7 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1380, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1422, "source_tile": [ 9, 14 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1422, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1456, "source_tile": [ 9, 21 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1456, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1638, "source_tile": [ 10, 21 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1638, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1666, "source_tile": [ 11, 1 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1666, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 1716, "source_tile": [ 11, 10 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 1716, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2108, "source_tile": [ 13, 18 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2108, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2130, "source_tile": [ 13, 21 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_4_73", "target_tile": [ 20, 13 - ] + ], + "arch": "exit", + "line": 2130, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2207, "source_tile": [ 14, 8 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2207, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2233, "source_tile": [ 14, 11 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2233, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2271, "source_tile": [ 14, 18 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2271, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2339, "source_tile": [ 15, 4 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2339, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2369, "source_tile": [ 15, 8 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2369, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2395, "source_tile": [ 15, 11 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2395, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 2518, "source_tile": [ 16, 4 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 2518, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 3058, "source_tile": [ 19, 14 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 3058, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 3068, "source_tile": [ 19, 15 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 1, 20 - ] + ], + "arch": "invis_exit", + "line": 3068, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_1_73_-2", - "line": 3541, "source_tile": [ 22, 15 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 22, 11 - ] + ], + "arch": "invis_exit", + "line": 3541, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_47", - "line": 93, "source_tile": [ 0, 15 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_1_47_-1", "target_tile": [ 23, 15 + ], + "arch": "stairs_stone_down", + "line": 93, + "form": "tiled", + "vertical": "down", + "authored_target": [ + "/shattered_islands/world_2_47_-1", + -1, + 15 ] }, { "source_map": "/shattered_islands/world_2_47", - "line": 2605, "source_tile": [ 17, 4 ], "arch": "ladder_up", - "form": "unresolved" + "line": 2605, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_2_47", - "line": 2642, "source_tile": [ 17, 6 ], "arch": "ladder_up", - "form": "unresolved" + "line": 2642, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_2_47", - "line": 3103, "source_tile": [ 20, 4 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3103, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_2_47", - "line": 3140, "source_tile": [ 20, 6 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3140, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_2_47_-1", - "line": 90, "source_tile": [ 0, 15 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_2_47", "target_tile": [ 0, 15 - ] + ], + "arch": "stairs_stone_up2", + "line": 90, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_50_-1", - "line": 1262, "source_tile": [ 11, 16 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_50_-2", "target_tile": [ 11, 17 - ] + ], + "arch": "stairs_stone_down", + "line": 1262, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_50_-1", - "line": 1271, "source_tile": [ 11, 17 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_50_-2", "target_tile": [ 11, 17 - ] + ], + "arch": "stairs_down", + "line": 1271, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_50_-2", - "line": 104, "source_tile": [ 11, 16 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_2_50_-1", "target_tile": [ 11, 16 - ] + ], + "arch": "stairs_stone_up2", + "line": 104, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_50_-2", - "line": 117, "source_tile": [ 11, 17 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_50_-1", "target_tile": [ 11, 16 - ] + ], + "arch": "stairs_stone_up", + "line": 117, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-1", - "line": 742, "source_tile": [ 6, 6 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 7, 6 - ] + ], + "arch": "invis_exit", + "line": 742, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_51_-1", - "line": 864, "source_tile": [ 7, 5 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 7, 6 - ] + ], + "arch": "invis_exit", + "line": 864, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_51_-1", - "line": 885, "source_tile": [ 7, 6 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 7, 6 - ] + ], + "arch": "invis_exit", + "line": 885, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1829, "source_tile": [ 21, 4 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 22, 12 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1829, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1852, "source_tile": [ 21, 5 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 22, 12 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1852, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1870, "source_tile": [ 21, 6 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 20, 4 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1870, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1900, "source_tile": [ 21, 7 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 20, 4 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1900, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1922, "source_tile": [ 21, 8 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 20, 4 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1922, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1950, "source_tile": [ 21, 9 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 22, 12 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1950, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 1973, "source_tile": [ 21, 10 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 22, 12 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1973, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 2179, "source_tile": [ 22, 11 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 20, 4 - ] + ], + "arch": "bollard_tied_s_2", + "line": 2179, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_51_-2", - "line": 2201, "source_tile": [ 22, 12 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_2_51_-2", "target_tile": [ 20, 4 - ] + ], + "arch": "bollard_tied_s_2", + "line": 2201, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_52_-2", - "line": 250, "source_tile": [ 17, 7 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-3", "target_tile": [ 12, 8 - ] + ], + "arch": "invis_exit", + "line": 250, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_52_-2", - "line": 268, "source_tile": [ 17, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-3", "target_tile": [ 12, 9 - ] + ], + "arch": "invis_exit", + "line": 268, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_52_-2", - "line": 286, "source_tile": [ 17, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-3", "target_tile": [ 12, 10 - ] + ], + "arch": "invis_exit", + "line": 286, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_52_-3", - "line": 1234, "source_tile": [ 13, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-2", "target_tile": [ 18, 7 - ] + ], + "arch": "invis_exit", + "line": 1234, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_52_-3", - "line": 1253, "source_tile": [ 13, 9 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-2", "target_tile": [ 18, 8 - ] + ], + "arch": "invis_exit", + "line": 1253, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_52_-3", - "line": 1272, "source_tile": [ 13, 10 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_2_52_-2", "target_tile": [ 18, 9 - ] + ], + "arch": "invis_exit", + "line": 1272, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_53", - "line": 295, "source_tile": [ 2, 3 ], "arch": "ladder_down", - "form": "tiled-unresolved" + "line": 295, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_60", - "line": 955, "source_tile": [ 7, 22 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_60_-1", "target_tile": [ 7, 22 - ] + ], + "arch": "ladder_down2", + "line": 955, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_60", - "line": 1638, "source_tile": [ 12, 22 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_2_60_-1", "target_tile": [ 12, 22 - ] + ], + "arch": "ladder_down3", + "line": 1638, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_60", - "line": 2334, "source_tile": [ 17, 22 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_60_-1", "target_tile": [ 17, 22 - ] + ], + "arch": "ladder_down2", + "line": 2334, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_60_-1", - "line": 110, "source_tile": [ 7, 22 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_60", "target_tile": [ 7, 22 - ] + ], + "arch": "ladder_up", + "line": 110, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_60_-1", - "line": 342, "source_tile": [ 12, 22 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_60", "target_tile": [ 12, 22 - ] + ], + "arch": "ladder_up", + "line": 342, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_60_-1", - "line": 530, "source_tile": [ 17, 22 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_60", "target_tile": [ 17, 22 - ] + ], + "arch": "ladder_up", + "line": 530, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_61", - "line": 919, "source_tile": [ 6, 15 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_61_-1", "target_tile": [ 6, 15 - ] + ], + "arch": "ladder_down2", + "line": 919, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_61", - "line": 1761, "source_tile": [ 11, 13 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_2_61_-1", "target_tile": [ 11, 13 - ] + ], + "arch": "ladder_down3", + "line": 1761, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_61", - "line": 2458, "source_tile": [ 16, 14 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_61_-1", "target_tile": [ 16, 14 - ] + ], + "arch": "ladder_down2", + "line": 2458, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_61_-1", - "line": 78, "source_tile": [ 6, 15 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_61", "target_tile": [ 6, 15 - ] + ], + "arch": "ladder_up", + "line": 78, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_61_-1", - "line": 387, "source_tile": [ 11, 13 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_61", "target_tile": [ 11, 13 - ] + ], + "arch": "ladder_up", + "line": 387, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_61_-1", - "line": 621, "source_tile": [ 16, 14 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_61", "target_tile": [ 16, 14 - ] + ], + "arch": "ladder_up", + "line": 621, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68", - "line": 280, "source_tile": [ 1, 15 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 1, 15 - ] + ], + "arch": "stairs_wood_up2", + "line": 280, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68", - "line": 289, "source_tile": [ 1, 16 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 1, 15 - ] + ], + "arch": "stairs_wood_up", + "line": 289, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68", - "line": 1011, "source_tile": [ 5, 9 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 6, 9 - ] + ], + "arch": "stairs_stone_up", + "line": 1011, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68", - "line": 1199, "source_tile": [ 6, 9 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 6, 9 - ] + ], + "arch": "stairs_stone_up2", + "line": 1199, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68", - "line": 2167, "source_tile": [ 12, 21 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_-1", "target_tile": [ 12, 21 - ] + ], + "arch": "ladder_down3", + "line": 2167, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_-1", - "line": 1127, "source_tile": [ 12, 21 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 12, 21 - ] + ], + "arch": "ladder_up", + "line": 1127, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 159, "source_tile": [ 1, 15 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 1, 16 - ] + ], + "arch": "stairs_wood_down", + "line": 159, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 168, "source_tile": [ 1, 16 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 1, 16 - ] + ], + "arch": "stairs_down", + "line": 168, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 607, "source_tile": [ 5, 5 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_2", "target_tile": [ 5, 5 - ] + ], + "arch": "ladder_up", + "line": 607, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 627, "source_tile": [ 5, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 5, 9 - ] + ], + "arch": "stairs_down", + "line": 627, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 712, "source_tile": [ 5, 21 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_2", "target_tile": [ 5, 21 - ] + ], + "arch": "stairs_wood_up2", + "line": 712, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 760, "source_tile": [ 6, 9 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68", "target_tile": [ 5, 9 - ] + ], + "arch": "stairs_stone_down", + "line": 760, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_1", - "line": 825, "source_tile": [ 6, 21 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_2", "target_tile": [ 5, 21 - ] + ], + "arch": "stairs_wood_up", + "line": 825, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68_2", - "line": 499, "source_tile": [ 4, 5 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_3", "target_tile": [ 4, 5 - ] + ], + "arch": "ladder_up", + "line": 499, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_68_2", - "line": 609, "source_tile": [ 5, 5 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 5, 5 - ] + ], + "arch": "ladder_down2", + "line": 609, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_2", - "line": 701, "source_tile": [ 5, 21 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 6, 21 - ] + ], + "arch": "stairs_wood_down", + "line": 701, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_2", - "line": 850, "source_tile": [ 6, 21 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_1", "target_tile": [ 6, 21 - ] + ], + "arch": "stairs_down", + "line": 850, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_68_3", - "line": 282, "source_tile": [ 4, 5 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_2_68_2", "target_tile": [ 4, 5 - ] + ], + "arch": "ladder_down2", + "line": 282, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_69", - "line": 48, "source_tile": [ 0, 6 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1", "target_tile": [ 23, 13 - ] + ], + "arch": "exit_green", + "line": 48, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_2_69", - "line": 58, "source_tile": [ 0, 7 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_2_69_-1", "target_tile": [ 0, 7 - ] + ], + "arch": "hole", + "line": 58, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_69_-1", - "line": 61, "source_tile": [ 0, 7 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_2_69", "target_tile": [ 0, 7 - ] + ], + "arch": "ladder_up", + "line": 61, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_2_70", - "line": 789, "source_tile": [ 5, 12 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_2", "target_tile": [ 1, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 789, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_74", - "line": 1365, "source_tile": [ 9, 21 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 6, 18 - ] + ], + "arch": "stairs_wood_down", + "line": 1365, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_83", - "line": 2231, "source_tile": [ 16, 12 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 2231, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_2_87", - "line": 1138, "source_tile": [ 7, 7 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/incuna/ship_lower_deck_to_incuna", "target_tile": [ 2, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 1138, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_43", - "line": 374, "source_tile": [ 2, 20 ], "arch": "ladder_down3", - "form": "tiled-unresolved" + "line": 374, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_44", - "line": 1926, "source_tile": [ 9, 18 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_44_-1", "target_tile": [ 9, 18 - ] + ], + "arch": "stairs_down", + "line": 1926, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_44", - "line": 1937, "source_tile": [ 9, 19 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_44_-1", "target_tile": [ 9, 18 - ] + ], + "arch": "stairs_stone_down", + "line": 1937, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_44_-1", - "line": 486, "source_tile": [ 9, 18 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_3_44", "target_tile": [ 9, 19 - ] + ], + "arch": "stairs_stone_up", + "line": 486, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_44_-1", - "line": 495, "source_tile": [ 9, 19 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_3_44", "target_tile": [ 9, 19 - ] + ], + "arch": "stairs_stone_up2", + "line": 495, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_46", - "line": 2212, "source_tile": [ 16, 14 ], "arch": "ladder_down3", - "form": "tiled-unresolved" + "line": 2212, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_56", - "line": 2326, "source_tile": [ 13, 22 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/world_3_56_-2", "target_tile": [ 13, 22 - ] + ], + "arch": "well_stone", + "line": 2326, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_3_56_-2 (13,22)" }, { "source_map": "/shattered_islands/world_3_56_-2", - "line": 698, "source_tile": [ 13, 22 ], - "arch": "well_bucket", - "form": "explicit", "target_map": "/shattered_islands/world_3_56", "target_tile": [ 13, 22 - ] + ], + "arch": "well_bucket", + "line": 698, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_59", - "line": 1504, "source_tile": [ 10, 21 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 1504, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_60", - "line": 2207, "source_tile": [ 17, 5 ], "arch": "hole", - "form": "unresolved" + "line": 2207, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_66_-2", - "line": 4164, "source_tile": [ 23, 12 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/brynknot/sewers/lab", "target_tile": [ 19, 13 + ], + "arch": "exit", + "line": 4164, + "form": "explicit", + "vertical": "none", + "event_scripts": [ + "strakewood_island/brynknot/sewers/scripts/nyhelobo_portal.py" ] }, { "source_map": "/shattered_islands/world_3_70", - "line": 648, "source_tile": [ 6, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/dark_cave/dark_cave_0102", "target_tile": [ 1, 1 - ] + ], + "arch": "invis_exit", + "line": 648, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2076, "source_tile": [ 13, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 13, 3 - ] + ], + "arch": "stairs_down", + "line": 2076, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2092, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 13, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2092, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2265, "source_tile": [ 14, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 14, 3 - ] + ], + "arch": "stairs_down", + "line": 2265, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2277, "source_tile": [ 14, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 14, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2277, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2454, "source_tile": [ 15, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 15, 3 - ] + ], + "arch": "stairs_down", + "line": 2454, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82", - "line": 2466, "source_tile": [ 15, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_82_-1", "target_tile": [ 15, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2466, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 663, "source_tile": [ 13, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 663, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 673, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 673, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 773, "source_tile": [ 14, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 14, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 773, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 783, "source_tile": [ 14, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 14, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 783, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 862, "source_tile": [ 15, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 15, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 862, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_82_-1", - "line": 872, "source_tile": [ 15, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_3_82", "target_tile": [ 15, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 872, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_83", - "line": 2555, "source_tile": [ 15, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_83_-2", "target_tile": [ 15, 2 - ] + ], + "arch": "invis_exit", + "line": 2555, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_83", - "line": 3068, "source_tile": [ 18, 2 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_83_-2", "target_tile": [ 18, 2 - ] + ], + "arch": "invis_exit", + "line": 3068, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_83_-2", - "line": 516, "source_tile": [ 14, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_83", "target_tile": [ 14, 1 - ] + ], + "arch": "invis_exit", + "line": 516, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_83_-2", - "line": 591, "source_tile": [ 14, 9 ], "arch": "ladder_down", - "form": "tiled-unresolved" + "line": 591, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_83_-2", - "line": 1289, "source_tile": [ 18, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_83", "target_tile": [ 18, 1 - ] + ], + "arch": "invis_exit", + "line": 1289, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_83_-2", - "line": 1467, "source_tile": [ 19, 1 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_3_83", "target_tile": [ 19, 1 - ] + ], + "arch": "invis_exit", + "line": 1467, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_3_85_-1", - "line": 201, "source_tile": [ 8, 8 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_85_-2", "target_tile": [ 8, 9 - ] + ], + "arch": "stairs_wood_down", + "line": 201, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_85_-1", - "line": 210, "source_tile": [ 8, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_3_85_-2", "target_tile": [ 8, 9 - ] + ], + "arch": "stairs_down", + "line": 210, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_3_85_-2", - "line": 1279, "source_tile": [ 8, 8 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_3_85_-1", "target_tile": [ 8, 8 - ] + ], + "arch": "stairs_wood_up2", + "line": 1279, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_3_85_-2", - "line": 1288, "source_tile": [ 8, 9 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_3_85_-1", "target_tile": [ 8, 8 - ] + ], + "arch": "stairs_wood_up", + "line": 1288, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_45", - "line": 3213, "source_tile": [ 18, 4 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_4_45", "target_tile": [ 19, 4 + ], + "arch": "exit", + "line": 3213, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/world_4_48", - "line": 3054, "source_tile": [ 22, 11 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_48_1", "target_tile": [ 22, 11 - ] + ], + "arch": "stairs_wood_up2", + "line": 3054, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_48", - "line": 3063, "source_tile": [ 22, 12 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_48_1", "target_tile": [ 22, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 3063, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_48_1", - "line": 96, "source_tile": [ 22, 11 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_48", "target_tile": [ 22, 12 - ] + ], + "arch": "stairs_wood_down", + "line": 96, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_48_1", - "line": 105, "source_tile": [ 22, 12 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_48", "target_tile": [ 22, 12 - ] + ], + "arch": "stairs_down", + "line": 105, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_50", - "line": 800, "source_tile": [ 5, 4 ], "arch": "ladder_up", - "form": "unresolved" + "line": 800, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_50", - "line": 837, "source_tile": [ 5, 6 ], "arch": "ladder_up", - "form": "unresolved" + "line": 837, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_50", - "line": 1318, "source_tile": [ 8, 4 ], "arch": "ladder_up", - "form": "unresolved" + "line": 1318, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_50", - "line": 1355, "source_tile": [ 8, 6 ], "arch": "ladder_up", - "form": "unresolved" + "line": 1355, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_52", - "line": 1250, "source_tile": [ 8, 12 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 1250, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_52", - "line": 1613, "source_tile": [ 10, 16 ], - "arch": "ladder_tall_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_52_1", "target_tile": [ 9, 16 - ] + ], + "arch": "ladder_tall_up", + "line": 1613, + "form": "explicit", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_52", - "line": 1626, "source_tile": [ 10, 17 ], - "arch": "ladder_tall_up", - "form": "explicit", "target_map": "/shattered_islands/world_4_52_1", "target_tile": [ 9, 17 - ] + ], + "arch": "ladder_tall_up", + "line": 1626, + "form": "explicit", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_52_1", - "line": 947, "source_tile": [ 10, 15 ], - "arch": "invis_exit", - "form": "tiled", "target_map": "/shattered_islands/world_4_52", "target_tile": [ 10, 15 - ] + ], + "arch": "invis_exit", + "line": 947, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_52_1", - "line": 956, "source_tile": [ 10, 16 ], - "arch": "invis_exit", - "form": "tiled", "target_map": "/shattered_islands/world_4_52", "target_tile": [ 10, 16 - ] + ], + "arch": "invis_exit", + "line": 956, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_52_1", - "line": 965, "source_tile": [ 10, 17 ], - "arch": "invis_exit", - "form": "tiled", "target_map": "/shattered_islands/world_4_52", "target_tile": [ 10, 17 - ] + ], + "arch": "invis_exit", + "line": 965, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_52_1", - "line": 974, "source_tile": [ 10, 18 ], - "arch": "invis_exit", - "form": "tiled", "target_map": "/shattered_islands/world_4_52", "target_tile": [ 10, 18 - ] + ], + "arch": "invis_exit", + "line": 974, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 634, "source_tile": [ 3, 18 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 1, 12 - ] + ], + "arch": "well_stone", + "line": 634, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_4_53_-2 (1,12)" }, { "source_map": "/shattered_islands/world_4_53", - "line": 1879, "source_tile": [ 10, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_down", + "line": 1879, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 1891, "source_tile": [ 10, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 1891, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 2065, "source_tile": [ 11, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 11, 3 - ] + ], + "arch": "stairs_down", + "line": 2065, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 2077, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 11, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2077, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 2289, "source_tile": [ 12, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 12, 3 - ] + ], + "arch": "stairs_down", + "line": 2289, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53", - "line": 2301, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 12, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2301, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 462, "source_tile": [ 4, 21 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 5, 21 - ] + ], + "arch": "hole", + "line": 462, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1202, "source_tile": [ 10, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 10, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1202, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1215, "source_tile": [ 10, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 10, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1215, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1350, "source_tile": [ 11, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1350, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1363, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1363, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1507, "source_tile": [ 12, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1507, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-1", - "line": 1520, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1520, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 144, "source_tile": [ 1, 12 ], - "arch": "well_bucket", - "form": "explicit", "target_map": "/shattered_islands/world_4_53", "target_tile": [ 3, 18 - ] + ], + "arch": "well_bucket", + "line": 144, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 558, "source_tile": [ 4, 21 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_4_53_-1", "target_tile": [ 4, 21 - ] + ], + "arch": "hole", + "line": 558, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 951, "source_tile": [ 7, 4 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 9, 3 - ] + ], + "arch": "hole", + "line": 951, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 1045, "source_tile": [ 7, 14 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 7, 20 - ] + ], + "arch": "hole", + "line": 1045, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 1090, "source_tile": [ 7, 20 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 7, 14 - ] + ], + "arch": "hole", + "line": 1090, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_53_-2", - "line": 1132, "source_tile": [ 8, 3 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_4_53_-2", "target_tile": [ 7, 5 - ] + ], + "arch": "hole", + "line": 1132, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_58", - "line": 2033, "source_tile": [ 13, 10 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 2033, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_58", - "line": 3402, "source_tile": [ 21, 12 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_4_58", "target_tile": [ 20, 13 + ], + "arch": "exit", + "line": 3402, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/world_4_59", - "line": 3269, "source_tile": [ 20, 9 ], "arch": "stairs_stone_down", - "form": "tiled-unresolved" + "line": 3269, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_67", - "line": 1682, "source_tile": [ 12, 4 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_67_-1", "target_tile": [ 12, 4 - ] + ], + "arch": "ladder_down", + "line": 1682, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_67", - "line": 1758, "source_tile": [ 12, 17 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_67_-1", "target_tile": [ 12, 17 - ] + ], + "arch": "ladder_down", + "line": 1758, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_67_-1", - "line": 1090, "source_tile": [ 7, 17 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_5_2_-1", "target_tile": [ 2, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 1090, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_67_-1", - "line": 1729, "source_tile": [ 12, 4 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_67", "target_tile": [ 12, 4 - ] + ], + "arch": "ladder_up", + "line": 1729, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_67_-1", - "line": 1810, "source_tile": [ 12, 17 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_67", "target_tile": [ 12, 17 - ] + ], + "arch": "ladder_up", + "line": 1810, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_73", - "line": 2257, "source_tile": [ 19, 13 ], - "arch": "exit", - "form": "explicit", "target_map": "/shattered_islands/world_1_73_-2", "target_tile": [ 14, 21 - ] + ], + "arch": "exit", + "line": 2257, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_4_83", - "line": 56, "source_tile": [ 0, 7 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_83_-1", "target_tile": [ 1, 7 - ] + ], + "arch": "stairs_stone_down", + "line": 56, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_83", - "line": 212, "source_tile": [ 1, 7 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_83_-1", "target_tile": [ 1, 7 - ] + ], + "arch": "stairs_down", + "line": 212, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_83", - "line": 1148, "source_tile": [ 6, 17 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/world_4_83_-2", "target_tile": [ 6, 17 - ] + ], + "arch": "well_stone", + "line": 1148, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_4_83_-2 (7,18)" }, { "source_map": "/shattered_islands/world_4_83", - "line": 2229, "source_tile": [ 13, 12 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/incuna/ellamin", "target_tile": [ 4, 13 - ] + ], + "arch": "stairs_wood_down", + "line": 2229, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_83", - "line": 2550, "source_tile": [ 15, 8 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/incuna/ellamin", "target_tile": [ 3, 1 - ] + ], + "arch": "invis_exit", + "line": 2550, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_4_83_-1", - "line": 37, "source_tile": [ 0, 7 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_83", "target_tile": [ 0, 7 - ] + ], + "arch": "stairs_stone_up2", + "line": 37, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_83_-1", - "line": 126, "source_tile": [ 1, 7 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_83", "target_tile": [ 0, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 126, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_83_-2", - "line": 365, "source_tile": [ 7, 18 ], - "arch": "well_bucket", - "form": "explicit", "target_map": "/shattered_islands/world_4_83", "target_tile": [ 6, 17 - ] + ], + "arch": "well_bucket", + "line": 365, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 292, "source_tile": [ 1, 15 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_1", "target_tile": [ 1, 15 - ] + ], + "arch": "ladder_up", + "line": 292, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84", - "line": 1229, "source_tile": [ 6, 14 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 7, 14 - ] + ], + "arch": "stairs_wood_down", + "line": 1229, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 1457, "source_tile": [ 7, 14 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 7, 14 - ] + ], + "arch": "stairs_down", + "line": 1457, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 1812, "source_tile": [ 9, 7 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 9, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 1812, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 1821, "source_tile": [ 9, 8 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 9, 8 - ] + ], + "arch": "stairs_down", + "line": 1821, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 1999, "source_tile": [ 10, 7 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 10, 8 - ] + ], + "arch": "stairs_stone_down", + "line": 1999, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 2008, "source_tile": [ 10, 8 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 10, 8 - ] + ], + "arch": "stairs_down", + "line": 2008, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 3382, "source_tile": [ 17, 18 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 10, 1 + ], + "arch": "exit", + "line": 3382, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] }, { "source_map": "/shattered_islands/world_4_84", - "line": 4329, "source_tile": [ 22, 10 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 23, 10 - ] + ], + "arch": "stairs_stone_down", + "line": 4329, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 4337, "source_tile": [ 22, 11 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 23, 11 - ] + ], + "arch": "stairs_stone_down", + "line": 4337, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 4479, "source_tile": [ 23, 10 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 23, 10 - ] + ], + "arch": "stairs_down", + "line": 4479, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84", - "line": 4487, "source_tile": [ 23, 11 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 23, 11 - ] + ], + "arch": "stairs_down", + "line": 4487, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 916, "source_tile": [ 6, 14 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 6, 14 - ] + ], + "arch": "stairs_wood_up2", + "line": 916, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 1065, "source_tile": [ 7, 14 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 6, 14 - ] + ], + "arch": "stairs_wood_up", + "line": 1065, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 1270, "source_tile": [ 9, 7 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 9, 7 - ] + ], + "arch": "stairs_stone_up2", + "line": 1270, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 1279, "source_tile": [ 9, 8 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 9, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 1279, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 1391, "source_tile": [ 10, 7 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 10, 7 - ] + ], + "arch": "stairs_stone_up2", + "line": 1391, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 1400, "source_tile": [ 10, 8 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 10, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 1400, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 2844, "source_tile": [ 20, 5 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 20, 5 - ] + ], + "arch": "shop_mat", + "line": 2844, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 3144, "source_tile": [ 22, 5 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_4_84_-1", "target_tile": [ 22, 5 - ] + ], + "arch": "shop_mat", + "line": 3144, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 3177, "source_tile": [ 22, 10 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 22, 10 - ] + ], + "arch": "stairs_stone_up2", + "line": 3177, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 3185, "source_tile": [ 22, 11 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 22, 11 - ] + ], + "arch": "stairs_stone_up2", + "line": 3185, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 3233, "source_tile": [ 23, 10 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 22, 10 - ] + ], + "arch": "stairs_stone_up", + "line": 3233, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-1", - "line": 3241, "source_tile": [ 23, 11 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 22, 11 - ] + ], + "arch": "stairs_stone_up", + "line": 3241, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_4_84_-2", - "line": 766, "source_tile": [ 16, 6 ], "arch": "exit_green", - "form": "unresolved" + "line": 766, + "form": "unresolved", + "vertical": "none" }, { "source_map": "/shattered_islands/world_4_84_1", - "line": 115, "source_tile": [ 1, 15 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_4_84", "target_tile": [ 1, 15 - ] + ], + "arch": "ladder_down", + "line": 115, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_85", - "line": 1130, "source_tile": [ 7, 13 ], - "arch": "stairs_wood_down", - "form": "same-map", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 7, 13 - ] + ], + "arch": "stairs_wood_down", + "line": 1130, + "form": "same-map", + "vertical": "down", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_4_85", - "line": 3052, "source_tile": [ 20, 13 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_4_85_-1", "target_tile": [ 20, 13 - ] + ], + "arch": "hole", + "line": 3052, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_4_85_-1", - "line": 578, "source_tile": [ 20, 13 ], - "arch": "rope_up", - "form": "tiled", "target_map": "/shattered_islands/world_4_85", "target_tile": [ 20, 13 - ] + ], + "arch": "rope_up", + "line": 578, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_42", - "line": 3015, "source_tile": [ 18, 14 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3015, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_5_42", - "line": 3054, "source_tile": [ 18, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3054, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_5_42", - "line": 3588, "source_tile": [ 21, 14 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3588, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_5_42", - "line": 3627, "source_tile": [ 21, 16 ], "arch": "ladder_up", - "form": "unresolved" + "line": 3627, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_5_43_1", - "line": 146, "source_tile": [ 19, 15 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_43_2", "target_tile": [ 19, 15 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 146, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_43_2", - "line": 228, "source_tile": [ 19, 15 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_43_1", "target_tile": [ 19, 15 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 228, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_44_1", - "line": 337, "source_tile": [ 19, 18 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_44_2", "target_tile": [ 19, 18 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 337, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_44_2", - "line": 354, "source_tile": [ 19, 18 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_44_1", "target_tile": [ 19, 18 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 354, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_45", - "line": 2351, "source_tile": [ 14, 9 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_45_1", "target_tile": [ 15, 9 - ] + ], + "arch": "stairs_wood_up", + "line": 2351, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_45", - "line": 2544, "source_tile": [ 15, 9 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_45_1", "target_tile": [ 15, 9 - ] + ], + "arch": "stairs_wood_up2", + "line": 2544, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_45_1", - "line": 158, "source_tile": [ 14, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_45", "target_tile": [ 14, 9 - ] + ], + "arch": "stairs_down", + "line": 158, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_45_1", - "line": 237, "source_tile": [ 15, 9 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_45", "target_tile": [ 14, 9 - ] + ], + "arch": "stairs_wood_down", + "line": 237, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_46", - "line": 2999, "source_tile": [ 17, 10 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_5_46", "target_tile": [ 17, 10 - ] + ], + "arch": "shop_mat", + "line": 2999, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_46", - "line": 3388, "source_tile": [ 19, 10 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_5_46", "target_tile": [ 19, 10 - ] + ], + "arch": "shop_mat", + "line": 3388, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_48", - "line": 1119, "source_tile": [ 6, 16 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_5_48_-1", "target_tile": [ 6, 18 - ] + ], + "arch": "stairs_stone_down", + "line": 1119, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_48", - "line": 1131, "source_tile": [ 6, 17 ], - "arch": "stairs_down", - "form": "explicit", "target_map": "/shattered_islands/world_5_48_-1", "target_tile": [ 6, 18 - ] + ], + "arch": "stairs_down", + "line": 1131, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_48", - "line": 3718, "source_tile": [ 22, 22 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/world_5_48_-1", "target_tile": [ 22, 22 - ] + ], + "arch": "stairs_wood_down", + "line": 3718, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_48_-1", - "line": 144, "source_tile": [ 6, 16 ], - "arch": "stairs_stone_up2", - "form": "explicit", "target_map": "/shattered_islands/world_5_48", "target_tile": [ 6, 15 - ] + ], + "arch": "stairs_stone_up2", + "line": 144, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_48_-1", - "line": 156, "source_tile": [ 6, 17 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_48", "target_tile": [ 6, 15 - ] + ], + "arch": "stairs_stone_up", + "line": 156, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_48_-1", - "line": 906, "source_tile": [ 22, 22 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_48", "target_tile": [ 22, 22 - ] + ], + "arch": "stairs_wood_up", + "line": 906, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_57", - "line": 1429, "source_tile": [ 7, 3 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", "target_tile": [ 1, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 1429, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_57", - "line": 1619, "source_tile": [ 8, 3 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", "target_tile": [ 2, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 1619, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_57", - "line": 1805, "source_tile": [ 9, 3 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_0101", "target_tile": [ 3, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 1805, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_57_-2", - "line": 708, "source_tile": [ 14, 14 ], - "arch": "well_bucket", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/old_outpost/old_outpost_a_0204", "target_tile": [ 8, 19 - ] + ], + "arch": "well_bucket", + "line": 708, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_58", - "line": 570, "source_tile": [ 3, 17 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_5_58", "target_tile": [ 3, 17 - ] + ], + "arch": "shop_mat", + "line": 570, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_58", - "line": 869, "source_tile": [ 5, 17 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_5_58", "target_tile": [ 5, 17 - ] + ], + "arch": "shop_mat", + "line": 869, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_65", - "line": 308, "source_tile": [ 2, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_0", "target_tile": [ 1, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 308, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_65", - "line": 1745, "source_tile": [ 11, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_65_1", "target_tile": [ 11, 2 - ] + ], + "arch": "ladder_up", + "line": 1745, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_65_1", - "line": 1019, "source_tile": [ 11, 2 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_65", "target_tile": [ 11, 2 - ] + ], + "arch": "ladder_down", + "line": 1019, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_65_1", - "line": 1224, "source_tile": [ 13, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_65_2", "target_tile": [ 13, 2 - ] + ], + "arch": "ladder_up", + "line": 1224, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_65_2", - "line": 869, "source_tile": [ 13, 2 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_65_1", "target_tile": [ 13, 2 - ] + ], + "arch": "ladder_down", + "line": 869, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_67", - "line": 1083, "source_tile": [ 6, 9 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_67_1", "target_tile": [ 6, 9 - ] + ], + "arch": "stairs_wood_up2", + "line": 1083, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_67", - "line": 1258, "source_tile": [ 7, 9 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_67_1", "target_tile": [ 6, 9 - ] + ], + "arch": "stairs_wood_up", + "line": 1258, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_67_1", - "line": 250, "source_tile": [ 6, 9 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_67", "target_tile": [ 7, 9 - ] + ], + "arch": "stairs_wood_down", + "line": 250, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_67_1", - "line": 309, "source_tile": [ 7, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_67", "target_tile": [ 7, 9 - ] + ], + "arch": "stairs_down", + "line": 309, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_68", - "line": 1378, "source_tile": [ 9, 5 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_68_-1", "target_tile": [ 9, 4 - ] + ], + "arch": "stairs_stone_down", + "line": 1378, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_68", - "line": 1550, "source_tile": [ 10, 5 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_68_-1", "target_tile": [ 10, 4 - ] + ], + "arch": "stairs_stone_down", + "line": 1550, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_68", - "line": 3377, "source_tile": [ 22, 9 ], "arch": "well_stone", - "form": "unresolved" + "line": 3377, + "form": "unresolved", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_68_-1", - "line": 87, "source_tile": [ 9, 4 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_68", "target_tile": [ 9, 5 - ] + ], + "arch": "stairs_stone_up", + "line": 87, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_68_-1", - "line": 120, "source_tile": [ 10, 4 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_68", "target_tile": [ 10, 5 - ] + ], + "arch": "stairs_stone_up", + "line": 120, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_71", - "line": 1157, "source_tile": [ 8, 15 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_6_72", "target_tile": [ 17, 11 - ] + ], + "arch": "exit_green", + "line": 1157, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 479, "source_tile": [ 2, 7 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 2, 8 - ] + ], + "arch": "stairs_wood_down", + "line": 479, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 488, "source_tile": [ 2, 8 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 2, 8 - ] + ], + "arch": "stairs_down", + "line": 488, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 524, "source_tile": [ 2, 13 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 2, 13 - ] + ], + "arch": "stairs_down", + "line": 524, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 532, "source_tile": [ 2, 14 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 2, 13 - ] + ], + "arch": "stairs_wood_down", + "line": 532, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 806, "source_tile": [ 3, 7 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 3, 8 - ] + ], + "arch": "stairs_wood_down", + "line": 806, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 815, "source_tile": [ 3, 8 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 3, 8 - ] + ], + "arch": "stairs_down", + "line": 815, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 851, "source_tile": [ 3, 13 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 3, 13 - ] + ], + "arch": "stairs_down", + "line": 851, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 859, "source_tile": [ 3, 14 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-3", "target_tile": [ 3, 13 - ] + ], + "arch": "stairs_wood_down", + "line": 859, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_84_-2", - "line": 2093, "source_tile": [ 9, 8 ], - "arch": "ladder_tall_up", - "form": "explicit", "target_map": "/shattered_islands/world_5_84", "target_tile": [ 9, 8 - ] + ], + "arch": "ladder_tall_up", + "line": 2093, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 113, "source_tile": [ 2, 7 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 2, 7 - ] + ], + "arch": "stairs_wood_up2", + "line": 113, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 148, "source_tile": [ 2, 8 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 2, 7 - ] + ], + "arch": "stairs_wood_up", + "line": 148, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 303, "source_tile": [ 2, 13 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 2, 14 - ] + ], + "arch": "stairs_wood_up", + "line": 303, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 338, "source_tile": [ 2, 14 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 2, 14 - ] + ], + "arch": "stairs_wood_up2", + "line": 338, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 389, "source_tile": [ 3, 7 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 3, 7 - ] + ], + "arch": "stairs_wood_up2", + "line": 389, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 424, "source_tile": [ 3, 8 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 3, 7 - ] + ], + "arch": "stairs_wood_up", + "line": 424, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 579, "source_tile": [ 3, 13 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 3, 14 - ] + ], + "arch": "stairs_wood_up", + "line": 579, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_84_-3", - "line": 614, "source_tile": [ 3, 14 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_5_84_-2", "target_tile": [ 3, 14 - ] + ], + "arch": "stairs_wood_up2", + "line": 614, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_5_85_-1", - "line": 1523, "source_tile": [ 18, 20 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_5_85_-2", "target_tile": [ 18, 20 - ] + ], + "arch": "hole", + "line": 1523, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_5_85_-2", - "line": 965, "source_tile": [ 18, 20 ], - "arch": "rope_up", - "form": "tiled", "target_map": "/shattered_islands/world_5_85_-1", "target_tile": [ 18, 20 - ] + ], + "arch": "rope_up", + "line": 965, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_42", - "line": 439, "source_tile": [ 2, 17 ], "arch": "ladder_down", - "form": "tiled-unresolved" + "line": 439, + "form": "tiled-unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_43_-1", - "line": 1168, "source_tile": [ 17, 22 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_6_43_-2", "target_tile": [ 17, 22 - ] + ], + "arch": "stairs_stone_down", + "line": 1168, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_43_-2", - "line": 396, "source_tile": [ 17, 22 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_43_-1", "target_tile": [ 17, 22 - ] + ], + "arch": "stairs_stone_up", + "line": 396, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44", - "line": 1205, "source_tile": [ 7, 15 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_44_1", "target_tile": [ 7, 15 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 1205, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44", - "line": 1351, "source_tile": [ 8, 12 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_44_1", "target_tile": [ 8, 12 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 1351, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44", - "line": 2452, "source_tile": [ 14, 12 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_44_1", "target_tile": [ 14, 12 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 2452, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44", - "line": 2649, "source_tile": [ 15, 15 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_44_1", "target_tile": [ 15, 15 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 2649, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44", - "line": 3183, "source_tile": [ 18, 13 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_6_44_-1", "target_tile": [ 18, 13 - ] + ], + "arch": "stairs_stone_down", + "line": 3183, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44", - "line": 3602, "source_tile": [ 21, 2 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_6_44_-1", "target_tile": [ 21, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 3602, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44_-1", - "line": 2831, "source_tile": [ 18, 13 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 18, 13 - ] + ], + "arch": "stairs_stone_up", + "line": 2831, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44_-1", - "line": 3220, "source_tile": [ 21, 2 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 21, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 3220, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44_1", - "line": 793, "source_tile": [ 7, 15 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 7, 15 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 793, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44_1", - "line": 859, "source_tile": [ 8, 12 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 8, 12 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 859, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44_1", - "line": 1174, "source_tile": [ 11, 15 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_44_2", "target_tile": [ 11, 15 - ] + ], + "arch": "stairs_stone_up", + "line": 1174, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_44_1", - "line": 1610, "source_tile": [ 14, 12 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 14, 12 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 1610, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44_1", - "line": 1750, "source_tile": [ 15, 15 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_44", "target_tile": [ 15, 15 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 1750, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_44_2", - "line": 1024, "source_tile": [ 11, 15 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_6_44_1", "target_tile": [ 11, 15 - ] + ], + "arch": "stairs_stone_down", + "line": 1024, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_45", - "line": 446, "source_tile": [ 2, 9 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_6_45_1", "target_tile": [ 2, 9 - ] + ], + "arch": "stairs_stone_up2", + "line": 446, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_45", - "line": 604, "source_tile": [ 3, 9 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_45_1", "target_tile": [ 2, 9 - ] + ], + "arch": "stairs_stone_up", + "line": 604, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_45_1", - "line": 171, "source_tile": [ 2, 9 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_45", "target_tile": [ 3, 9 - ] + ], + "arch": "stairs_stone_down", + "line": 171, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_45_1", - "line": 232, "source_tile": [ 3, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_45", "target_tile": [ 3, 9 - ] + ], + "arch": "stairs_down", + "line": 232, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_46", - "line": 240, "source_tile": [ 1, 1 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_6_46_-1", "target_tile": [ 1, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 240, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_46", - "line": 542, "source_tile": [ 2, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 2, 11 - ] + ], + "arch": "shop_mat", + "line": 542, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46", - "line": 941, "source_tile": [ 4, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 4, 11 - ] + ], + "arch": "shop_mat", + "line": 941, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46", - "line": 3569, "source_tile": [ 19, 1 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 19, 1 - ] + ], + "arch": "shop_mat", + "line": 3569, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46", - "line": 3631, "source_tile": [ 19, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 19, 11 - ] + ], + "arch": "shop_mat", + "line": 3631, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46", - "line": 3921, "source_tile": [ 21, 1 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 21, 1 - ] + ], + "arch": "shop_mat", + "line": 3921, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46", - "line": 4017, "source_tile": [ 21, 11 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 21, 11 - ] + ], + "arch": "shop_mat", + "line": 4017, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_46_-1", - "line": 49, "source_tile": [ 1, 1 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_46", "target_tile": [ 1, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 49, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_48", - "line": 452, "source_tile": [ 3, 1 ], - "arch": "ladder_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_48_-1", "target_tile": [ 3, 1 - ] + ], + "arch": "ladder_down", + "line": 452, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_48", - "line": 704, "source_tile": [ 4, 12 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_6_48_1", "target_tile": [ 4, 12 - ] + ], + "arch": "stairs_wood_up2", + "line": 704, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_48", - "line": 861, "source_tile": [ 5, 12 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_48_1", "target_tile": [ 4, 12 - ] + ], + "arch": "stairs_wood_up", + "line": 861, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_48_-1", - "line": 370, "source_tile": [ 3, 1 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_48", "target_tile": [ 3, 1 - ] + ], + "arch": "ladder_up", + "line": 370, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_48_1", - "line": 492, "source_tile": [ 4, 12 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_48", "target_tile": [ 5, 12 - ] + ], + "arch": "stairs_wood_down", + "line": 492, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_48_1", - "line": 590, "source_tile": [ 5, 12 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_48", "target_tile": [ 5, 12 - ] + ], + "arch": "stairs_down", + "line": 590, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51", - "line": 2019, "source_tile": [ 14, 17 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_51_-1", "target_tile": [ 14, 17 - ] + ], + "arch": "stairs_down", + "line": 2019, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51", - "line": 2385, "source_tile": [ 15, 17 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_6_51_-1", "target_tile": [ 14, 17 - ] + ], + "arch": "stairs_stone_down", + "line": 2385, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51", - "line": 2481, "source_tile": [ 16, 6 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_2", "target_tile": [ 6, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 2481, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51", - "line": 2948, "source_tile": [ 18, 18 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/world_6_51_-2", "target_tile": [ 18, 18 - ] + ], + "arch": "well_stone", + "line": 2948, + "form": "explicit", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_6_51_-2 (18,18)" }, { "source_map": "/shattered_islands/world_6_51_-1", - "line": 332, "source_tile": [ 14, 17 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_51", "target_tile": [ 15, 17 - ] + ], + "arch": "stairs_stone_up", + "line": 332, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_51_-1", - "line": 413, "source_tile": [ 15, 17 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_6_51", "target_tile": [ 15, 17 - ] + ], + "arch": "stairs_stone_up2", + "line": 413, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_51_-1", - "line": 748, "source_tile": [ 18, 21 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_51_-1", "target_tile": [ 18, 21 - ] + ], + "arch": "shop_mat", + "line": 748, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_51_-1", - "line": 997, "source_tile": [ 20, 21 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_6_51_-1", "target_tile": [ 20, 21 - ] + ], + "arch": "shop_mat", + "line": 997, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_51_-1", - "line": 1127, "source_tile": [ 22, 11 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_6_51_-2", "target_tile": [ 22, 11 - ] + ], + "arch": "hole", + "line": 1127, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51_-2", - "line": 1450, "source_tile": [ 18, 18 ], - "arch": "well_bucket", - "form": "explicit", "target_map": "/shattered_islands/world_6_51", "target_tile": [ 18, 18 - ] + ], + "arch": "well_bucket", + "line": 1450, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_51_-2", - "line": 1508, "source_tile": [ 19, 16 ], - "arch": "invis_exit", - "form": "same-map", "target_map": "/shattered_islands/world_6_51_-2", "target_tile": [ 18, 17 - ] + ], + "arch": "invis_exit", + "line": 1508, + "form": "same-map", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_54_-1", - "line": 96, "source_tile": [ 5, 5 ], - "arch": "hole2", - "form": "tiled", "target_map": "/shattered_islands/world_6_54_-2", "target_tile": [ 5, 5 - ] + ], + "arch": "hole2", + "line": 96, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_54_-2", - "line": 352, "source_tile": [ 5, 5 ], - "arch": "rope_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_54_-1", "target_tile": [ 5, 5 - ] + ], + "arch": "rope_up", + "line": 352, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_57", - "line": 1611, "source_tile": [ 9, 14 ], - "arch": "well_stone", - "form": "same-map", "target_map": "/shattered_islands/world_6_57", "target_tile": [ 9, 14 - ] + ], + "arch": "well_stone", + "line": 1611, + "form": "same-map", + "vertical": "up", + "vertical_inferred_from": "reciprocal well_bucket at /shattered_islands/world_6_57_-1 (9,14)" }, { "source_map": "/shattered_islands/world_6_57_-1", - "line": 1598, "source_tile": [ 9, 14 ], - "arch": "well_bucket", - "form": "tiled", "target_map": "/shattered_islands/world_6_57", "target_tile": [ 9, 14 - ] + ], + "arch": "well_bucket", + "line": 1598, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_66", - "line": 1406, "source_tile": [ 18, 15 ], "arch": "hole", - "form": "unresolved" + "line": 1406, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_66_-1", - "line": 1216, "source_tile": [ 18, 15 ], - "arch": "rope_up", - "form": "explicit", "target_map": "/shattered_islands/world_6_66", "target_tile": [ 19, 15 - ] + ], + "arch": "rope_up", + "line": 1216, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_67", - "line": 653, "source_tile": [ 4, 14 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_6_67", "target_tile": [ 4, 14 - ] + ], + "arch": "hole", + "line": 653, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_6_67_-1", - "line": 253, "source_tile": [ 4, 14 ], - "arch": "rope_up", - "form": "tiled", "target_map": "/shattered_islands/world_6_67", "target_tile": [ 4, 14 - ] + ], + "arch": "rope_up", + "line": 253, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_6_67_-1", - "line": 665, "source_tile": [ 8, 9 ], - "arch": "portal_yellow", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/underground_city/underground_city_0_1_-2", "target_tile": [ 22, 22 - ] + ], + "arch": "portal_yellow", + "line": 665, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_72", - "line": 998, "source_tile": [ 6, 11 ], - "arch": "portal_green", - "form": "explicit", "target_map": "/shattered_islands/world_8_73", "target_tile": [ 11, 10 - ] + ], + "arch": "portal_green", + "line": 998, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_6_78", - "line": 2073, "source_tile": [ 19, 21 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 6, 10 - ] + ], + "arch": "stairs_wood_down", + "line": 2073, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_43_1", - "line": 96, "source_tile": [ 1, 15 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_43_2", "target_tile": [ 1, 15 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 96, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_43_2", - "line": 93, "source_tile": [ 1, 15 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_43_1", "target_tile": [ 1, 15 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 93, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_44", - "line": 2381, "source_tile": [ 13, 13 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_7_44_1", "target_tile": [ 13, 12 - ] + ], + "arch": "stairs_stone_up", + "line": 2381, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_44_1", - "line": 227, "source_tile": [ 1, 18 ], - "arch": "stairs_spiral_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_44_2", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_spiral_stone_up", + "line": 227, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_44_1", - "line": 677, "source_tile": [ 13, 12 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_7_44", "target_tile": [ 13, 13 - ] + ], + "arch": "stairs_stone_down", + "line": 677, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_44_2", - "line": 183, "source_tile": [ 1, 18 ], - "arch": "stairs_spiral_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_44_1", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_spiral_stone_down", + "line": 183, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_45", - "line": 821, "source_tile": [ 4, 9 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_45_1", "target_tile": [ 4, 9 - ] + ], + "arch": "stairs_wood_up2", + "line": 821, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_45", - "line": 1006, "source_tile": [ 5, 9 ], - "arch": "stairs_wood_up", - "form": "explicit", "target_map": "/shattered_islands/world_7_45_1", "target_tile": [ 5, 9 - ] + ], + "arch": "stairs_wood_up", + "line": 1006, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_45_-1", - "line": 1160, "source_tile": [ 20, 16 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_7_45_-1", "target_tile": [ 23, 13 - ] + ], + "arch": "hole", + "line": 1160, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_45_-1", - "line": 1253, "source_tile": [ 21, 16 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_7_45_-1", "target_tile": [ 21, 16 - ] + ], + "arch": "shop_mat", + "line": 1253, + "form": "shop-mat", + "vertical": "down", + "vertical_inferred_from": "co-located hole" }, { "source_map": "/shattered_islands/world_7_45_-1", - "line": 1425, "source_tile": [ 23, 13 ], - "arch": "hole", - "form": "same-map", "target_map": "/shattered_islands/world_7_45_-1", "target_tile": [ 20, 16 - ] + ], + "arch": "hole", + "line": 1425, + "form": "same-map", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_45_-1", - "line": 1457, "source_tile": [ 23, 16 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_7_45_-1", "target_tile": [ 23, 16 - ] + ], + "arch": "shop_mat", + "line": 1457, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_7_45_1", - "line": 363, "source_tile": [ 4, 9 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_45", "target_tile": [ 5, 9 - ] + ], + "arch": "stairs_wood_down", + "line": 363, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_45_1", - "line": 433, "source_tile": [ 5, 9 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_45", "target_tile": [ 5, 9 - ] + ], + "arch": "stairs_down", + "line": 433, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_46", - "line": 1407, "source_tile": [ 6, 13 ], - "arch": "sewer_hole1", - "form": "tiled", "target_map": "/shattered_islands/world_7_46_-1", "target_tile": [ 6, 13 - ] + ], + "arch": "sewer_hole1", + "line": 1407, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_46", - "line": 2764, "source_tile": [ 14, 5 ], "arch": "stairs_wood_up", - "form": "unresolved" + "line": 2764, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_7_46_-1", - "line": 385, "source_tile": [ 6, 13 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_46", "target_tile": [ 6, 13 - ] + ], + "arch": "ladder_up", + "line": 385, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_47", - "line": 262, "source_tile": [ 1, 19 ], - "arch": "sewer_hole2", - "form": "tiled", "target_map": "/shattered_islands/world_7_47_-1", "target_tile": [ 1, 19 - ] + ], + "arch": "sewer_hole2", + "line": 262, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_47", - "line": 1483, "source_tile": [ 9, 9 ], - "arch": "stairs_stone_down", - "form": "explicit", "target_map": "/shattered_islands/world_7_47_-1", "target_tile": [ 9, 9 - ] + ], + "arch": "stairs_stone_down", + "line": 1483, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_47_-1", - "line": 279, "source_tile": [ 1, 19 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_47", "target_tile": [ 1, 19 - ] + ], + "arch": "ladder_up", + "line": 279, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_47_-1", - "line": 1202, "source_tile": [ 9, 3 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_47", "target_tile": [ 9, 3 - ] + ], + "arch": "ladder_up", + "line": 1202, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_47_-1", - "line": 1240, "source_tile": [ 9, 5 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_47", "target_tile": [ 9, 5 - ] + ], + "arch": "ladder_up", + "line": 1240, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_47_-1", - "line": 1272, "source_tile": [ 9, 9 ], - "arch": "stairs_stone_up", - "form": "explicit", "target_map": "/shattered_islands/world_7_47", "target_tile": [ 9, 9 - ] + ], + "arch": "stairs_stone_up", + "line": 1272, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_48", - "line": 497, "source_tile": [ 3, 10 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_7_48_-1", "target_tile": [ 3, 10 - ] + ], + "arch": "hole", + "line": 497, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_48_-1", - "line": 390, "source_tile": [ 3, 10 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_48", "target_tile": [ 3, 10 - ] + ], + "arch": "ladder_up", + "line": 390, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_60", - "line": 2260, "source_tile": [ 17, 8 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_7_60_-1", "target_tile": [ 17, 8 - ] + ], + "arch": "hole", + "line": 2260, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64", - "line": 2767, "source_tile": [ 18, 7 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_1", "target_tile": [ 18, 7 - ] + ], + "arch": "stairs_wood_up2", + "line": 2767, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64", - "line": 2948, "source_tile": [ 19, 7 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_1", "target_tile": [ 18, 7 - ] + ], + "arch": "stairs_wood_up", + "line": 2948, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64", - "line": 3373, "source_tile": [ 21, 13 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 21, 13 - ] + ], + "arch": "stairs_down", + "line": 3373, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64", - "line": 3389, "source_tile": [ 21, 14 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 21, 13 - ] + ], + "arch": "stairs_stone_down", + "line": 3389, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64", - "line": 3564, "source_tile": [ 22, 13 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 22, 13 - ] + ], + "arch": "stairs_down", + "line": 3564, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64", - "line": 3573, "source_tile": [ 22, 14 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 22, 13 - ] + ], + "arch": "stairs_stone_down", + "line": 3573, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1705, "source_tile": [ 20, 7 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-2", "target_tile": [ 21, 7 - ] + ], + "arch": "stairs_stone_down", + "line": 1705, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1790, "source_tile": [ 21, 7 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-2", "target_tile": [ 21, 7 - ] + ], + "arch": "stairs_down", + "line": 1790, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1831, "source_tile": [ 21, 13 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 21, 14 - ] + ], + "arch": "stairs_stone_up", + "line": 1831, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1840, "source_tile": [ 21, 14 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 21, 14 - ] + ], + "arch": "stairs_stone_up2", + "line": 1840, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1905, "source_tile": [ 22, 13 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 22, 14 - ] + ], + "arch": "stairs_stone_up", + "line": 1905, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_-1", - "line": 1914, "source_tile": [ 22, 14 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 22, 14 - ] + ], + "arch": "stairs_stone_up2", + "line": 1914, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_-2", - "line": 1182, "source_tile": [ 20, 7 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 20, 7 - ] + ], + "arch": "stairs_stone_up2", + "line": 1182, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_-2", - "line": 1258, "source_tile": [ 21, 7 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_-1", "target_tile": [ 20, 7 - ] + ], + "arch": "stairs_stone_up", + "line": 1258, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_1", - "line": 727, "source_tile": [ 14, 7 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_2", "target_tile": [ 14, 7 - ] + ], + "arch": "stairs_wood_up2", + "line": 727, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_1", - "line": 804, "source_tile": [ 15, 7 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_2", "target_tile": [ 14, 7 - ] + ], + "arch": "stairs_wood_up", + "line": 804, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_64_1", - "line": 1037, "source_tile": [ 18, 7 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 19, 7 - ] + ], + "arch": "stairs_wood_down", + "line": 1037, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_1", - "line": 1133, "source_tile": [ 19, 7 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64", "target_tile": [ 19, 7 - ] + ], + "arch": "stairs_down", + "line": 1133, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_2", - "line": 888, "source_tile": [ 14, 7 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_1", "target_tile": [ 15, 7 - ] + ], + "arch": "stairs_wood_down", + "line": 888, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_64_2", - "line": 985, "source_tile": [ 15, 7 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_7_64_1", "target_tile": [ 15, 7 - ] + ], + "arch": "stairs_down", + "line": 985, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_65", - "line": 918, "source_tile": [ 8, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_7_65_-1", "target_tile": [ 8, 12 - ] + ], + "arch": "invis_exit", + "line": 918, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_7_65", - "line": 1083, "source_tile": [ 9, 12 ], - "arch": "invis_exit", - "form": "explicit", "target_map": "/shattered_islands/world_7_65_-1", "target_tile": [ 9, 12 - ] + ], + "arch": "invis_exit", + "line": 1083, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_7_66_-1", - "line": 938, "source_tile": [ 13, 15 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_1_-1", "target_tile": [ 2, 17 - ] + ], + "arch": "hole", + "line": 938, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_66_-1", - "line": 1146, "source_tile": [ 16, 15 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_1_0_-2", "target_tile": [ 2, 2 - ] + ], + "arch": "hole", + "line": 1146, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_66_-1", - "line": 1345, "source_tile": [ 19, 15 ], - "arch": "hole", - "form": "explicit", "target_map": "/shattered_islands/strakewood_island/zechna_temple/zechna_temple_0_0_-3", "target_tile": [ 4, 23 - ] + ], + "arch": "hole", + "line": 1345, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_68", - "line": 1752, "source_tile": [ 12, 20 ], - "arch": "hole2", - "form": "tiled", "target_map": "/shattered_islands/world_7_68_-1", "target_tile": [ 12, 20 - ] + ], + "arch": "hole2", + "line": 1752, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_7_68_-1", - "line": 127, "source_tile": [ 12, 20 ], - "arch": "rope_up", - "form": "tiled", "target_map": "/shattered_islands/world_7_68", "target_tile": [ 12, 20 - ] + ], + "arch": "rope_up", + "line": 127, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_71", - "line": 2430, "source_tile": [ 19, 22 ], - "arch": "exit_green", - "form": "explicit", "target_map": "/shattered_islands/world_6_72", "target_tile": [ 17, 11 - ] + ], + "arch": "exit_green", + "line": 2430, + "form": "explicit", + "vertical": "none" }, { "source_map": "/shattered_islands/world_7_77", - "line": 2287, "source_tile": [ 18, 21 ], - "arch": "ladder_up", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/treehome_1", "target_tile": [ 4, 4 - ] + ], + "arch": "ladder_up", + "line": 2287, + "form": "explicit", + "vertical": "up" }, { "source_map": "/shattered_islands/world_7_78", - "line": 1354, "source_tile": [ 9, 7 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_7_78", "target_tile": [ 9, 7 - ] + ], + "arch": "shop_mat", + "line": 1354, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_7_78", - "line": 1646, "source_tile": [ 11, 7 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_7_78", "target_tile": [ 11, 7 - ] + ], + "arch": "shop_mat", + "line": 1646, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_8_45_-1", - "line": 361, "source_tile": [ 3, 12 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_45_-2", "target_tile": [ 3, 12 - ] + ], + "arch": "stairs_down", + "line": 361, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_45_-1", - "line": 370, "source_tile": [ 3, 13 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_45_-2", "target_tile": [ 3, 12 - ] + ], + "arch": "stairs_stone_down", + "line": 370, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_45_-1", - "line": 959, "source_tile": [ 8, 23 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_45", "target_tile": [ 8, 23 - ] + ], + "arch": "ladder_up", + "line": 959, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_45_-1", - "line": 1158, "source_tile": [ 10, 20 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_45", "target_tile": [ 10, 20 - ] + ], + "arch": "ladder_up", + "line": 1158, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_45_-1", - "line": 1221, "source_tile": [ 10, 23 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_45", "target_tile": [ 10, 23 - ] + ], + "arch": "ladder_up", + "line": 1221, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_45_-2", - "line": 137, "source_tile": [ 3, 12 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_45_-1", "target_tile": [ 3, 13 - ] + ], + "arch": "stairs_stone_up", + "line": 137, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_45_-2", - "line": 146, "source_tile": [ 3, 13 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_8_45_-1", "target_tile": [ 3, 13 - ] + ], + "arch": "stairs_stone_up2", + "line": 146, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_46", - "line": 212, "source_tile": [ 1, 5 ], "arch": "stairs_stone_up", - "form": "unresolved" + "line": 212, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_8_46", - "line": 1649, "source_tile": [ 9, 5 ], "arch": "stairs_stone_up", - "form": "unresolved" + "line": 1649, + "form": "unresolved", + "vertical": "up", + "explicit_last_heal_0": true }, { "source_map": "/shattered_islands/world_8_46_-1", - "line": 240, "source_tile": [ 6, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_46", "target_tile": [ 6, 2 - ] + ], + "arch": "ladder_up", + "line": 240, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_46_-1", - "line": 334, "source_tile": [ 8, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_46", "target_tile": [ 8, 2 - ] + ], + "arch": "ladder_up", + "line": 334, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_46_-1", - "line": 428, "source_tile": [ 10, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_46", "target_tile": [ 10, 2 - ] + ], + "arch": "ladder_up", + "line": 428, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_58", - "line": 1400, "source_tile": [ 7, 11 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_58_-1", "target_tile": [ 7, 12 - ] + ], + "arch": "stairs_stone_down", + "line": 1400, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_58", - "line": 1409, "source_tile": [ 7, 12 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_58_-1", "target_tile": [ 7, 12 - ] + ], + "arch": "stairs_down", + "line": 1409, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_58", - "line": 1602, "source_tile": [ 8, 11 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_58_-1", "target_tile": [ 8, 12 - ] + ], + "arch": "stairs_stone_down", + "line": 1602, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_58", - "line": 1611, "source_tile": [ 8, 12 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_8_58_-1", "target_tile": [ 8, 12 - ] + ], + "arch": "stairs_down", + "line": 1611, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_58_-1", - "line": 294, "source_tile": [ 7, 11 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_8_58", "target_tile": [ 7, 11 - ] + ], + "arch": "stairs_stone_up2", + "line": 294, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_58_-1", - "line": 307, "source_tile": [ 7, 12 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_58", "target_tile": [ 7, 11 - ] + ], + "arch": "stairs_stone_up", + "line": 307, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_58_-1", - "line": 344, "source_tile": [ 8, 11 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_8_58", "target_tile": [ 8, 11 - ] + ], + "arch": "stairs_stone_up2", + "line": 344, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_58_-1", - "line": 357, "source_tile": [ 8, 12 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_58", "target_tile": [ 8, 11 - ] + ], + "arch": "stairs_stone_up", + "line": 357, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_63", - "line": 3083, "source_tile": [ 21, 3 ], - "arch": "hole", - "form": "tiled", "target_map": "/shattered_islands/world_8_63_-1", "target_tile": [ 21, 3 - ] + ], + "arch": "hole", + "line": 3083, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_63_-1", - "line": 68, "source_tile": [ 2, 4 ], "arch": "hole2", - "form": "unresolved" + "line": 68, + "form": "unresolved", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_63_-1", - "line": 1966, "source_tile": [ 21, 3 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_63", "target_tile": [ 21, 3 - ] + ], + "arch": "ladder_up", + "line": 1966, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_68", - "line": 2344, "source_tile": [ 17, 6 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_8_68_-1", "target_tile": [ 17, 6 - ] + ], + "arch": "ladder_down2", + "line": 2344, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_68", - "line": 2493, "source_tile": [ 17, 17 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_8_68_-1", "target_tile": [ 17, 17 - ] + ], + "arch": "ladder_down2", + "line": 2493, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_8_68_-1", - "line": 1363, "source_tile": [ 17, 6 ], - "arch": "ladder_tall_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_68", "target_tile": [ 17, 6 - ] + ], + "arch": "ladder_tall_up", + "line": 1363, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_68_-1", - "line": 1446, "source_tile": [ 17, 17 ], - "arch": "ladder_tall_up", - "form": "tiled", "target_map": "/shattered_islands/world_8_68", "target_tile": [ 17, 17 - ] + ], + "arch": "ladder_tall_up", + "line": 1446, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_8_78", - "line": 989, "source_tile": [ 6, 23 ], - "arch": "well_stone", - "form": "explicit", "target_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", "target_tile": [ 1, 1 - ] + ], + "arch": "well_stone", + "line": 989, + "form": "explicit", + "vertical": "down", + "vertical_inferred_from": "reciprocal ladder_up at /shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance (1,1)" }, { "source_map": "/shattered_islands/world_8_79", - "line": 2081, "source_tile": [ 13, 7 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_8_79", "target_tile": [ 13, 7 - ] + ], + "arch": "shop_mat", + "line": 2081, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_8_79", - "line": 2097, "source_tile": [ 13, 9 ], - "arch": "shop_mat", - "form": "shop-mat", "target_map": "/shattered_islands/world_8_79", "target_tile": [ 13, 9 - ] + ], + "arch": "shop_mat", + "line": 2097, + "form": "shop-mat", + "vertical": "none" }, { "source_map": "/shattered_islands/world_9_41", - "line": 1499, "source_tile": [ 12, 3 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 12, 18 - ] + ], + "arch": "stairs_wood_down", + "line": 1499, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_42", - "line": 1680, "source_tile": [ 12, 2 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 12, 2 - ] + ], + "arch": "stairs_wood_down", + "line": 1680, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_42", - "line": 3221, "source_tile": [ 21, 19 ], - "arch": "stairs_wood_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_42_1", "target_tile": [ 21, 19 - ] + ], + "arch": "stairs_wood_up2", + "line": 3221, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_42", - "line": 3231, "source_tile": [ 21, 20 ], - "arch": "stairs_wood_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_42_1", "target_tile": [ 21, 19 - ] + ], + "arch": "stairs_wood_up", + "line": 3231, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_42_1", - "line": 112, "source_tile": [ 21, 19 ], - "arch": "stairs_wood_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_42", "target_tile": [ 21, 20 - ] + ], + "arch": "stairs_wood_down", + "line": 112, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_42_1", - "line": 121, "source_tile": [ 21, 20 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_42", "target_tile": [ 21, 20 - ] + ], + "arch": "stairs_down", + "line": 121, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_42_2", - "line": 234, "source_tile": [ 23, 17 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_42_3", "target_tile": [ 23, 17 - ] + ], + "arch": "ladder_up", + "line": 234, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_42_3", - "line": 207, "source_tile": [ 23, 17 ], - "arch": "ladder_down3", - "form": "tiled", "target_map": "/shattered_islands/world_9_42_2", "target_tile": [ 23, 17 - ] + ], + "arch": "ladder_down3", + "line": 207, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44", - "line": 1788, "source_tile": [ 9, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 1788, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44", - "line": 1797, "source_tile": [ 9, 2 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 2 - ] + ], + "arch": "stairs_stone_up2", + "line": 1797, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44", - "line": 1806, "source_tile": [ 9, 3 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 3 - ] + ], + "arch": "stairs_stone_up2", + "line": 1806, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44", - "line": 2007, "source_tile": [ 10, 1 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 2007, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44", - "line": 2016, "source_tile": [ 10, 2 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 2016, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44", - "line": 2025, "source_tile": [ 10, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 9, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 2025, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1355, "source_tile": [ 9, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 1355, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1363, "source_tile": [ 9, 2 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 1363, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1371, "source_tile": [ 9, 3 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 1371, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1477, "source_tile": [ 10, 1 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 1 - ] + ], + "arch": "stairs_down", + "line": 1477, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1491, "source_tile": [ 10, 2 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 2 - ] + ], + "arch": "stairs_down", + "line": 1491, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1505, "source_tile": [ 10, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44", "target_tile": [ 10, 3 - ] + ], + "arch": "stairs_down", + "line": 1505, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1660, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1660, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1669, "source_tile": [ 11, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1669, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1816, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1816, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1825, "source_tile": [ 12, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1825, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1948, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1948, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_1", - "line": 1957, "source_tile": [ 13, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1957, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 710, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up2", + "line": 710, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 718, "source_tile": [ 5, 2 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 2 - ] + ], + "arch": "stairs_stone_up2", + "line": 718, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 726, "source_tile": [ 5, 3 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 3 - ] + ], + "arch": "stairs_stone_up2", + "line": 726, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 838, "source_tile": [ 6, 1 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 1 - ] + ], + "arch": "stairs_stone_up", + "line": 838, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 846, "source_tile": [ 6, 2 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 2 - ] + ], + "arch": "stairs_stone_up", + "line": 846, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 854, "source_tile": [ 6, 3 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 5, 3 - ] + ], + "arch": "stairs_stone_up", + "line": 854, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 1696, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 11, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 1696, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 1705, "source_tile": [ 11, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 11, 5 - ] + ], + "arch": "stairs_down", + "line": 1705, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 1869, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 12, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 1869, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 1878, "source_tile": [ 12, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 12, 5 - ] + ], + "arch": "stairs_down", + "line": 1878, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 2020, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 13, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 2020, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_2", - "line": 2029, "source_tile": [ 13, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_1", "target_tile": [ 13, 5 - ] + ], + "arch": "stairs_down", + "line": 2029, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 790, "source_tile": [ 5, 1 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 1 - ] + ], + "arch": "stairs_stone_down", + "line": 790, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 798, "source_tile": [ 5, 2 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 2 - ] + ], + "arch": "stairs_stone_down", + "line": 798, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 806, "source_tile": [ 5, 3 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 3 - ] + ], + "arch": "stairs_stone_down", + "line": 806, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 915, "source_tile": [ 6, 1 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 1 - ] + ], + "arch": "stairs_down", + "line": 915, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 930, "source_tile": [ 6, 2 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 2 - ] + ], + "arch": "stairs_down", + "line": 930, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 945, "source_tile": [ 6, 3 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_2", "target_tile": [ 6, 3 - ] + ], + "arch": "stairs_down", + "line": 945, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 1776, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1776, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 1785, "source_tile": [ 11, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 11, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1785, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 1933, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 1933, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 1942, "source_tile": [ 12, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 12, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 1942, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 2073, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_up2", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up2", + "line": 2073, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_3", - "line": 2082, "source_tile": [ 13, 5 ], - "arch": "stairs_stone_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_4", "target_tile": [ 13, 4 - ] + ], + "arch": "stairs_stone_up", + "line": 2082, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 1768, "source_tile": [ 11, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 11, 5 - ] + ], + "arch": "stairs_stone_down", + "line": 1768, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 1777, "source_tile": [ 11, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 11, 5 - ] + ], + "arch": "stairs_down", + "line": 1777, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 1931, "source_tile": [ 12, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 12, 5 - ] + ], + "arch": "stairs_stone_down", + "line": 1931, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 1940, "source_tile": [ 12, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 12, 5 - ] + ], + "arch": "stairs_down", + "line": 1940, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 2078, "source_tile": [ 13, 4 ], - "arch": "stairs_stone_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 13, 5 - ] + ], + "arch": "stairs_stone_down", + "line": 2078, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_44_4", - "line": 2087, "source_tile": [ 13, 5 ], - "arch": "stairs_down", - "form": "tiled", "target_map": "/shattered_islands/world_9_44_3", "target_tile": [ 13, 5 - ] + ], + "arch": "stairs_down", + "line": 2087, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_56", - "line": 2546, "source_tile": [ 17, 17 ], - "arch": "stairs_wood_down", - "form": "explicit", "target_map": "/shattered_islands/ships/ship_1", "target_tile": [ 1, 18 - ] + ], + "arch": "stairs_wood_down", + "line": 2546, + "form": "explicit", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_65", - "line": 925, "source_tile": [ 6, 10 ], - "arch": "well_stone", - "form": "tiled", "target_map": "/shattered_islands/world_9_65_-1", "target_tile": [ 6, 10 - ] + ], + "arch": "well_stone", + "line": 925, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_65_-1", - "line": 860, "source_tile": [ 6, 10 ], - "arch": "well_bucket", - "form": "tiled", "target_map": "/shattered_islands/world_9_65", "target_tile": [ 6, 10 - ] + ], + "arch": "well_bucket", + "line": 860, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_65_-1", - "line": 1500, "source_tile": [ 11, 18 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_9_65_-1", "target_tile": [ 11, 21 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1500, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_65_-1", - "line": 1517, "source_tile": [ 11, 19 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_9_65_-1", "target_tile": [ 11, 18 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1517, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_65_-1", - "line": 1534, "source_tile": [ 11, 20 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_9_65_-1", "target_tile": [ 11, 21 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1534, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_65_-1", - "line": 1556, "source_tile": [ 11, 21 ], - "arch": "bollard_tied_s_2", - "form": "same-map", "target_map": "/shattered_islands/world_9_65_-1", "target_tile": [ 11, 18 - ] + ], + "arch": "bollard_tied_s_2", + "line": 1556, + "form": "same-map", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68", - "line": 229, "source_tile": [ 1, 2 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_-1", "target_tile": [ 1, 2 - ] + ], + "arch": "ladder_down2", + "line": 229, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_68", - "line": 238, "source_tile": [ 1, 3 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_1", "target_tile": [ 1, 3 - ] + ], + "arch": "ladder_up", + "line": 238, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68", - "line": 425, "source_tile": [ 1, 20 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_1", "target_tile": [ 1, 20 - ] + ], + "arch": "ladder_up", + "line": 425, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68", - "line": 434, "source_tile": [ 1, 21 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_-1", "target_tile": [ 1, 21 - ] + ], + "arch": "ladder_down2", + "line": 434, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_68_-1", - "line": 174, "source_tile": [ 1, 2 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68", "target_tile": [ 1, 2 - ] + ], + "arch": "ladder_up", + "line": 174, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68_-1", - "line": 275, "source_tile": [ 1, 21 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68", "target_tile": [ 1, 21 - ] + ], + "arch": "ladder_up", + "line": 275, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68_1", - "line": 167, "source_tile": [ 1, 3 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68", "target_tile": [ 1, 3 - ] + ], + "arch": "ladder_down2", + "line": 167, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_68_1", - "line": 198, "source_tile": [ 1, 7 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_2", "target_tile": [ 1, 7 - ] + ], + "arch": "ladder_up", + "line": 198, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68_1", - "line": 251, "source_tile": [ 1, 16 ], - "arch": "ladder_up", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_2", "target_tile": [ 1, 16 - ] + ], + "arch": "ladder_up", + "line": 251, + "form": "tiled", + "vertical": "up" }, { "source_map": "/shattered_islands/world_9_68_1", - "line": 282, "source_tile": [ 1, 20 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68", "target_tile": [ 1, 20 - ] + ], + "arch": "ladder_down2", + "line": 282, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_68_2", - "line": 273, "source_tile": [ 1, 7 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_1", "target_tile": [ 1, 7 - ] + ], + "arch": "ladder_down2", + "line": 273, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_68_2", - "line": 334, "source_tile": [ 1, 16 ], - "arch": "ladder_down2", - "form": "tiled", "target_map": "/shattered_islands/world_9_68_1", "target_tile": [ 1, 16 - ] + ], + "arch": "ladder_down2", + "line": 334, + "form": "tiled", + "vertical": "down" }, { "source_map": "/shattered_islands/world_9_69", - "line": 2455, "source_tile": [ 17, 13 ], - "arch": "exit", - "form": "same-map", "target_map": "/shattered_islands/world_9_69", "target_tile": [ 18, 12 + ], + "arch": "exit", + "line": 2455, + "form": "same-map", + "vertical": "none", + "event_scripts": [ + "/python/generic/apartment_teleport.py" ] } ], @@ -31836,11 +33431,6 @@ "vertical": "down", "source_group": "/shattered_islands/world", "target_group": "/shattered_islands/deserted_tutorial_island/ship_lower_deck", - "authored_target": [ - null, - null, - null - ], "wired": { "target_from": "the tile below (plus the landing offset [0, 1])", "plane_landing": [ @@ -32683,11 +34273,6 @@ "vertical": "up", "source_group": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", "target_group": "/shattered_islands/world", - "authored_target": [ - null, - null, - null - ], "wired": { "target_from": "the tile above (plus the landing offset [0, 0])", "plane_landing": [ @@ -32752,11 +34337,6 @@ "vertical": "up", "source_group": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", "target_group": "/shattered_islands/world", - "authored_target": [ - null, - null, - null - ], "wired": { "target_from": "the tile above (plus the landing offset [0, 0])", "plane_landing": [ @@ -44537,11 +46117,6 @@ "vertical": "up", "source_group": "/shattered_islands/strakewood_island/rockforge/rockforge", "target_group": "/shattered_islands/world", - "authored_target": [ - null, - null, - null - ], "wired": { "target_from": "the destination of the invis_exit on the same tile, 1 tile(s) from the landing", "plane_landing": [ @@ -44667,11 +46242,6 @@ "vertical": "up", "source_group": "/shattered_islands/strakewood_island/rockforge/rockforge", "target_group": "/shattered_islands/world", - "authored_target": [ - null, - null, - null - ], "wired": { "target_from": "the destination of the invis_exit on the same tile, 1 tile(s) from the landing", "plane_landing": [ diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 26049f2..2eba552 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -325,13 +325,39 @@ def place_pool(pool, connected, blockers, depth, place): return order, failed, remaining -def inventory_entry(e: dict) -> dict: - """One exit object as read from the content, before placement or wiring.""" - rec = {"source_map": e["source_map"], "line": e["line"], "source_tile": [e["source_x"], e["source_y"]], - "arch": e["arch"], "form": e["form"]} +def exit_source_fields(e: dict) -> dict: + """The fields of an exit that come from the content alone, in record order. + + The exit records and the exit inventory both use them, so the verifier can + compare every record with the exit object it was made from.""" + r = {"source_map": e["source_map"], "source_tile": [e["source_x"], e["source_y"]]} if e.get("resolved_map"): - rec["target_map"] = e["resolved_map"] - rec["target_tile"] = [e["resolved_x"], e["resolved_y"]] + r["target_map"] = e["resolved_map"] + r["target_tile"] = [e["resolved_x"], e["resolved_y"]] + r.update(arch=e["arch"], line=e["line"], form=e["form"], vertical=e["vdir"]) + if e.get("target_map") is not None and e["target_map"] != e.get("resolved_map"): + r["authored_target"] = [e["target_map"], e.get("target_x"), e.get("target_y")] + if e.get("target_default_entry"): + r["target_is_map_default_entry"] = True + if e["per_player"]: + r["per_player_last_eat_1"] = True + if e["event_scripts"]: + r["event_scripts"] = e["event_scripts"] + if e.get("dir_inferred_from"): + r["vertical_inferred_from"] = e["dir_inferred_from"] + return r + + +def inventory_entry(e: dict) -> dict: + """One exit object as read from the content, before placement or wiring. + + Besides the record fields it carries the reader's facts the data problems + are derived from: a target problem and an explicit last_heal 0.""" + rec = exit_source_fields(e) + if e.get("problem"): + rec["problem"] = e["problem"] + if e.get("explicit_last_heal_0"): + rec["explicit_last_heal_0"] = True return rec @@ -1206,7 +1232,8 @@ def run_placement(radius): o2, f2, l2 = run_pool(tentative_ids, True) o3, f3 = [], [] for gid, (anchor, dl, why) in sorted(TENTATIVE_ALIGN.items()): - if gid in G and gid not in S.placed and anchor in S.placed: + # only for groups no pool tried: a group whose own placement failed stays failed + if gid in G and gid not in S.placements and anchor in S.placed: a = S.placed[anchor] res = place_group(gid, tentative=True, forced={"dx": a["dx"], "dy": a["dy"], "L": a["L"] + dl}) if placement_failed(res): @@ -1308,22 +1335,11 @@ def portal_reason(e, s, t, dist, dl): return out def exit_record(e, with_eval=True): - r = { - "source_map": e["source_map"], "source_tile": [e["source_x"], e["source_y"]], - "target_map": e["resolved_map"], "target_tile": [e["resolved_x"], e["resolved_y"]], - "arch": e["arch"], "line": e["line"], "form": e["form"], "vertical": e["vdir"], - "source_group": e["source_group"], "target_group": e["target_group"], - } - if e["resolved_map"] != e.get("target_map"): - r["authored_target"] = [e.get("target_map"), e.get("target_x"), e.get("target_y")] - if e.get("target_default_entry"): - r["target_is_map_default_entry"] = True - if e["per_player"]: - r["per_player_last_eat_1"] = True - if e["event_scripts"]: - r["event_scripts"] = e["event_scripts"] - if e.get("dir_inferred_from"): - r["vertical_inferred_from"] = e["dir_inferred_from"] + src = exit_source_fields(e) + r = {k: src.pop(k) for k in ("source_map", "source_tile", "target_map", "target_tile", "arch", "line", + "form", "vertical")} + r.update(source_group=e["source_group"], target_group=e["target_group"]) + r.update(src) if e.get("wired"): r["wired"] = e["wired"] if with_eval: diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index f20104e..cbffe1d 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -146,7 +146,8 @@ def test_dropping_the_plane_endpoints_of_a_placed_exit(self): for _g, _lst, e in copies(p, CROSSING): for k in ("plane_source", "plane_target", "xy_distance", "layer_delta", "becomes"): e.pop(k) - self.assertCaught(p, "exit {}:{} lacks plane endpoints".format(*CROSSING)) + self.assertCaught(p, "exit {}:{} lacks placement fields ['plane_source', 'plane_target', 'xy_distance', " + "'layer_delta', 'becomes'], although both of its ends are placed".format(*CROSSING)) def test_a_wrong_overworld_plane_end(self): p = self.plan() @@ -194,6 +195,56 @@ def test_a_data_problem_that_differs_from_the_inventory(self): p["data_problems"]["exits"][0]["source_tile"] = [99, 99] self.assertCaught(p, "data problem of exit") + def test_deleting_a_data_problem(self): + p = self.plan() + lst = p["data_problems"]["exits"] + d = next(d for d in lst if d["kind"] == "exit-without-destination") + lst.remove(d) + self.assertCaught(p, "1 exit data problem(s) differ from the exit inventory, e.g. exit-without-destination " + "{} of exit {}:{}".format(d["severity"], d["source_map"], d["line"])) + p = self.plan() + d = p["data_problems"]["exits"][0] + d["severity"] = "info" if d.get("severity") == "problem" else "problem" + self.assertCaught(p, "exit data problem(s) differ from the exit inventory") + + def test_a_reversed_exit_direction(self): + p = self.plan() + key = ("/shattered_islands/berri_lur/temple_loki/tol_0201", 441) + self.assertTrue(copies(p, key)) + for _g, _lst, e in copies(p, key): + e["vertical"] = "down" if e["vertical"] == "up" else "up" + self.assertCaught(p, "record of exit {}:{} differs from its inventory entry in vertical".format(*key)) + + def test_partial_plane_endpoints_on_an_unplaced_exit(self): + p = self.plan() + key = ("/emergency", 17) + for _g, _lst, e in copies(p, key): + self.assertNotIn("plane_source", e) + e["plane_source"] = [0, 0, 0] + self.assertCaught(p, "exit {}:{} has placement fields ['plane_source'], although not both of its ends " + "are placed".format(*key)) + + def test_placement_fields(self): + cases = (("xy_distance", 5, "records 5 tiles"), ("layer_delta", 3, "/ 3 layers"), + ("stairwell", "SW1", "labelled stairs records stairwell SW1"), + ("portal_reason", "overworld", "portal reason overworld"), + ("bogus", 1, "has fields the verifier does not know: bogus")) + for field, value, needle in cases: + with self.subTest(field=field): + p = self.plan() + for _g, _lst, e in copies(p, CROSSING): + e[field] = value + self.assertCaught(p, needle) + + def test_a_wrong_portal_reason(self): + p = self.plan() + e = next(e for g in p["groups"] for e in g.get("entrances_from_other_groups", []) + if e.get("portal_reason") == "other-entrance-aligned") + for _g, _lst, c in copies(p, (e["source_map"], e["line"])): + c["portal_reason"] = "group-shifted" + self.assertCaught(p, "portal reason group-shifted (slope reachable None); expected portal reason " + "other-entrance-aligned") + class StairwellReplaces(VerifierTest): """Fix 2: every stairwell replaces entry equals its indexed exit record.""" @@ -236,6 +287,15 @@ def test_other_copies(self): p["relayering"]["exits"][0]["crosses_to_group"] = "/emergency" self.assertCaught(p, "crosses to /emergency") + def test_an_unrelated_exit_at_a_blocked_site(self): + p = self.plan() + key = ("/shattered_islands/world_11_61", 668) + e = copies(p, key)[0][2] + self.assertEqual(e["becomes"], "stairs") + p["stairwell_sites_blocked"][0]["exits"].append( + {"source_map": e["source_map"], "source_tile": e["source_tile"], "arch": e["arch"], "line": e["line"]}) + self.assertCaught(p, "blocked column stairwell site lists exit {}:{}, which needs no stairwell".format(*key)) + class FailedPlacement(VerifierTest): """Fix 3: a plan with a failed group does not verify, so it is not published.""" @@ -280,6 +340,18 @@ def test_failed_tentative_group(self): self.fail(p, gid) self.assertCaught(p, "placement of {} failed".format(gid)) + def test_tentative_placement_only_by_classification_or_hint(self): + p = self.plan() + gid = p["summary"]["tentative_placement_order"][0] + g = group(p, gid) + self.assertEqual((g["classification"], g["reason"]), ("separate", "unreachable")) + g["placement"].pop("tentative_reason") + self.assertCaught(p, "separate group {} (unreachable) has a tentative placement without an alignment " + "hint".format(gid)) + p = self.plan() + group(p, gid)["reason"] = "per_player" + self.assertCaught(p, "separate group {} (per_player) has a tentative placement".format(gid)) + def test_place_group_without_a_placement(self): p = self.plan() group(p, self.GID).pop("placement") diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index e38ebf1..7694057 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -5,8 +5,9 @@ Placement (every placed group, proposed and tentative): * every place group has a proposed placement and every uncertain group a - tentative one; a failed placement fails the plan; the summary's placement - order, unplaced groups and failed placements are re-derived + tentative one (an unreachable group may have one from an alignment hint); + a failed placement fails the plan; the summary's placement order, unplaced + groups and failed placements are re-derived * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -37,17 +38,21 @@ * exit_inventory lists every exit object of the content once; the exit records are exactly the copies it calls for (one per group a crossing exit touches, the overworld's own exits in internal_exits; a wired exit as - data_problems lists it) and each equals its inventory entry; the summary - and per-group crossing counts are re-derived from the records + data_problems lists it); every content field of a record (tiles, target, + archetype, form, direction, flags) equals its inventory entry, and a record + holds no field the verifier does not know; the exit data problems (kind, + severity) and the summary and per-group crossing counts are re-derived * every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) equals its record - or inventory entry + or inventory entry; a blocked site lists only exits that need a stairwell * the records of one exit (one per group it touches) are identical; an exit labelled stairwell is listed in exactly that stairwell's replaces - * an exit record has plane_source / plane_target exactly when both ends - are placed (or overworld); they and overworld_plane_source / _target - equal origin + tile on the tile's final layer, and its "becomes" label - matches its geometry; + * an exit record has its placement fields (plane ends, distances, label) + exactly when both ends are placed (or overworld); the plane ends and + overworld_plane_source / _target equal origin + tile on the tile's final + layer, xy_distance and layer_delta follow from them, the "becomes" label + matches the geometry, and a portal's reason (and slope_reachable) is + re-derived from the blocked sites, the layers and the later placed end; wired exits are plain stairs and their verdict follows from their evidence; exits the owner keeps as decoration have no exit record * every exit 2+ layers and at most one tile per layer apart is a stairwell @@ -78,6 +83,15 @@ WORLD_RE = re.compile(r"/world_(-?\d+)_(-?\d+)(?:_(-?\d+))?$") N8 = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1) if (dx, dy) != (0, 0)] +# Fields of an exit record that come from the content: equal to its inventory entry. +SOURCE_FIELDS = ("source_map", "source_tile", "target_map", "target_tile", "arch", "line", "form", "vertical", + "authored_target", "target_is_map_default_entry", "per_player_last_eat_1", "event_scripts", + "vertical_inferred_from") +# Fields a record has exactly when both of its ends are placed, and those that depend on its label. +PLACED_FIELDS = ("plane_source", "plane_target", "xy_distance", "layer_delta", "becomes") +LABEL_FIELDS = ("stairwell", "portal_reason", "portal_reason_text", "slope_reachable") +RECORD_FIELDS = set(SOURCE_FIELDS) | set(PLACED_FIELDS) | set(LABEL_FIELDS) | { + "source_group", "target_group", "wired", "overworld_plane_source", "overworld_plane_target"} def world_cell(path: str): @@ -223,6 +237,12 @@ def put(owner, mapname, layer, x0, y0, rows): errors.append("{} group {} has no {} placement".format(g["classification"], gid, want)) if s == "proposed" and g["classification"] != "place": errors.append("{} group {} has a proposed placement".format(g["classification"], gid)) + # tentative: an uncertain group, or an unreachable group placed only by an alignment hint + if s == "tentative" and g["classification"] != "uncertain" and not ( + g["classification"] == "separate" and g["reason"] == "unreachable" + and g["placement"].get("tentative_reason")): + errors.append("{} group {} ({}) has a tentative placement without an alignment hint".format( + g["classification"], gid, g["reason"])) index = {g["id"]: g["placement"].get("placement_index") for g in placed} order = summ["placement_order"] + summ["tentative_placement_order"] if sorted(order) != sorted(index) or [index[gid] for gid in order] != list(range(len(order))) or \ @@ -411,7 +431,7 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): expected_copies = set() want_rec = {} # exit key -> the record fields the inventory (and wiring) fix for k, x in sorted(inv.items()): - want = {f: x.get(f) for f in ("source_map", "line", "source_tile", "arch", "form", "target_map", "target_tile")} + want = {f: x.get(f) for f in SOURCE_FIELDS} if k in wired_dp: if x["form"] != "tiled-unresolved" or "target_map" in x: errors.append("wired exit {}:{} is not a legacy exit that leads nowhere".format(*k)) @@ -442,9 +462,11 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): errors.append("{} exit record(s) the exit inventory does not call for, e.g. exit {}:{} in {} of {}".format( len(extra), extra[0][2][0], extra[0][2][1], extra[0][1], extra[0][0])) for k, e in sorted(records.items()): + unknown = sorted(set(e) - RECORD_FIELDS) + if unknown: + errors.append("exit {}:{} has fields the verifier does not know: {}".format(k[0], k[1], ", ".join(unknown))) if k in want_rec: - same_as("record", e, want_rec[k], ("source_tile", "arch", "form", "target_map", "target_tile", - "source_group", "target_group"), "inventory entry") + same_as("record", e, want_rec[k], SOURCE_FIELDS + ("source_group", "target_group"), "inventory entry") if (e.get("form") == "wired") != ("wired" in e): errors.append("exit {}:{} has form {} but {} wiring evidence".format( k[0], k[1], e.get("form"), "carries" if "wired" in e else "no")) @@ -470,9 +492,33 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): if what != "data problem" and x["form"] != "tiled-unresolved": errors.append("{} exit {}:{} is not a legacy exit that leads nowhere".format( what, d["source_map"], d["line"])) - if {(d["source_map"], d["line"]) for d in plan["data_problems"]["exits"] if d["kind"] == "tiled-exit-without-link"} \ - != {k for k, x in inv.items() if x["form"] == "tiled-unresolved"}: - errors.append("tiled exits without a link in the data problems differ from the exit inventory") + # the exit data problems, re-derived from the inventory: kind and severity of each + size = {m["path"]: (m["width"], m["height"]) for g in others for m in g["maps"]} + working = {(x["source_map"], tuple(x["source_tile"])) for x in inv.values() if "target_map" in x} + want_dp = collections.Counter() + for k, x in sorted(inv.items()): + form, tile = x["form"], tuple(x["source_tile"]) + sev = None + if form in ("tiled-unresolved", "unresolved") and not x.get("event_scripts"): + sev = "info" if (x["source_map"], tile) in working or x.get("explicit_last_heal_0") else "problem" + w, h = size.get(x["source_map"], (st["overworld_map_size"],) * 2) + if not (0 <= tile[0] < w and 0 <= tile[1] < h): + want_dp[(k, "exit-outside-map-bounds", sev)] += 1 + if form == "tiled-unresolved": + want_dp[(k, "tiled-exit-without-link", sev)] += 1 + elif form == "unresolved": + if not x.get("event_scripts"): + want_dp[(k, "exit-without-destination", sev)] += 1 + elif x.get("problem"): + want_dp[(k, x["problem"], sev)] += 1 + elif form == "dynamic": + want_dp[(k, "dynamic-random-map-exit", "info")] += 1 + got_dp = collections.Counter(((d["source_map"], d["line"]), d["kind"], d.get("severity")) + for d in plan["data_problems"]["exits"]) + if got_dp != want_dp: + diff = sorted((want_dp - got_dp) + (got_dp - want_dp), key=str) + errors.append("{} exit data problem(s) differ from the exit inventory, e.g. {} {} of exit {}:{}".format( + len(diff), diff[0][1], diff[0][2], *diff[0][0])) # ----------------------------------------------------------- stairwells chain = collections.defaultdict(dict) # layer -> owner -> Rows @@ -585,12 +631,62 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): for sw in plan["stairwells"]: for r in sw["replaces"]: replaced_by[(r["source_map"], r["line"])].append(sw["id"]) + def needs_stairwell(e): + """Ends 2+ layers and at most one tile per layer apart, unless a legacy overworld shaft kept as it was.""" + s_, d_ = e["plane_source"], e["plane_target"] + dl, dist = abs(d_[2] - s_[2]), cheb(s_, d_) + if dl < 2 or dist > dl * tol: + return False + if e["source_group"] == ow_id and e["target_group"] == ow_id: + return abs(world_cell(e["target_map"])[2] - world_cell(e["source_map"])[2]) != dl + return True + + blocked_keys = {} + for key_, sites in (("column", plan["stairwell_sites_blocked"]), ("sloped", plan["sloped_stairwell_sites_blocked"]), + ("relayering", rl["stairwells_blocked"])): + for b in sites: + for x in b["exits"]: + k = (x["source_map"], x["line"]) + if k in blocked_keys: + errors.append("exit {}:{} is listed at two blocked stairwell sites".format(*k)) + r = records.get(k) + if r is None: + errors.append("blocked {} stairwell site lists exit {}:{}, which has no exit record".format(key_, *k)) + else: + same_as("blocked {} site entry".format(key_), x, r, ("source_tile", "arch")) + if "plane_source" not in r or "plane_target" not in r or not needs_stairwell(r): + errors.append("blocked {} stairwell site lists exit {}:{}, which needs no stairwell".format( + key_, *k)) + blocked_keys[k] = (key_, b) + + def portal_reason(e, k, dist, dl): + """The generator's reason an exit stays a portal, and whether a sloped stairwell would reach.""" + if k in blocked_keys: + return ("slope-blocked", True) if blocked_keys[k][0] == "sloped" else ("stairwell-blocked", None) + if dl == 0: + return "same-layer", None + ends_ = [x for x in (e["source_group"], e["target_group"]) if x != ow_id] + if not ends_: + return "overworld", None + later = max(ends_, key=lambda x: index.get(x, -1)) + pl = placed_by_id[later]["placement"] + mc = pl.get("main_connection") or {} + slope = True if 2 <= dist <= dl * tol else None + if pl.get("shift_from_ideal") != [0, 0]: + return "group-shifted", slope + if mc.get("source_map") == e["source_map"] and mc.get("source_tile") == e["source_tile"]: + return "main-entrance-offset", slope + return "other-entrance-aligned", slope + + placed_by_id = {g["id"]: g for g in placed} exits_checked = 0 for (src_map, line), e in sorted(records.items()): live = all(x == ow_id or x in placed_ids for x in (e["source_group"], e["target_group"])) - if live != ("plane_source" in e and "plane_target" in e): - errors.append("exit {}:{} {} plane endpoints, but {} of its ends is placed".format( - src_map, line, "lacks" if live else "has", "each" if live else "not each")) + have = [f for f in PLACED_FIELDS + LABEL_FIELDS if f in e] + lacks = [f for f in PLACED_FIELDS if f not in e] + if live and lacks or not live and have: + errors.append("exit {}:{} {} placement fields {}, although {} of its ends are placed".format( + src_map, line, "lacks" if live else "has", lacks if live else have, "both" if live else "not both")) continue ends = {} for end, ow_end, mkey, tkey, gkey in ( @@ -622,7 +718,17 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): if len(ends) == 2: s, d = ends["plane_source"], ends["plane_target"] dist, dl = cheb(s, d), abs(d[2] - s[2]) + if e["xy_distance"] != dist or e["layer_delta"] != d[2] - s[2]: + errors.append("exit {}:{} records {} tiles / {} layers, its ends are {} / {} apart".format( + src_map, line, e["xy_distance"], e["layer_delta"], dist, d[2] - s[2])) b = e.get("becomes") + want_reason, want_slope = portal_reason(e, (src_map, line), dist, dl) if b == "portal" else (None, None) + if ("stairwell" in e) != (b == "stairwell") or e.get("portal_reason") != want_reason or \ + ("portal_reason_text" in e) != (want_reason is not None) or e.get("slope_reachable") != want_slope: + errors.append("exit {}:{} labelled {} records stairwell {}, portal reason {} (slope reachable {}); " + "expected portal reason {} (slope reachable {})".format( + src_map, line, b, e.get("stairwell"), e.get("portal_reason"), + e.get("slope_reachable"), want_reason, want_slope)) sw = sw_by_id.get(e.get("stairwell")) ok = {"stairs": dl == 1 and dist <= tol, "stairwell": sw is not None and dl >= 2 and dist <= (tol if sw["shape"] == "column" else dl * tol), @@ -706,31 +812,12 @@ def counted(es): # layer must be a stairwell, unless a blocked site lists it: crossing exits # (placed groups) always, overworld exits only when the relayering changed # their layer distance. Blocked sites must really have no void chain. - blocked_keys = {} - for key_, sites in (("column", plan["stairwell_sites_blocked"]), ("sloped", plan["sloped_stairwell_sites_blocked"]), - ("relayering", rl["stairwells_blocked"])): - for b in sites: - for x in b["exits"]: - k = (x["source_map"], x["line"]) - if k in blocked_keys: - errors.append("exit {}:{} is listed at two blocked stairwell sites".format(*k)) - if k not in records: - errors.append("blocked {} stairwell site lists exit {}:{}, which has no exit record".format(key_, *k)) - else: - same_as("blocked {} site entry".format(key_), x, records[k], ("source_tile", "arch")) - blocked_keys[k] = (key_, b) required = 0 for (src_map, line), e in sorted(records.items()): - if "plane_source" not in e or "plane_target" not in e: + if "plane_source" not in e or "plane_target" not in e or not needs_stairwell(e): continue s_, d_ = e["plane_source"], e["plane_target"] dl, dist = abs(d_[2] - s_[2]), cheb(s_, d_) - if dl < 2 or dist > dl * tol: - continue - if e["source_group"] == ow_id and e["target_group"] == ow_id: - legacy = abs(world_cell(e["target_map"])[2] - world_cell(e["source_map"])[2]) - if legacy == dl: - continue # a legacy overworld shaft the relayering leaves alone required += 1 if (src_map, line) in blocked_keys: if e.get("becomes") != "portal": From 808ea4acfb2e4627dc28c30addf3784932852f34 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 13:01:04 -0500 Subject: [PATCH 03/20] fix(placement-plan): require a valid main connection for each placement Address the second Codex review round on #47. A placement must name a main connection unless an alignment hint forced it, and never both. The connection's kind must be "in" or "out", it must be one of the group's exit records of that kind, and its other end must be the overworld or a group placed earlier. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 23 ++++++++++++ .../placement-plan/scripts/verify_plan.py | 37 ++++++++++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index cbffe1d..e8a24a8 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -190,6 +190,29 @@ def test_a_main_connection_that_is_no_record(self): group(p, MINE + "mine_entrance_a")["placement"]["main_connection"]["source_tile"] = [0, 0] self.assertCaught(p, "main connection of {} is not one of its exit records".format(MINE + "mine_entrance_a")) + def test_main_connections(self): + deck = "/shattered_islands/deserted_tutorial_island/ship_lower_deck" + p = self.plan() + group(p, deck)["placement"]["main_connection"] = None + self.assertCaught(p, "placement of {} has no main connection".format(deck)) + p = self.plan() + group(p, deck)["placement"]["main_connection"]["kind"] = "sideways" + self.assertCaught(p, "main connection of {} has kind sideways".format(deck)) + p = self.plan() + group(p, deck)["placement"]["tentative_reason"] = "hint" + self.assertCaught(p, "placement of {} has both a main connection and an alignment hint".format(deck)) + # mine_entrance_a is entered from mine_entrance, placed before it; claim it was placed after + p = self.plan() + a, b = group(p, MINE + "mine_entrance_a")["placement"], group(p, MINE + "mine_entrance")["placement"] + self.assertEqual(a["main_connection"]["source_map"], MINE + "mine_entrance") + a["placement_index"], b["placement_index"] = b["placement_index"], a["placement_index"] + o = p["summary"]["placement_order"] + i, j = o.index(MINE + "mine_entrance_a"), o.index(MINE + "mine_entrance") + o[i], o[j] = o[j], o[i] + res = verify_plan.verify(p) + self.assertIn("main connection of {} leads to {}, which is not placed before it".format( + MINE + "mine_entrance_a", MINE + "mine_entrance"), res["errors"]) + def test_a_data_problem_that_differs_from_the_inventory(self): p = self.plan() p["data_problems"]["exits"][0]["source_tile"] = [99, 99] diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 7694057..44f9b03 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -44,7 +44,9 @@ severity) and the summary and per-group crossing counts are re-derived * every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) equals its record - or inventory entry; a blocked site lists only exits that need a stairwell + or inventory entry; a blocked site lists only exits that need a stairwell; + every placement has a main connection to something placed before it, + except one forced by an alignment hint * the records of one exit (one per group it touches) are identical; an exit labelled stairwell is listed in exactly that stairwell's replaces * an exit record has its placement fields (plane ends, distances, label) @@ -796,16 +798,31 @@ def counted(es): if mine != g["placement"].get("crossing_exits_after_placement"): errors.append("crossing exits of {} {} differ from its exit records {}".format( g["id"], g["placement"].get("crossing_exits_after_placement"), mine)) - # the main connection is one of the group's exit records + # the main connection is one of the group's exit records, to something placed before it; + # only a placement forced by an alignment hint has none mc = g["placement"].get("main_connection") - if mc is not None: - keys = ("entrances_from_overworld", "entrances_from_other_groups") if mc.get("kind") == "in" else \ - ("exits_to_overworld", "exits_to_other_groups") - if not any([e["source_map"], e["source_tile"], e["target_map"], e["target_tile"], e["arch"], - e["vertical"]] == [mc.get("source_map"), mc.get("source_tile"), mc.get("target_map"), - mc.get("target_tile"), mc.get("arch"), mc.get("vdir")] - for key in keys for e in g.get(key, [])): - errors.append("main connection of {} is not one of its exit records".format(g["id"])) + hinted = bool(g["placement"].get("tentative_reason")) + if mc is None or hinted: + if (mc is None) != hinted: + errors.append("placement of {} has {}".format( + g["id"], "no main connection" if mc is None else "both a main connection and an alignment hint")) + continue + if mc.get("kind") not in ("in", "out"): + errors.append("main connection of {} has kind {}".format(g["id"], mc.get("kind"))) + continue + keys = ("entrances_from_overworld", "entrances_from_other_groups") if mc["kind"] == "in" else \ + ("exits_to_overworld", "exits_to_other_groups") + match = [e for key in keys for e in g.get(key, []) + if [e["source_map"], e["source_tile"], e["target_map"], e["target_tile"], e["arch"], e["vertical"]] + == [mc.get("source_map"), mc.get("source_tile"), mc.get("target_map"), mc.get("target_tile"), + mc.get("arch"), mc.get("vdir")]] + if not match: + errors.append("main connection of {} is not one of its exit records".format(g["id"])) + continue + other = match[0]["source_group"] if mc["kind"] == "in" else match[0]["target_group"] + oi, gi = index.get(other), index.get(g["id"]) + if other != ow_id and not (isinstance(oi, int) and isinstance(gi, int) and oi < gi): + errors.append("main connection of {} leads to {}, which is not placed before it".format(g["id"], other)) # ------------------------------------------------- required stairwells # Every exit whose ends are 2+ layers apart and at most one tile apart per From a1f5c1706dc37f73ecae91c17d26213cb51fc3a8 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 13:28:12 -0500 Subject: [PATCH 04/20] fix(placement-plan): re-derive exit data problems from the inventory Address the third Codex review round on #47. - Every inventory entry's source map must belong to a group, including exits without a destination. - The inventory carries the remaining facts the exit data problems come from: the tile-link slot of a tiled exit without a link, a dynamic exit's authored path, and a target problem with its authored target. The verifier re-derives every data-problem entry field by field, details and notes included. - Two more exit copies are checked. A blocked site may list only exits between its own two owners (or between the top and bottom maps of a relayering site), and a kept decoration's plane_source must be its tile on the plane. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/placement-plan.json | 173 ++++++++++++------ .../placement-plan/scripts/build_plan.py | 14 +- .../scripts/test_placement_plan.py | 34 +++- .../placement-plan/scripts/verify_plan.py | 82 +++++++-- 4 files changed, 220 insertions(+), 83 deletions(-) diff --git a/migration/placement-plan/placement-plan.json b/migration/placement-plan/placement-plan.json index c9b9a3d..de15332 100644 --- a/migration/placement-plan/placement-plan.json +++ b/migration/placement-plan/placement-plan.json @@ -98,7 +98,7 @@ "relayering": "overworld tiles are on their final layer everywhere in this plan (see relayering); occupied_rows_by_layer gives each map's tiles per final layer", "wired_exits": "an exit with form 'wired' led nowhere in legacy; it now targets the content on the layer above or below (see its 'wired' evidence)", "exits_kept_as_decoration": "dangling exits the owner keeps as decoration (no exit): not wired, with the landing they would have had", - "exit_inventory": "every exit object read from the content (inventory exits excluded), keyed by source_map and line, with its legacy form and resolved target (none for an exit that leads nowhere); the exit records in the groups are checked against it" + "exit_inventory": "every exit object read from the content (inventory exits excluded), keyed by source_map and line: the content fields of its exit record with its legacy form and resolved target (none for an exit that leads nowhere), plus the facts its data problems come from; the exit records and data problems are checked against it" }, "summary": { "maps_total": 3629, @@ -6618,7 +6618,8 @@ "arch": "stairs_wood_up", "line": 2078, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/guilds/quick_wolves/guild", @@ -6629,7 +6630,8 @@ "arch": "stairs_wood_down", "line": 2631, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/guilds/quick_wolves/guild", @@ -6768,7 +6770,8 @@ "arch": "stairs_stone_up", "line": 1676, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", @@ -6779,7 +6782,8 @@ "arch": "stairs_stone_up", "line": 1845, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/planes/bone_cairn/bone_cairn_0_0", @@ -6806,7 +6810,8 @@ "arch": "stairs_stone_up", "line": 2009, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/planes/creation/creation_0501", @@ -7802,7 +7807,8 @@ "arch": "ladder_down", "line": 64, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", @@ -7829,7 +7835,8 @@ "arch": "ladder_up", "line": 181, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", @@ -7840,7 +7847,8 @@ "arch": "ladder_down", "line": 537, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", @@ -7851,7 +7859,8 @@ "arch": "ladder_up", "line": 732, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/eld_woods_island/clearhaven/mine/mine_entrance", @@ -10508,7 +10517,8 @@ "arch": "ladder_up", "line": 415, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10519,7 +10529,8 @@ "arch": "ladder_up", "line": 476, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10530,7 +10541,8 @@ "arch": "ladder_up", "line": 541, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10557,7 +10569,8 @@ "arch": "ladder_up", "line": 2619, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10568,7 +10581,8 @@ "arch": "ladder_up", "line": 2688, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10579,7 +10593,8 @@ "arch": "ladder_up", "line": 2757, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/greyton/jail/jail", @@ -10895,7 +10910,8 @@ "arch": "stairs_stone_up", "line": 954, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", @@ -10939,7 +10955,8 @@ "arch": "stairs_stone_up", "line": 1161, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_0101", @@ -11033,7 +11050,8 @@ "arch": "stairs_stone_up", "line": 343, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11077,7 +11095,8 @@ "arch": "stairs_stone_up", "line": 506, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11137,7 +11156,8 @@ "arch": "stairs_stone_up", "line": 591, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11165,7 +11185,8 @@ "arch": "stairs_stone_up", "line": 608, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11176,7 +11197,8 @@ "arch": "stairs_stone_up", "line": 629, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11203,7 +11225,8 @@ "arch": "stairs_stone_up", "line": 735, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11231,7 +11254,8 @@ "arch": "stairs_stone_up", "line": 759, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11242,7 +11266,8 @@ "arch": "stairs_stone_up", "line": 787, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11269,7 +11294,8 @@ "arch": "stairs_stone_up", "line": 1425, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11297,7 +11323,8 @@ "arch": "stairs_stone_up", "line": 1442, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11308,7 +11335,8 @@ "arch": "stairs_stone_up", "line": 1470, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11319,7 +11347,8 @@ "arch": "stairs_stone_up", "line": 1504, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11379,7 +11408,8 @@ "arch": "stairs_stone_up", "line": 1589, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11407,7 +11437,8 @@ "arch": "stairs_stone_up", "line": 1613, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11418,7 +11449,8 @@ "arch": "stairs_stone_up", "line": 1641, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -11429,7 +11461,8 @@ "arch": "stairs_stone_up", "line": 1675, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/strakewood_island/rockforge/rockforge_a_0101", @@ -12629,7 +12662,8 @@ "arch": "stairs_stone_down", "line": 2207, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_-1_44", @@ -12640,7 +12674,8 @@ "arch": "stairs_stone_up", "line": 1592, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_-1_44", @@ -12651,7 +12686,8 @@ "arch": "stairs_stone_up", "line": 1607, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_-1_44", @@ -12662,7 +12698,8 @@ "arch": "stairs_stone_up", "line": 1733, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_-1_44", @@ -12673,7 +12710,8 @@ "arch": "stairs_stone_up", "line": 1748, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_-1_57", @@ -13134,7 +13172,8 @@ "arch": "stairs_wood_down", "line": 1192, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_-7_76", @@ -15973,7 +16012,8 @@ "arch": "stairs_stone_up", "line": 737, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_10_43_-2", @@ -16173,7 +16213,8 @@ "arch": "stairs_stone_down", "line": 540, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_11_67", @@ -16411,7 +16452,8 @@ "arch": "stairs_wood_down", "line": 2058, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_12_4", @@ -16518,7 +16560,8 @@ "arch": "stairs_stone_down", "line": 2382, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_12_68", @@ -16585,7 +16628,8 @@ "arch": "stairs_stone_down", "line": 1148, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_13_64", @@ -17195,7 +17239,8 @@ "arch": "stairs_stone_down", "line": 1958, "form": "dynamic", - "vertical": "down" + "vertical": "down", + "dynamic_target": "/random/" }, { "source_map": "/shattered_islands/world_19_4", @@ -17715,7 +17760,8 @@ "arch": "stairs_stone_up", "line": 2367, "form": "tiled-unresolved", - "vertical": "up" + "vertical": "up", + "tiled_slot": 9 }, { "source_map": "/shattered_islands/world_1_73", @@ -17726,7 +17772,8 @@ "arch": "stairs_stone_down", "line": 2384, "form": "dynamic", - "vertical": "down" + "vertical": "down", + "dynamic_target": "/random/" }, { "source_map": "/shattered_islands/world_1_73", @@ -18702,7 +18749,8 @@ "arch": "ladder_down", "line": 295, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_2_60", @@ -19273,7 +19321,8 @@ "arch": "stairs_stone_down", "line": 2231, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_2_87", @@ -19300,7 +19349,8 @@ "arch": "ladder_down3", "line": 374, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_3_44", @@ -19375,7 +19425,8 @@ "arch": "ladder_down3", "line": 2212, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_3_56", @@ -19419,7 +19470,8 @@ "arch": "stairs_stone_down", "line": 1504, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_3_60", @@ -19716,7 +19768,8 @@ "arch": "ladder_down", "line": 591, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_3_83_-2", @@ -19954,7 +20007,8 @@ "arch": "stairs_stone_down", "line": 1250, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_4_52", @@ -20384,7 +20438,8 @@ "arch": "stairs_stone_down", "line": 2033, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_4_58", @@ -20414,7 +20469,8 @@ "arch": "stairs_stone_down", "line": 3269, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_4_67", @@ -22052,7 +22108,8 @@ "arch": "ladder_down", "line": 439, "form": "tiled-unresolved", - "vertical": "down" + "vertical": "down", + "tiled_slot": 10 }, { "source_map": "/shattered_islands/world_6_43_-1", diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 2eba552..ccf272b 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -352,10 +352,17 @@ def inventory_entry(e: dict) -> dict: """One exit object as read from the content, before placement or wiring. Besides the record fields it carries the reader's facts the data problems - are derived from: a target problem and an explicit last_heal 0.""" + are derived from: the tile-link slot of a tiled exit without a link, the + authored path of a dynamic exit, a target problem with its authored target, + and an explicit last_heal 0.""" rec = exit_source_fields(e) + if e["form"] == "tiled-unresolved": + rec["tiled_slot"] = e["tiled_slot"] + if e["form"] == "dynamic": + rec["dynamic_target"] = e.get("raw_target") if e.get("problem"): rec["problem"] = e["problem"] + rec["problem_target"] = [e.get("target_map"), e.get("target_x"), e.get("target_y")] if e.get("explicit_last_heal_0"): rec["explicit_last_heal_0"] = True return rec @@ -2014,8 +2021,9 @@ def bbox_layers(gid, dx=0, dy=0, L=0): "exits_kept_as_decoration": "dangling exits the owner keeps as decoration (no exit): not wired, with the " "landing they would have had", "exit_inventory": "every exit object read from the content (inventory exits excluded), keyed by " - "source_map and line, with its legacy form and resolved target (none for an exit " - "that leads nowhere); the exit records in the groups are checked against it", + "source_map and line: the content fields of its exit record with its legacy form " + "and resolved target (none for an exit that leads nowhere), plus the facts its data " + "problems come from; the exit records and data problems are checked against it", }, "summary": {"maps_total": len(maps), "groups_total": len(groups), "non_overworld_groups": len(groups) - 1, "by_classification": dict(sorted(counts.items())), diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index e8a24a8..13cfec2 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -223,13 +223,37 @@ def test_deleting_a_data_problem(self): lst = p["data_problems"]["exits"] d = next(d for d in lst if d["kind"] == "exit-without-destination") lst.remove(d) - self.assertCaught(p, "1 exit data problem(s) differ from the exit inventory, e.g. exit-without-destination " - "{} of exit {}:{}".format(d["severity"], d["source_map"], d["line"])) + self.assertCaught(p, "1 exit data problem(s) differ from the exit inventory, e.g. ", + '"kind": "exit-without-destination", "line": {}'.format(d["line"])) p = self.plan() d = p["data_problems"]["exits"][0] d["severity"] = "info" if d.get("severity") == "problem" else "problem" self.assertCaught(p, "exit data problem(s) differ from the exit inventory") + def test_an_exit_on_a_map_that_is_in_no_group(self): + p = self.plan() + key = ("/shattered_islands/deserted_tutorial_island/ship_lower_deck", 64) + for d in p["exit_inventory"] + p["data_problems"]["exits"]: + if (d["source_map"], d["line"]) == key: + d["source_map"] = "/not/a/map" + self.assertCaught(p, "exit /not/a/map:64 is on a map that is in no group") + + def test_a_dynamic_exit_target(self): + key = ("/shattered_islands/world_18_4", 1958) + p = self.plan() + d = next(d for d in p["data_problems"]["exits"] if (d["source_map"], d["line"]) == key) + self.assertEqual(d["kind"], "dynamic-random-map-exit") + d["detail"] = "/somewhere/else" + self.assertCaught(p, "exit data problem(s) differ from the exit inventory") + p = self.plan() + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == key)["dynamic_target"] = "/elsewhere" + self.assertCaught(p, "exit data problem(s) differ from the exit inventory") + + def test_a_kept_decoration_in_the_wrong_place(self): + p = self.plan() + p["exits_kept_as_decoration"][0]["plane_source"] = [0, 0, 0] + self.assertCaught(p, "kept as decoration is at [0, 0, 0]") + def test_a_reversed_exit_direction(self): p = self.plan() key = ("/shattered_islands/berri_lur/temple_loki/tol_0201", 441) @@ -310,6 +334,12 @@ def test_other_copies(self): p["relayering"]["exits"][0]["crosses_to_group"] = "/emergency" self.assertCaught(p, "crosses to /emergency") + def test_blocked_site_owners(self): + p = self.plan() + p["stairwell_sites_blocked"][0]["other"] = "/shattered_islands/world_0_0" + self.assertCaught(p, "lists exit {}:{} of other owners".format( + p["stairwell_sites_blocked"][0]["exits"][0]["source_map"], p["stairwell_sites_blocked"][0]["exits"][0]["line"])) + def test_an_unrelated_exit_at_a_blocked_site(self): p = self.plan() key = ("/shattered_islands/world_11_61", 668) diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 44f9b03..034b827 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -40,11 +40,14 @@ touches, the overworld's own exits in internal_exits; a wired exit as data_problems lists it); every content field of a record (tiles, target, archetype, form, direction, flags) equals its inventory entry, and a record - holds no field the verifier does not know; the exit data problems (kind, - severity) and the summary and per-group crossing counts are re-derived + holds no field the verifier does not know; the exit data problems (every + entry, field by field) and the summary and per-group crossing counts are + re-derived * every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) equals its record - or inventory entry; a blocked site lists only exits that need a stairwell; + or inventory entry; a blocked site lists only exits that need a stairwell, + between its own two owners (or top and bottom maps); a kept decoration + sits at its tile on the plane; every placement has a main connection to something placed before it, except one forced by an alignment hint * the records of one exit (one per group it touches) are identical; an @@ -94,6 +97,8 @@ LABEL_FIELDS = ("stairwell", "portal_reason", "portal_reason_text", "slope_reachable") RECORD_FIELDS = set(SOURCE_FIELDS) | set(PLACED_FIELDS) | set(LABEL_FIELDS) | { "source_group", "target_group", "wired", "overworld_plane_source", "overworld_plane_target"} +# Tile-link slot names of the map format (the reader's SLOT_NAMES), used in data-problem details. +SLOT_NAMES = {1: "N", 2: "E", 3: "S", 4: "W", 5: "NE", 6: "SE", 7: "SW", 8: "NW", 9: "up", 10: "down"} def world_cell(path: str): @@ -439,11 +444,15 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): errors.append("wired exit {}:{} is not a legacy exit that leads nowhere".format(*k)) want.update(form="wired", target_map=wired_dp[k].get("target_map"), target_tile=wired_dp[k].get("target_tile")) + sg = group_of.get(x["source_map"]) + if sg is None: + errors.append("exit {}:{} is on a map that is in no group".format(*k)) + continue if want["target_map"] is None: continue - sg, tg = group_of.get(x["source_map"]), group_of.get(want["target_map"]) - if sg is None or tg is None: - errors.append("exit {}:{} joins a map that is in no group".format(*k)) + tg = group_of.get(want["target_map"]) + if tg is None: + errors.append("exit {}:{} leads to {}, which is in no group".format(k[0], k[1], want["target_map"])) continue want.update(source_group=sg, target_group=tg) want_rec[k] = want @@ -494,33 +503,41 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): if what != "data problem" and x["form"] != "tiled-unresolved": errors.append("{} exit {}:{} is not a legacy exit that leads nowhere".format( what, d["source_map"], d["line"])) - # the exit data problems, re-derived from the inventory: kind and severity of each + # the exit data problems, re-derived entry by entry from the inventory size = {m["path"]: (m["width"], m["height"]) for g in others for m in g["maps"]} working = {(x["source_map"], tuple(x["source_tile"])) for x in inv.values() if "target_map" in x} - want_dp = collections.Counter() + want_dp = [] for k, x in sorted(inv.items()): form, tile = x["form"], tuple(x["source_tile"]) - sev = None + base = {f: x[f] for f in ("source_map", "source_tile", "arch", "line")} if form in ("tiled-unresolved", "unresolved") and not x.get("event_scripts"): - sev = "info" if (x["source_map"], tile) in working or x.get("explicit_last_heal_0") else "problem" + if (x["source_map"], tile) in working: + base.update(severity="info", note="another exit object on the same tile has a destination " + "(decorative duplicate)") + elif x.get("explicit_last_heal_0"): + base.update(severity="info", note="explicitly disabled with last_heal 0 (decorative)") + else: + base["severity"] = "problem" w, h = size.get(x["source_map"], (st["overworld_map_size"],) * 2) if not (0 <= tile[0] < w and 0 <= tile[1] < h): - want_dp[(k, "exit-outside-map-bounds", sev)] += 1 + want_dp.append(dict(base, kind="exit-outside-map-bounds")) if form == "tiled-unresolved": - want_dp[(k, "tiled-exit-without-link", sev)] += 1 + want_dp.append(dict(base, kind="tiled-exit-without-link", detail="last_heal {} ({}) but the map has no " + "such tile link; the exit leads nowhere".format(x.get("tiled_slot"), + SLOT_NAMES.get(x.get("tiled_slot"))))) elif form == "unresolved": if not x.get("event_scripts"): - want_dp[(k, "exit-without-destination", sev)] += 1 + want_dp.append(dict(base, kind="exit-without-destination", + detail="no slaying, no tiled slot, hp/sp -1 and no event script")) elif x.get("problem"): - want_dp[(k, x["problem"], sev)] += 1 + want_dp.append(dict(base, kind=x["problem"], target=x.get("problem_target"))) elif form == "dynamic": - want_dp[(k, "dynamic-random-map-exit", "info")] += 1 - got_dp = collections.Counter(((d["source_map"], d["line"]), d["kind"], d.get("severity")) - for d in plan["data_problems"]["exits"]) + want_dp.append(dict(base, kind="dynamic-random-map-exit", detail=x.get("dynamic_target"), severity="info")) + want_dp = collections.Counter(json.dumps(d, sort_keys=True) for d in want_dp) + got_dp = collections.Counter(json.dumps(d, sort_keys=True) for d in plan["data_problems"]["exits"]) if got_dp != want_dp: - diff = sorted((want_dp - got_dp) + (got_dp - want_dp), key=str) - errors.append("{} exit data problem(s) differ from the exit inventory, e.g. {} {} of exit {}:{}".format( - len(diff), diff[0][1], diff[0][2], *diff[0][0])) + diff = sorted((want_dp - got_dp) + (got_dp - want_dp)) + errors.append("{} exit data problem(s) differ from the exit inventory, e.g. {}".format(len(diff), diff[0])) # ----------------------------------------------------------- stairwells chain = collections.defaultdict(dict) # layer -> owner -> Rows @@ -643,6 +660,10 @@ def needs_stairwell(e): return abs(world_cell(e["target_map"])[2] - world_cell(e["source_map"])[2]) != dl return True + def owner_of_end(e, side): + """The owner of one end of an exit: the overworld map itself, else the group.""" + return e[side + "_map"] if e[side + "_group"] == ow_id else e[side + "_group"] + blocked_keys = {} for key_, sites in (("column", plan["stairwell_sites_blocked"]), ("sloped", plan["sloped_stairwell_sites_blocked"]), ("relayering", rl["stairwells_blocked"])): @@ -659,6 +680,16 @@ def needs_stairwell(e): if "plane_source" not in r or "plane_target" not in r or not needs_stairwell(r): errors.append("blocked {} stairwell site lists exit {}:{}, which needs no stairwell".format( key_, *k)) + elif key_ == "relayering": + up_src = r["plane_source"][2] > r["plane_target"][2] + ends_ = [r["source_map"], r["target_map"]] if up_src else [r["target_map"], r["source_map"]] + if ends_ != [b.get("top_map"), b.get("bottom_map")]: + errors.append("blocked relayering site {} -> {} lists exit {}:{} between {}".format( + b.get("top_map"), b.get("bottom_map"), k[0], k[1], ends_)) + elif sorted([owner_of_end(r, "source"), owner_of_end(r, "target")]) != \ + sorted([b.get("group"), b.get("other")]): + errors.append("blocked {} site of {} / {} lists exit {}:{} of other owners".format( + key_, b.get("group"), b.get("other"), *k)) blocked_keys[k] = (key_, b) def portal_reason(e, k, dist, dl): @@ -777,6 +808,17 @@ def portal_reason(e, k, dist, dl): errors.append("exit {}:{} is kept as decoration but has an exit record".format(d["source_map"], d["line"])) if not d.get("decision") or "no exit" not in d.get("migrate_as", ""): errors.append("exit {}:{} is kept as decoration without its decision".format(d["source_map"], d["line"])) + # where the kept object ends up: its tile on the plane, like an exit's source end + mp, (tx, ty) = d["source_map"], d["source_tile"] + if group_of.get(mp) == ow_id: + X, Y, _Z = world_cell(mp) + exp = [24 * X + tx, 24 * Y + ty, ow_layer(mp, tx, ty)] + else: + o = origin.get(mp) + exp = None if o is None else [o[0] + tx, o[1] + ty, o[2]] + if d.get("plane_source") != exp: + errors.append("exit {}:{} kept as decoration is at {}, its tile is at {}".format( + d["source_map"], d["line"], d.get("plane_source"), exp)) # crossing counts, re-derived from the records (one per exit object) def counted(es): From 0006dd7296d9f336db92b30ce6fc9fc04da49639 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 13:51:31 -0500 Subject: [PATCH 05/20] fix(placement-plan): re-derive stairwell, site and placement metadata Address the fourth Codex review round on #47. - Portal records: portal_reason_text is re-derived exactly, like the code and slope_reachable. - Stairwells: the shape follows from the ends (column or sloped, no other value), the kind is known and the ids run in one series per kind. max_bend, the owners (group, owner, other, connects: from the replaced exits), and the intermediate tiles' decoration (the exits' stair family) are re-derived. A stairwell must replace at least one exit. - Blocked sites: each must list an exit and have the shape of its ends, and a group's site belongs to the later placed end. The summary's stairwell and blocked-site counts are re-derived. - Groups: classifications and reasons must be known values, there is one overworld group, and the summary's group, map and classification counts are re-derived. - Placements: the offsets must equal the ideal spot plus the recorded shift and extra layers, and the ideal must follow from the main connection within the recorded vertical bounds. - The verifier's docstring now lists what it does not re-derive (the generator's explanations and evidence). The plan is unchanged; a rerun is byte-identical. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 87 ++++++++ .../placement-plan/scripts/verify_plan.py | 193 +++++++++++++++--- 2 files changed, 257 insertions(+), 23 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 13cfec2..4cd76a1 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -190,6 +190,25 @@ def test_a_main_connection_that_is_no_record(self): group(p, MINE + "mine_entrance_a")["placement"]["main_connection"]["source_tile"] = [0, 0] self.assertCaught(p, "main connection of {} is not one of its exit records".format(MINE + "mine_entrance_a")) + def test_a_main_connection_that_did_not_fix_the_placement(self): + # the deck's other up-stairs, one tile east, would put the deck one tile further west + deck = "/shattered_islands/deserted_tutorial_island/ship_lower_deck" + p = self.plan() + mc = group(p, deck)["placement"]["main_connection"] + mc.update(source_tile=[2, 6], arch="stairs_wood_up") + self.assertCaught(p, "ideal spot {'dx': -161, 'dy': 1832, 'L': -1} of " + deck + " does not follow from its " + "main connection ({'dx': -162, 'dy': 1832, 'L': -1})") + + def test_offsets_that_are_not_the_ideal_spot(self): + # ellamin sits 4 layers below its ideal spot: flipping its orientation moves the ideal layer the other way + ellamin = "/shattered_islands/incuna/ellamin" + for field, value in (("shift_from_ideal", [1, 0]), ("extra_layers_from_ideal", 1), ("orientation", "above"), + ("orientation", "sideways")): + with self.subTest(field=field, value=value): + p = self.plan() + group(p, ellamin)["placement"][field] = value + self.assertCaught(p, "offsets of {} are not its ideal spot".format(ellamin)) + def test_main_connections(self): deck = "/shattered_islands/deserted_tutorial_island/ship_lower_deck" p = self.plan() @@ -283,6 +302,24 @@ def test_placement_fields(self): e[field] = value self.assertCaught(p, needle) + def test_a_wrong_portal_reason_text(self): + p = self.plan() + e = next(e for g in p["groups"] for e in g.get("entrances_from_other_groups", []) if e.get("portal_reason")) + for _g, _lst, c in copies(p, (e["source_map"], e["line"])): + c["portal_reason_text"] = "no reason" + self.assertCaught(p, "exit {}:{} labelled portal".format(e["source_map"], e["line"])) + + def test_classifications(self): + p = self.plan() + group(p, "/emergency")["classification"] = "seperate" + self.assertCaught(p, "group /emergency has classification seperate with reason system") + p = self.plan() + group(p, "/emergency")["reason"] = "place" + self.assertCaught(p, "group /emergency has classification separate with reason place") + p = self.plan() + p["summary"]["by_classification"]["separate:system"] += 1 + self.assertCaught(p, "summary group and map counts differ from the groups") + def test_a_wrong_portal_reason(self): p = self.plan() e = next(e for g in p["groups"] for e in g.get("entrances_from_other_groups", []) @@ -314,6 +351,56 @@ def test_all_fields_at_once(self): plane_source=[0, 0, 0], plane_target=[0, 0, 0]) self.assertCaught(p, "SW1 replaces entry") + def test_shape(self): + p = self.plan() + sr1 = next(sw for sw in p["stairwells"] if sw["id"] == "SR1") + sr1["shape"] = "banana" + self.assertCaught(p, "SR1 shape banana does not fit") + p = self.plan() + sr1 = next(sw for sw in p["stairwells"] if sw["id"] == "SR1") + sr1["shape"] = "sloped" if sr1["shape"] == "column" else "column" + self.assertCaught(p, "SR1 shape {} does not fit".format(sr1["shape"])) + + def test_owners(self): + p = self.plan() + sw = p["stairwells"][0] + sw["other"], sw["connects"] = "/nowhere", sorted([sw["group"], "/nowhere"]) + self.assertCaught(p, "SW1 owners") + p = self.plan() + sr1 = next(sw for sw in p["stairwells"] if sw["id"] == "SR1") + sr1["owner"] = "stairwell@0,0,0" + self.assertCaught(p, "SR1 owners") + + def test_intermediate_appearance(self): + p = self.plan() + sw = p["stairwells"][0] + sw["intermediate_appearance"] = "stairs_red_down" + self.assertCaught(p, "SW1 intermediate tiles do not show") + p = self.plan() + p["stairwells"][0]["tiles"][1]["objects"][0]["walkable"] = True + self.assertCaught(p, "SW1 intermediate tiles do not show") + + def test_series_bend_and_counts(self): + p = self.plan() + p["stairwells"][0]["id"], p["stairwells"][1]["id"] = p["stairwells"][1]["id"], p["stairwells"][0]["id"] + self.assertCaught(p, "is out of its series") + p = self.plan() + p["stairwells"][0]["max_bend"] += 1 + self.assertCaught(p, "SW1 max_bend") + p = self.plan() + p["summary"]["stairwells_by_kind"]["placement"] += 1 + self.assertCaught(p, "summary stairwell and blocked-site counts differ from the lists") + + def test_an_empty_blocked_site(self): + for key in ("stairwell_sites_blocked", "sloped_stairwell_sites_blocked"): + with self.subTest(key=key): + p = self.plan() + p[key].append(dict(p[key][0], exits=[])) + self.assertCaught(p, "lists 0 exits") + p = self.plan() + p["stairwell_sites_blocked"][0]["shape"] = "sloped" + self.assertCaught(p, "with shape sloped (column by its ends)") + def test_direction(self): p = self.plan() r = p["stairwells"][0]["replaces"][0] diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 034b827..c200aca 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -3,11 +3,19 @@ python3 -I scripts/verify_plan.py [placement-plan.json] +Groups: + * one overworld group; every group's classification and reason are known + values; the summary's group, map and classification counts are re-derived Placement (every placed group, proposed and tentative): * every place group has a proposed placement and every uncertain group a tentative one (an unreachable group may have one from an alignment hint); a failed placement fails the plan; the summary's placement order, unplaced groups and failed placements are re-derived + * offsets = ideal + shift_from_ideal and extra_layers_from_ideal in the + orientation's direction; the ideal follows from the main connection (its + far end, one layer away in the exit's direction, within the recorded + vertical bounds), which is one of the group's exits to the overworld or + an earlier placed group; only an alignment-hinted placement has none * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -34,6 +42,10 @@ exactly the exit objects (archetype, exit flag) that start there (a one-way stairwell's far end is a landing); every replaces entry equals its exit record (tiles, target, archetype, plane endpoints, direction) + * kind, shape (from its ends), id series, max_bend, owners (group, owner, + other, connects: from the exits it replaces) and the intermediate tiles' + decoration (the exits' stair family) are re-derived; so are the summary's + stairwell and blocked-site counts Exits: * exit_inventory lists every exit object of the content once; the exit records are exactly the copies it calls for (one per group a crossing exit @@ -45,9 +57,10 @@ re-derived * every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) equals its record - or inventory entry; a blocked site lists only exits that need a stairwell, - between its own two owners (or top and bottom maps); a kept decoration - sits at its tile on the plane; + or inventory entry; a blocked site lists at least one exit, only exits + that need a stairwell, between its own two owners (or top and bottom + maps), and has the shape of its ends; a kept decoration sits at its tile + on the plane; every placement has a main connection to something placed before it, except one forced by an alignment hint * the records of one exit (one per group it touches) are identical; an @@ -56,8 +69,8 @@ exactly when both ends are placed (or overworld); the plane ends and overworld_plane_source / _target equal origin + tile on the tile's final layer, xy_distance and layer_delta follow from them, the "becomes" label - matches the geometry, and a portal's reason (and slope_reachable) is - re-derived from the blocked sites, the layers and the later placed end; + matches the geometry, and a portal's reason, its text and slope_reachable + are re-derived from the blocked sites, the layers and the later placed end; wired exits are plain stairs and their verdict follows from their evidence; exits the owner keeps as decoration have no exit record * every exit 2+ layers and at most one tile per layer apart is a stairwell @@ -74,6 +87,18 @@ moved tile ends on layer 0 * cut walkable edges, seams, seam pieces and their new stairs; steep steps; new contacts; moved maps and the before/after layer deltas of exits +Not re-derived: the generator's explanations and evidence, which the +migration does not act on. That is classification evidence, reach depth and +script references; the placement rationale beyond the consistency above +(orientation and its evidence, the vertical bounds and constraint note, +conflicts_resolved, main_stairwell_blocked, bounding boxes, formulas); the +evidence of blocked sites (column_occupied, layers, reason), of wiring and of +kept decorations (would_land_on) other than the verdict and target; the +relayering's descriptions (why, resolution, judgement calls, cave options, the +maps, boxes and z ranges listed per region and unit) beyond the re-derived +regions, units and final layers; placement_changes_from_v1, +view_radius_sensitivity and conventions; and the tile-link, layout and +connectivity data problems. Exit status 0 when every check passes. """ @@ -97,6 +122,12 @@ LABEL_FIELDS = ("stairwell", "portal_reason", "portal_reason_text", "slope_reachable") RECORD_FIELDS = set(SOURCE_FIELDS) | set(PLACED_FIELDS) | set(LABEL_FIELDS) | { "source_group", "target_group", "wired", "overworld_plane_source", "overworld_plane_target"} +# Stair families: a stairwell's intermediate tiles show the down archetype of the first family +# (in this order) that one of its exit archetypes belongs to (build_plan.STAIR_FAMILIES). +STAIR_FAMILIES = (("stairs_spiral_stone_", "stairs_spiral_stone_down"), ("stairs_stone_", "stairs_stone_down"), + ("stairs_wood_", "stairs_wood_down"), ("stairs_red_", "stairs_red_down"), + ("stairs_", "stairs_down"), ("ladder_", "ladder_down")) +SW_PREFIX = {"placement": "SW", "sloped": "SL", "relayering": "SR"} # Tile-link slot names of the map format (the reader's SLOT_NAMES), used in data-problem details. SLOT_NAMES = {1: "N", 2: "E", 3: "S", 4: "W", 5: "NE", 6: "SE", 7: "SW", 8: "NW", 9: "up", 10: "down"} @@ -232,6 +263,20 @@ def put(owner, mapname, layer, x0, y0, rows): # (proposed) and every uncertain group tentatively. The summary's order and # unplaced lists are re-derived from the placement records. others = [g for g in plan["groups"] if g["classification"] != "overworld"] + reasons = {"overworld": {"overworld"}, "place": {"place"}, "uncertain": {"uncertain"}, + "separate": {"guild", "other_world", "per_player", "ship", "system", "unreachable"}} + for g in plan["groups"]: + if g.get("reason") not in reasons.get(g["classification"], ()): + errors.append("group {} has classification {} with reason {}".format( + g["id"], g["classification"], g.get("reason"))) + if sum(1 for g in plan["groups"] if g["classification"] == "overworld") != 1: + errors.append("the plan has no single overworld group") + if summ.get("groups_total") != len(plan["groups"]) or summ.get("non_overworld_groups") != len(others) or \ + summ.get("maps_total") != sum(g["map_count"] for g in plan["groups"]) or \ + any(g["map_count"] != len(g["maps"]) for g in others) or \ + summ.get("by_classification") != dict(sorted(collections.Counter( + "{}:{}".format(g["classification"], g["reason"]) for g in others).items())): + errors.append("summary group and map counts differ from the groups") status = {g["id"]: (g.get("placement") or {}).get("status") for g in others} for g in others: gid, s = g["id"], status[g["id"]] @@ -405,6 +450,10 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): errors.append("{} of exit {}:{} differs from its {} in {}".format( what, ref["source_map"], ref["line"], ref_name, ", ".join(bad))) + def owner_of_end(e, side): + """The owner of one end of an exit: the overworld map itself, else the group.""" + return e[side + "_map"] if e[side + "_group"] == ow_id else e[side + "_group"] + # The exit inventory is the expected set: every exit object read from the # content. Each one with a destination has a record in every group it touches # (the overworld's own exits in its internal_exits, none for an exit inside @@ -551,6 +600,18 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): for layer in chain.values(): for rr in layer.values(): rr.done() + n_kind = collections.Counter() + for sw in plan["stairwells"]: + n_kind[sw["kind"]] += 1 + if sw["id"] != "{}{}".format(SW_PREFIX.get(sw["kind"], "?"), n_kind[sw["kind"]]): + errors.append("stairwell {} of kind {} is out of its series".format(sw["id"], sw["kind"])) + if summ.get("stairwells") != len(plan["stairwells"]) or summ.get("stairwells_by_kind") != dict(sorted( + n_kind.items())) or summ.get("stairwell_spans_layers") != {str(k): v for k, v in sorted(collections.Counter( + sw["top_layer"] - sw["bottom_layer"] for sw in plan["stairwells"]).items())} or \ + summ.get("stairwell_sites_blocked") != len(plan["stairwell_sites_blocked"]) or \ + summ.get("sloped_stairwell_sites_blocked") != len(plan["sloped_stairwell_sites_blocked"]) or \ + summ.get("relayering_stairwell_sites_blocked") != len(rl["stairwells_blocked"]): + errors.append("summary stairwell and blocked-site counts differ from the lists") sw_tiles = {} intermediate = 0 kinds = collections.Counter() @@ -574,13 +635,54 @@ def same_as(what, copy, ref, fields, ref_name="exit record"): bot = (t[-1]["x"], t[-1]["y"], t[-1]["layer"]) span = top[2] - bot[2] off = cheb(top, bot) - if sw["shape"] == "column" and off > tol or sw["shape"] == "sloped" and not tol < off <= span * tol: + if sw["shape"] != ("column" if off <= tol else "sloped") or off > span * tol: errors.append("{} shape {} does not fit ends {} tiles apart over {} layers".format( sw["id"], sw["shape"], off, span)) if sw["horizontal_offset"] != off: errors.append("{} horizontal_offset {} != {}".format(sw["id"], sw["horizontal_offset"], off)) - if sw["kind"] == "placement" and sw["shape"] != "column" or sw["kind"] == "sloped" and sw["shape"] != "sloped": + if sw["kind"] not in SW_PREFIX or sw["kind"] == "placement" and sw["shape"] != "column" or \ + sw["kind"] == "sloped" and sw["shape"] != "sloped": errors.append("{} kind {} does not match shape {}".format(sw["id"], sw["kind"], sw["shape"])) + if sw.get("max_bend") != max([cheb((x_["x"], x_["y"]), top) for x_ in t[1:-1]] or [0]): + errors.append("{} max_bend {} does not match its tiles".format(sw["id"], sw.get("max_bend"))) + if not sw["replaces"]: + errors.append("{} replaces no exit".format(sw["id"])) + # owners, re-derived from the exits it replaces: a placed group's stairwell joins that group + # (placed after the other end) and the other owner; a relayering one two overworld maps + pairs_ = set() + for r in sw["replaces"]: + e = records.get((r["source_map"], r["line"])) + if e is not None and "plane_source" in e and "plane_target" in e: + if sw["kind"] == "relayering": + up_src = e["plane_source"][2] > e["plane_target"][2] + pairs_.add((e["source_map"], e["target_map"]) if up_src else (e["target_map"], e["source_map"])) + else: + pairs_.add(tuple(sorted([owner_of_end(e, "source"), owner_of_end(e, "target")]))) + if sw["kind"] == "relayering": + want_o = None if len(pairs_) != 1 else { + "group": None, "owner": "stairwell@{},{},{}".format(*top), "other": next(iter(pairs_))[1], + "connects": sorted(set(next(iter(pairs_))))} + else: + g_, o_ = sw.get("group"), sw.get("other") + want_o = None if len(pairs_) != 1 or tuple(sorted([g_, o_])) not in pairs_ or g_ not in placed_ids or \ + o_ in placed_ids and not (index.get(o_, len(index)) < index.get(g_, -1)) else { + "group": g_, "owner": g_, "other": o_, "connects": sorted({g_, o_})} + if want_o is None or any(sw.get(f) != v for f, v in want_o.items()): + errors.append("{} owners (group {}, owner {}, other {}, connects {}) do not follow from the exits it " + "replaces".format(sw["id"], sw.get("group"), sw.get("owner"), sw.get("other"), + sw.get("connects"))) + # intermediate tiles show the down archetype of the exits' stair family; they are not exits + ends_arch = sorted({r["arch"] for r in sw["replaces"] if r["goes"] == "down"}) + \ + sorted({r["arch"] for r in sw["replaces"] if r["goes"] == "up"}) + fam = next((down for a in ends_arch for prefix, down in STAIR_FAMILIES if a.casefold().startswith(prefix)), + None) + app_src = ("appearance of the down archetype of the {} family used by this stairwell's exits; the " + "intermediate objects are decorations, not exits".format(fam.rsplit("_", 1)[0]) if fam else + "no stair archetype among {}; owner to choose".format(ends_arch)) + want_objs_mid = [{"archetype": fam, "exit": False, "walkable": False, "use": "appearance only"}] if fam else [] + if sw.get("intermediate_appearance") != fam or sw.get("intermediate_appearance_source") != app_src or \ + any(x_.get("objects") != want_objs_mid or x_.get("existing") is not False for x_ in t[1:-1]): + errors.append("{} intermediate tiles do not show {} as a decoration".format(sw["id"], fam)) for r in sw["replaces"]: e = records.get((r["source_map"], r["line"])) if e is None: @@ -660,14 +762,18 @@ def needs_stairwell(e): return abs(world_cell(e["target_map"])[2] - world_cell(e["source_map"])[2]) != dl return True - def owner_of_end(e, side): - """The owner of one end of an exit: the overworld map itself, else the group.""" - return e[side + "_map"] if e[side + "_group"] == ow_id else e[side + "_group"] - blocked_keys = {} for key_, sites in (("column", plan["stairwell_sites_blocked"]), ("sloped", plan["sloped_stairwell_sites_blocked"]), ("relayering", rl["stairwells_blocked"])): for b in sites: + shape_ = "sloped" if cheb(b["top"], b["bottom"]) > tol else "column" + if not b["exits"] or b.get("shape") != shape_ or key_ != "relayering" and shape_ != key_: + errors.append("blocked {} stairwell site at {} lists {} exits with shape {} ({} by its ends)".format( + key_, b["top"], len(b["exits"]), b.get("shape"), shape_)) + if key_ != "relayering" and (b.get("group") not in placed_ids or b.get("other") in placed_ids and not ( + index.get(b["other"], len(index)) < index.get(b["group"], -1))): + errors.append("blocked {} stairwell site at {} belongs to {}, which is not the later placed " + "end".format(key_, b["top"], b.get("group"))) for x in b["exits"]: k = (x["source_map"], x["line"]) if k in blocked_keys: @@ -692,24 +798,29 @@ def owner_of_end(e, side): key_, b.get("group"), b.get("other"), *k)) blocked_keys[k] = (key_, b) - def portal_reason(e, k, dist, dl): - """The generator's reason an exit stays a portal, and whether a sloped stairwell would reach.""" + def portal_reason(e, k, s, dist, dl): + """The generator's reason an exit stays a portal, its text, and whether a sloped stairwell would reach.""" + if k in blocked_keys and blocked_keys[k][0] == "sloped": + return "slope-blocked", "{} tiles and {} layers apart, but no void sloped chain exists".format( + dist, dl), True if k in blocked_keys: - return ("slope-blocked", True) if blocked_keys[k][0] == "sloped" else ("stairwell-blocked", None) + return "stairwell-blocked", "aligned over {} layers, but no void stair chain exists".format(dl), None if dl == 0: - return "same-layer", None + return "same-layer", "both ends on layer {}, {} tiles apart".format(s[2], dist), None ends_ = [x for x in (e["source_group"], e["target_group"]) if x != ow_id] if not ends_: - return "overworld", None + return "overworld", "overworld exit whose ends are {} tiles and {} layer(s) apart".format(dist, dl), None later = max(ends_, key=lambda x: index.get(x, -1)) pl = placed_by_id[later]["placement"] mc = pl.get("main_connection") or {} slope = True if 2 <= dist <= dl * tol else None if pl.get("shift_from_ideal") != [0, 0]: - return "group-shifted", slope + return "group-shifted", "{} was shifted {} from its ideal spot".format(later, pl.get("shift_from_ideal")), \ + slope if mc.get("source_map") == e["source_map"] and mc.get("source_tile") == e["source_tile"]: - return "main-entrance-offset", slope - return "other-entrance-aligned", slope + return "main-entrance-offset", "the main connection itself lands {} tiles away".format(dist), slope + return "other-entrance-aligned", "{} is aligned to its main connection; this exit's ends are {} tiles and {} " \ + "layer(s) apart".format(later, dist, dl), slope placed_by_id = {g["id"]: g for g in placed} exits_checked = 0 @@ -755,9 +866,10 @@ def portal_reason(e, k, dist, dl): errors.append("exit {}:{} records {} tiles / {} layers, its ends are {} / {} apart".format( src_map, line, e["xy_distance"], e["layer_delta"], dist, d[2] - s[2])) b = e.get("becomes") - want_reason, want_slope = portal_reason(e, (src_map, line), dist, dl) if b == "portal" else (None, None) + want_reason, want_text, want_slope = portal_reason(e, (src_map, line), s, dist, dl) if b == "portal" \ + else (None, None, None) if ("stairwell" in e) != (b == "stairwell") or e.get("portal_reason") != want_reason or \ - ("portal_reason_text" in e) != (want_reason is not None) or e.get("slope_reachable") != want_slope: + e.get("portal_reason_text") != want_text or e.get("slope_reachable") != want_slope: errors.append("exit {}:{} labelled {} records stairwell {}, portal reason {} (slope reachable {}); " "expected portal reason {} (slope reachable {})".format( src_map, line, b, e.get("stairwell"), e.get("portal_reason"), @@ -840,10 +952,23 @@ def counted(es): if mine != g["placement"].get("crossing_exits_after_placement"): errors.append("crossing exits of {} {} differ from its exit records {}".format( g["id"], g["placement"].get("crossing_exits_after_placement"), mine)) + # the offsets are the ideal spot plus the recorded shift and extra layers (deeper when the group + # is oriented below, higher when above) + pl = g["placement"] + ideal, orient = pl.get("ideal") or {}, pl.get("orientation") + step = {"below": -1, "above": 1}.get(orient) + sh, extra = pl.get("shift_from_ideal") or [None, None], pl.get("extra_layers_from_ideal") + if step is None or not isinstance(extra, int) or extra < 0 or None in (ideal.get("dx"), ideal.get("dy"), + ideal.get("L"), *sh) or \ + [pl["x_offset"], pl["y_offset"], pl["layer_offset"]] != [ + ideal["dx"] + sh[0], ideal["dy"] + sh[1], ideal["L"] + step * extra]: + errors.append("offsets of {} are not its ideal spot {} shifted by {} and {} layer(s) {}".format( + g["id"], ideal, sh, extra, orient)) + continue # the main connection is one of the group's exit records, to something placed before it; # only a placement forced by an alignment hint has none - mc = g["placement"].get("main_connection") - hinted = bool(g["placement"].get("tentative_reason")) + mc = pl.get("main_connection") + hinted = bool(pl.get("tentative_reason")) if mc is None or hinted: if (mc is None) != hinted: errors.append("placement of {} has {}".format( @@ -865,6 +990,28 @@ def counted(es): oi, gi = index.get(other), index.get(g["id"]) if other != ow_id and not (isinstance(oi, int) and isinstance(gi, int) and oi < gi): errors.append("main connection of {} leads to {}, which is not placed before it".format(g["id"], other)) + continue + # the ideal spot puts the connection's end in this group under (or over) its other end: one + # layer away in the exit's direction (or the orientation's), within the vertical bounds + e = match[0] + if "plane_source" not in e or "plane_target" not in e: + continue # reported with the exit records + far, (near_map, near_tile) = (e["plane_source"], (e["target_map"], e["target_tile"])) if mc["kind"] == "in" \ + else (e["plane_target"], (e["source_map"], e["source_tile"])) + lo_ = next((m["local_offset"] for m in g["maps"] if m["path"] == near_map), None) + if lo_ is None: + continue # reported with the exit records + lx, ly, lz = lo_[0] + near_tile[0], lo_[1] + near_tile[1], lo_[2] + d_ = mc["vdir"] if mc["vdir"] != "none" else ( + ("down" if orient == "below" else "up") if mc["kind"] == "in" else ("up" if orient == "below" else "down")) + below = d_ == ("down" if mc["kind"] == "in" else "up") + L_ = far[2] - 1 - lz if below else far[2] + 1 - lz + lo_b, hi_b = (pl.get("vertical_bounds_from_exit_semantics") or [None, None]) + L_ = max(L_, lo_b) if lo_b is not None else L_ + L_ = min(L_, hi_b) if hi_b is not None else L_ + if ideal != {"dx": far[0] - lx, "dy": far[1] - ly, "L": L_}: + errors.append("ideal spot {} of {} does not follow from its main connection ({})".format( + ideal, g["id"], {"dx": far[0] - lx, "dy": far[1] - ly, "L": L_})) # ------------------------------------------------- required stairwells # Every exit whose ends are 2+ layers apart and at most one tile apart per From 9bc81aa7484f5ba3093370a2d70b67b9ed2cf50b Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 14:26:19 -0500 Subject: [PATCH 06/20] fix(placement-plan): bound targets, bends and the overworld map count Address the fifth Codex review round on #47. - The overworld group's map count and per-layer counts are re-derived from its cells, like the other groups' counts from their member lists. - Every resolved target in the exit inventory must lie on a tile of its target map. - A column stairwell may leave its column by at most settings.stairwell_max_bend_tiles, and the blocked-site chain search uses that setting instead of a hard-coded 2. The plan is unchanged; a rerun is byte-identical. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 22 +++++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 19 ++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 4cd76a1..213f9ad 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -320,6 +320,21 @@ def test_classifications(self): p["summary"]["by_classification"]["separate:system"] += 1 self.assertCaught(p, "summary group and map counts differ from the groups") + def test_the_overworld_map_count(self): + p = self.plan() + next(g for g in p["groups"] if g["classification"] == "overworld")["map_count"] += 1 + p["summary"]["maps_total"] += 1 + self.assertCaught(p, "summary group and map counts differ from the groups") + + def test_a_target_outside_its_map(self): + p = self.plan() + key = ("/shattered_islands/ships/ship_1", 122) + x = next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == key) + x.update(target_map="/emergency", target_tile=[999, 999]) + for _g, _lst, e in copies(p, key): + e.update(target_map="/emergency", target_tile=[999, 999]) + self.assertCaught(p, "exit {}:{} targets tile [999, 999] outside /emergency".format(*key)) + def test_a_wrong_portal_reason(self): p = self.plan() e = next(e for g in p["groups"] for e in g.get("entrances_from_other_groups", []) @@ -380,6 +395,13 @@ def test_intermediate_appearance(self): p["stairwells"][0]["tiles"][1]["objects"][0]["walkable"] = True self.assertCaught(p, "SW1 intermediate tiles do not show") + def test_the_bend_limit(self): + p = self.plan() + p["settings"]["stairwell_max_bend_tiles"] = 0 + bent = [sw["id"] for sw in p["stairwells"] if sw["shape"] == "column" and sw["max_bend"]] + self.assertTrue(bent) + self.assertCaught(p, *("{} bends 1 tile(s) from its column".format(i) for i in bent)) + def test_series_bend_and_counts(self): p = self.plan() p["stairwells"][0]["id"], p["stairwells"][1]["id"] = p["stairwells"][1]["id"], p["stairwells"][0]["id"] diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index c200aca..9a75469 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -6,6 +6,7 @@ Groups: * one overworld group; every group's classification and reason are known values; the summary's group, map and classification counts are re-derived + (the overworld's map and per-layer counts from its cells) Placement (every placed group, proposed and tentative): * every place group has a proposed placement and every uncertain group a tentative one (an unreachable group may have one from an alignment hint); @@ -42,12 +43,15 @@ exactly the exit objects (archetype, exit flag) that start there (a one-way stairwell's far end is a landing); every replaces entry equals its exit record (tiles, target, archetype, plane endpoints, direction) + * a column stairwell leaves its column by at most stairwell_max_bend_tiles + (the same limit the blocked-site chain search uses) * kind, shape (from its ends), id series, max_bend, owners (group, owner, other, connects: from the exits it replaces) and the intermediate tiles' decoration (the exits' stair family) are re-derived; so are the summary's stairwell and blocked-site counts Exits: - * exit_inventory lists every exit object of the content once; the exit + * exit_inventory lists every exit object of the content once, each + resolved target on a tile of its target map; the exit records are exactly the copies it calls for (one per group a crossing exit touches, the overworld's own exits in internal_exits; a wired exit as data_problems lists it); every content field of a record (tiles, target, @@ -202,6 +206,7 @@ def flood(keys, same): def verify(plan: dict) -> dict: st = plan["settings"] R, M, tol = st["view_radius_tiles"], st["contact_margin_tiles"], st["stair_tolerance_tiles"] + bend = st["stairwell_max_bend_tiles"] HZ, MINR, WDZ = st["layer_height_z"], st["relayer_min_region_tiles"], st["walkable_dz"] ow = next(g for g in plan["groups"] if g["classification"] == "overworld") ow_id = ow["id"] @@ -274,6 +279,8 @@ def put(owner, mapname, layer, x0, y0, rows): if summ.get("groups_total") != len(plan["groups"]) or summ.get("non_overworld_groups") != len(others) or \ summ.get("maps_total") != sum(g["map_count"] for g in plan["groups"]) or \ any(g["map_count"] != len(g["maps"]) for g in others) or \ + ow["map_count"] != sum(len(c) for c in ow["cells_by_layer"].values()) or \ + ow.get("layers") != {z: len(c) for z, c in sorted(ow["cells_by_layer"].items(), key=lambda kv: int(kv[0]))} or \ summ.get("by_classification") != dict(sorted(collections.Counter( "{}:{}".format(g["classification"], g["reason"]) for g in others).items())): errors.append("summary group and map counts differ from the groups") @@ -570,6 +577,11 @@ def owner_of_end(e, side): w, h = size.get(x["source_map"], (st["overworld_map_size"],) * 2) if not (0 <= tile[0] < w and 0 <= tile[1] < h): want_dp.append(dict(base, kind="exit-outside-map-bounds")) + if "target_map" in x: # the reader resolves a target onto a tile of its map + tw, th = size.get(x["target_map"], (st["overworld_map_size"],) * 2) + if not (0 <= x["target_tile"][0] < tw and 0 <= x["target_tile"][1] < th): + errors.append("exit {}:{} targets tile {} outside {} ({} x {})".format( + k[0], k[1], x["target_tile"], x["target_map"], tw, th)) if form == "tiled-unresolved": want_dp.append(dict(base, kind="tiled-exit-without-link", detail="last_heal {} ({}) but the map has no " "such tile link; the exit leads nowhere".format(x.get("tiled_slot"), @@ -645,6 +657,9 @@ def owner_of_end(e, side): errors.append("{} kind {} does not match shape {}".format(sw["id"], sw["kind"], sw["shape"])) if sw.get("max_bend") != max([cheb((x_["x"], x_["y"]), top) for x_ in t[1:-1]] or [0]): errors.append("{} max_bend {} does not match its tiles".format(sw["id"], sw.get("max_bend"))) + if sw["shape"] == "column" and sw.get("max_bend", 0) > bend: + errors.append("{} bends {} tile(s) from its column, more than stairwell_max_bend_tiles {}".format( + sw["id"], sw.get("max_bend"), bend)) if not sw["replaces"]: errors.append("{} replaces no exit".format(sw["id"])) # owners, re-derived from the exits it replaces: a placed group's stairwell joins that group @@ -1052,7 +1067,7 @@ def chain_exists(top, bottom, shape): for dy in (-1, 0, 1): p_ = (x + dx, y + dy) if shape == "column": - ok = cheb(p_, top) <= 2 # STAIRWELL_MAX_BEND + ok = cheb(p_, top) <= bend else: ok = cheb(p_, top) <= i and cheb(p_, bottom) <= n - i if ok and void_at(z, *p_): From 444de716bfd98cf5dfba1bd923d3497f8558ade9 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 15:16:56 -0500 Subject: [PATCH 07/20] fix(placement-plan): require unique identities and hint-only exemptions Address the extra Codex pass on 9bc81aa in #47. - Group ids and member maps (overworld cells included) must be unique before any count is derived from them. - Only a tentative placement may carry an alignment hint, so a proposed placement can no longer drop its main connection and skip the ideal-spot check. - An uncertain group that no pool reached but that an alignment hint places is no longer also listed as unplaced. The plan is unchanged; a rerun is byte-identical. Co-Authored-By: Claude Opus 5.5 --- .../placement-plan/scripts/build_plan.py | 3 +- .../scripts/test_placement_plan.py | 30 ++++++++++++++++++- .../placement-plan/scripts/verify_plan.py | 19 ++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index ccf272b..8c762ee 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -1277,7 +1277,8 @@ def run_placement(radius): n[sw["kind"]] += 1 sw["id"] = "{}{}".format(prefix[sw["kind"]], n[sw["kind"]]) # A failed group stays unplaced (and fails verification, so the plan is not published). - return o1, sorted(l1 + f1), o2 + o3, sorted(l2 + f2), sorted(f1 + f2 + f3) + # an uncertain group no pool reached may still get its tentative spot from an alignment hint + return o1, sorted(l1 + f1), o2 + o3, sorted((set(l2) - set(o3)) | set(f2)), sorted(f1 + f2 + f3) def becomes_counts(): c = collections.Counter() diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 213f9ad..842d268 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -219,7 +219,15 @@ def test_main_connections(self): self.assertCaught(p, "main connection of {} has kind sideways".format(deck)) p = self.plan() group(p, deck)["placement"]["tentative_reason"] = "hint" - self.assertCaught(p, "placement of {} has both a main connection and an alignment hint".format(deck)) + self.assertCaught(p, "proposed placement of {} carries an alignment hint".format(deck)) + p = self.plan() + group(p, deck)["placement"].update(main_connection=None, tentative_reason="hint") + self.assertCaught(p, "proposed placement of {} carries an alignment hint".format(deck), + "placement of {} has no main connection".format(deck)) + p = self.plan() + tent = group(p, p["summary"]["tentative_placement_order"][0])["placement"] + tent["main_connection"] = dict(group(p, deck)["placement"]["main_connection"]) + self.assertCaught(p, "has both a main connection and an alignment hint") # mine_entrance_a is entered from mine_entrance, placed before it; claim it was placed after p = self.plan() a, b = group(p, MINE + "mine_entrance_a")["placement"], group(p, MINE + "mine_entrance")["placement"] @@ -320,6 +328,26 @@ def test_classifications(self): p["summary"]["by_classification"]["separate:system"] += 1 self.assertCaught(p, "summary group and map counts differ from the groups") + def test_duplicate_identities(self): + p = self.plan() + apt = "/shattered_islands/eld_woods_island/apartments/apartment_cheap" + dup = json.loads(json.dumps(group(p, apt))) + p["groups"].append(dup) + s = p["summary"] + s["groups_total"] += 1 + s["non_overworld_groups"] += 1 + s["maps_total"] += dup["map_count"] + s["by_classification"]["{}:{}".format(dup["classification"], dup["reason"])] += 1 + self.assertCaught(p, "group id {} is listed several times".format(apt)) + p = self.plan() + ow = next(g for g in p["groups"] if g["classification"] == "overworld") + ow["cells_by_layer"]["0"].append(list(ow["cells_by_layer"]["0"][0])) + ow["map_count"] += 1 + ow["layers"]["0"] += 1 + p["summary"]["maps_total"] += 1 + self.assertCaught(p, "member map /shattered_islands/world_{}_{} is listed several times".format( + *ow["cells_by_layer"]["0"][0])) + def test_the_overworld_map_count(self): p = self.plan() next(g for g in p["groups"] if g["classification"] == "overworld")["map_count"] += 1 diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 9a75469..a39b082 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -4,7 +4,8 @@ python3 -I scripts/verify_plan.py [placement-plan.json] Groups: - * one overworld group; every group's classification and reason are known + * one overworld group; group ids and member maps (overworld cells + included) are unique; every group's classification and reason are known values; the summary's group, map and classification counts are re-derived (the overworld's map and per-layer counts from its cells) Placement (every placed group, proposed and tentative): @@ -16,7 +17,8 @@ orientation's direction; the ideal follows from the main connection (its far end, one layer away in the exit's direction, within the recorded vertical bounds), which is one of the group's exits to the overworld or - an earlier placed group; only an alignment-hinted placement has none + an earlier placed group; only an alignment-hinted tentative placement + has none (a proposed placement carries no hint) * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -276,6 +278,16 @@ def put(owner, mapname, layer, x0, y0, rows): g["id"], g["classification"], g.get("reason"))) if sum(1 for g in plan["groups"] if g["classification"] == "overworld") != 1: errors.append("the plan has no single overworld group") + # identities are unique before anything is counted + for what, items in ( + ("group id", [g["id"] for g in plan["groups"]]), + ("member map", [m["path"] for g in others for m in g["maps"]] + [ + "{}_{}_{}{}".format(ow_id, X, Y, "" if int(z) == 0 else "_{}".format(z)) + for z, cells in ow["cells_by_layer"].items() for X, Y in cells])): + dup = sorted(k for k, n in collections.Counter(items).items() if n > 1) + if dup: + errors.append("{} {} {} listed {} times".format(what, dup[0], "and {} more are".format(len(dup) - 1) + if len(dup) > 1 else "is", "several")) if summ.get("groups_total") != len(plan["groups"]) or summ.get("non_overworld_groups") != len(others) or \ summ.get("maps_total") != sum(g["map_count"] for g in plan["groups"]) or \ any(g["map_count"] != len(g["maps"]) for g in others) or \ @@ -984,6 +996,9 @@ def counted(es): # only a placement forced by an alignment hint has none mc = pl.get("main_connection") hinted = bool(pl.get("tentative_reason")) + if hinted and pl.get("status") != "tentative": + errors.append("{} placement of {} carries an alignment hint".format(pl.get("status"), g["id"])) + hinted = False if mc is None or hinted: if (mc is None) != hinted: errors.append("placement of {} has {}".format( From 763010cf9ce860334018cbda9ca3dd48ee7495c1 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 15:37:08 -0500 Subject: [PATCH 08/20] fix(placement-plan): validate source values and structure alignment hints Address the seventh Codex review round on #47. - Inventory entries must have a known exit form and direction. They carry a target exactly when the form resolves one, unless the target map is missing. - A stairwell's mode must be "clear" or "void-only", matching its view conflicts and contacts. - The generator publishes its alignment hints in settings.tentative_alignments. A hinted tentative placement must be one of them and carry its reason, its anchor must be placed first, and its ideal spot must be the anchor's offsets the configured layers away. Arbitrary hint text no longer exempts a placement from the main-connection checks. Regenerated from content 3ad20150da76: placement-plan.json gains settings.tentative_alignments; REPORT.md and the PNGs are unchanged. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/placement-plan.json | 8 ++++ .../placement-plan/scripts/build_plan.py | 2 + .../scripts/test_placement_plan.py | 38 +++++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 36 ++++++++++++++++-- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/migration/placement-plan/placement-plan.json b/migration/placement-plan/placement-plan.json index de15332..cbf9a3a 100644 --- a/migration/placement-plan/placement-plan.json +++ b/migration/placement-plan/placement-plan.json @@ -73,6 +73,14 @@ "ladder_up" ] ], + "tentative_alignments": [ + { + "group": "/shattered_islands/strakewood_island/rockforge/rockforge_c", + "anchor": "/shattered_islands/strakewood_island/rockforge/rockforge_b", + "layer_delta": -1, + "why": "rockforge_a/_b/_c share the same 5x5 sheet naming (0101..ab05) and rockforge_a<->rockforge_b stairs connect identical local coordinates; rockforge_c ('Rockforge: Mines') is assumed to be the next sheet below rockforge_b at the same x/y." + } + ], "overworld_map_size": 24, "excluded_trees": [ "python", diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 8c762ee..7f8212c 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -1981,6 +1981,8 @@ def bbox_layers(gid, dx=0, dy=0, L=0): "stairwell_max_bend_tiles": STAIRWELL_MAX_BEND, "layer_height_z": LAYER_HEIGHT_Z, "relayer_min_region_tiles": relayer.MIN_REGION_TILES, "walkable_dz": relayer.WALKABLE_DZ, "owner_keep_as_decoration": [[m, list(t), a] for (m, t, a) in sorted(OWNER_KEEP_AS_DECORATION)], + "tentative_alignments": [{"group": gid, "anchor": anchor, "layer_delta": dl, "why": why} + for gid, (anchor, dl, why) in sorted(TENTATIVE_ALIGN.items())], "overworld_map_size": 24, "excluded_trees": list(md.EXCLUDED_TOP)}, "conventions": { "plane": "overworld plane; layer 0 surface, negative below, positive above", diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 842d268..818238f 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -354,6 +354,21 @@ def test_the_overworld_map_count(self): p["summary"]["maps_total"] += 1 self.assertCaught(p, "summary group and map counts differ from the groups") + def test_unknown_source_values(self): + for field, value, needle in (("form", "banana", "has form banana"), + ("vertical", "sideways", "has form explicit, direction sideways")): + with self.subTest(field=field): + p = self.plan() + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == CROSSING)[field] = value + for _g, _lst, e in copies(p, CROSSING): + e[field] = value + self.assertCaught(p, "exit {}:{} {}".format(CROSSING[0], CROSSING[1], needle)) + p = self.plan() + x = next(x for x in p["exit_inventory"] if x["form"] == "unresolved") + x.update(target_map="/emergency", target_tile=[0, 0]) + self.assertCaught(p, "exit {}:{} has form unresolved, direction {} and a target".format( + x["source_map"], x["line"], x["vertical"])) + def test_a_target_outside_its_map(self): p = self.plan() key = ("/shattered_islands/ships/ship_1", 122) @@ -430,6 +445,11 @@ def test_the_bend_limit(self): self.assertTrue(bent) self.assertCaught(p, *("{} bends 1 tile(s) from its column".format(i) for i in bent)) + def test_stairwell_mode(self): + p = self.plan() + p["stairwells"][0]["mode"] = "banana" + self.assertCaught(p, "SW1 mode banana disagrees") + def test_series_bend_and_counts(self): p = self.plan() p["stairwells"][0]["id"], p["stairwells"][1]["id"] = p["stairwells"][1]["id"], p["stairwells"][0]["id"] @@ -542,6 +562,24 @@ def test_tentative_placement_only_by_classification_or_hint(self): group(p, gid)["reason"] = "per_player" self.assertCaught(p, "separate group {} (per_player) has a tentative placement".format(gid)) + def test_a_fabricated_alignment_hint(self): + # the last proposed placement turned into an uncertain group with a made-up hint instead of a connection + p = self.plan() + s = p["summary"] + gid = s["placement_order"].pop() + s["tentative_placement_order"].insert(0, gid) + s["by_classification"]["place:place"] -= 1 + s["by_classification"]["uncertain:uncertain"] = 1 + g = group(p, gid) + g.update(classification="uncertain", reason="uncertain") + g["placement"].update(status="tentative", main_connection=None, tentative_reason="made up") + self.assertCaught(p, "alignment hint of {} is not a configured alignment to a placed anchor".format(gid)) + # a configured hint whose layer distance does not match the placement + p = self.plan() + p["settings"]["tentative_alignments"][0]["layer_delta"] -= 1 + self.assertCaught(p, "alignment hint of {} is not a configured alignment".format( + p["settings"]["tentative_alignments"][0]["group"])) + def test_place_group_without_a_placement(self): p = self.plan() group(p, self.GID).pop("placement") diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index a39b082..22466e6 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -18,7 +18,9 @@ far end, one layer away in the exit's direction, within the recorded vertical bounds), which is one of the group's exits to the overworld or an earlier placed group; only an alignment-hinted tentative placement - has none (a proposed placement carries no hint) + has none (a proposed placement carries no hint), and its hint is one of + settings.tentative_alignments: its ideal is the anchor's offsets, the + configured layers away * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -52,8 +54,9 @@ decoration (the exits' stair family) are re-derived; so are the summary's stairwell and blocked-site counts Exits: - * exit_inventory lists every exit object of the content once, each - resolved target on a tile of its target map; the exit + * exit_inventory lists every exit object of the content once, with a + known form and direction and a target exactly when its form resolves one, + each resolved target on a tile of its target map; the exit records are exactly the copies it calls for (one per group a crossing exit touches, the overworld's own exits in internal_exits; a wired exit as data_problems lists it); every content field of a record (tiles, target, @@ -134,6 +137,9 @@ ("stairs_wood_", "stairs_wood_down"), ("stairs_red_", "stairs_red_down"), ("stairs_", "stairs_down"), ("ladder_", "ladder_down")) SW_PREFIX = {"placement": "SW", "sloped": "SL", "relayering": "SR"} +# The reader's exit forms: those that resolve to a target, and those that never do. +TARGET_FORMS = {"explicit", "tiled", "same-map", "automatic-link", "shop-mat"} +NO_TARGET_FORMS = {"tiled-unresolved", "unresolved", "dynamic"} # Tile-link slot names of the map format (the reader's SLOT_NAMES), used in data-problem details. SLOT_NAMES = {1: "N", 2: "E", 3: "S", 4: "W", 5: "NE", 6: "SE", 7: "SW", 8: "NW", 9: "up", 10: "down"} @@ -491,6 +497,14 @@ def owner_of_end(e, side): if k in inv: errors.append("exit {}:{} is listed twice in the exit inventory".format(*k)) inv[k] = x + # a known form and direction; a target exactly when the form resolves one and the map exists + if x.get("form") not in TARGET_FORMS | NO_TARGET_FORMS or x.get("vertical") not in ("up", "down", "none") or \ + ("target_map" in x) != ("target_tile" in x) or \ + "target_map" in x and x["form"] not in TARGET_FORMS or \ + "target_map" not in x and x["form"] in TARGET_FORMS and not str(x.get("problem", "")).startswith( + "missing-target-map"): + errors.append("exit {}:{} has form {}, direction {} and {} target".format( + k[0], k[1], x.get("form"), x.get("vertical"), "a" if "target_map" in x else "no")) if len(plan["exit_inventory"]) != summ["exit_objects"]: errors.append("the exit inventory lists {} exit objects, the summary {}".format( len(plan["exit_inventory"]), summ["exit_objects"])) @@ -771,7 +785,7 @@ def owner_of_end(e, side): and rr.any_within(x["x"], x["y"], M)}) if touch != touched.get(key, []): errors.append("{} tile {} contacts {} != recorded {}".format(sw["id"], key, touch, touched.get(key, []))) - if (sw["mode"] == "clear") != (not sw["view_conflicts"] and not sw["contacts"]): + if sw.get("mode") != ("clear" if not sw["view_conflicts"] and not sw["contacts"] else "void-only"): errors.append("{} mode {} disagrees with its view conflicts and contacts".format(sw["id"], sw["mode"])) # ---------------------------------------------------------------- exits @@ -850,6 +864,11 @@ def portal_reason(e, k, s, dist, dl): "layer(s) apart".format(later, dist, dl), slope placed_by_id = {g["id"]: g for g in placed} + aligns = {} + for a in st.get("tentative_alignments", []): + if a.get("group") in aligns: + errors.append("tentative alignment of {} is configured twice".format(a.get("group"))) + aligns[a.get("group")] = a exits_checked = 0 for (src_map, line), e in sorted(records.items()): live = all(x == ow_id or x in placed_ids for x in (e["source_group"], e["target_group"])) @@ -999,6 +1018,15 @@ def counted(es): if hinted and pl.get("status") != "tentative": errors.append("{} placement of {} carries an alignment hint".format(pl.get("status"), g["id"])) hinted = False + if hinted: + # a configured alignment hint: the group's ideal spot is its anchor's, the given layers away + a = aligns.get(g["id"]) + anchor = placed_by_id.get(a["anchor"]) if a else None + if anchor is None or pl["tentative_reason"] != a["why"] or ideal != { + "dx": anchor["placement"]["x_offset"], "dy": anchor["placement"]["y_offset"], + "L": anchor["placement"]["layer_offset"] + a["layer_delta"]} or \ + not index.get(a["anchor"], len(index)) < index.get(g["id"], -1): + errors.append("alignment hint of {} is not a configured alignment to a placed anchor".format(g["id"])) if mc is None or hinted: if (mc is None) != hinted: errors.append("placement of {} has {}".format( From e91d40da1d6ef7e5811d7bd3161c39e2633dc4a7 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 15:55:41 -0500 Subject: [PATCH 09/20] feat(placement-plan): check published artifacts against the content Address the eighth Codex review round on #47. The JSON-only verifier cannot see an edit that keeps the plan consistent with itself, such as an exit inside one group dropped from exit_inventory together with summary.exit_objects. Another copy in the JSON would only be one more field to edit. build_plan.py --check instead regenerates the plan from the content in a temporary directory and fails unless every published artifact is byte-identical. This binds the exit inventory, and everything else, to the content at content_git_head, and writes nothing. REPORT.md's Reproducing section and the verifier's docstring describe the split. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/REPORT.md | 3 +- .../placement-plan/scripts/build_plan.py | 33 +++++++++++++++++-- migration/placement-plan/scripts/report.py | 6 +++- .../placement-plan/scripts/verify_plan.py | 5 +++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/migration/placement-plan/REPORT.md b/migration/placement-plan/REPORT.md index afc6e4f..5dda199 100644 --- a/migration/placement-plan/REPORT.md +++ b/migration/placement-plan/REPORT.md @@ -883,9 +883,10 @@ cd migration/placement-plan python3 -I scripts/build_plan.py --content /home/zoey/dev/worktrees/content-plan-v2-input --out . python3 -I scripts/verify_plan.py placement-plan.json python3 -I scripts/test_placement_plan.py +python3 -I scripts/build_plan.py --content /home/zoey/dev/worktrees/content-plan-v2-input --out . --check ``` -`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of the committed JSON and checks that the verifier rejects each one; it needs no content checkout. +`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of the committed JSON and checks that the verifier rejects each one; it needs no content checkout. `verify_plan.py` checks the JSON against itself; `--check` checks it against the content: it regenerates in a temporary directory and fails unless every artifact is byte-identical, so an edit no JSON-internal check can see (an exit dropped from `exit_inventory` together with its count, say) fails there. The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content checkout; rerunning with the same arguments on the same content rewrites `placement-plan.json`, `REPORT.md` and the PNGs byte-for-byte. `content_git_head`, `content_worktree_clean` and `content_inputs_digest` (SHA-256 over every file under `arch/` and `maps/`, checked again after the run) in the JSON identify the input. Scripts: `mapdata.py` (read-only reader, tile links, occupied tiles, floors per sub-layer, `no_pass` tiles), `analysis.py` (components, layout, exit resolution), `relayer.py` (the relayering: regions, units, conflicts, seams), `footprint.py` (gap rule, column and sloped stairwell chains), `build_plan.py` (classification, relayering, placement, stairwells, wiring, JSON, staged publication), `report.py`, `render.py` and `raster.py` (images), and `verify_plan.py` (independent check of the JSON). Tunables (`VIEW_RADIUS`, `CONTACT_MARGIN`, `MAX_EXTRA_LAYERS`, `MAX_SHIFT`, `STAIR_TOLERANCE`, `STAIRWELL_MAX_BEND`, `LAYER_HEIGHT_Z` in `build_plan.py`; `MIN_REGION_TILES`, `WALKABLE_DZ`, `SEAM_CELL` in `relayer.py`) and the hand-written inputs (system maps, owner decisions, the Rockforge mines alignment hint, version 1's placements for the comparison) are constants. diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 7f8212c..3d9b367 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -9,7 +9,10 @@ staging directory, verifies the staged plan, and only then replaces the artifacts in the output directory. Standard library only. - python3 -I scripts/build_plan.py [--content DIR] [--out DIR] [--allow-dirty] + python3 -I scripts/build_plan.py [--content DIR] [--out DIR] [--allow-dirty] [--check] + +--check regenerates in a temporary directory and fails unless every artifact +in --out is byte-identical; it writes nothing. Everything is deterministic: inputs are iterated in sorted order and every tie is broken lexicographically. @@ -2088,15 +2091,30 @@ def publish(stage: Path, out: Path) -> list: return new +def compare_artifacts(stage: Path, out: Path) -> list: + """Artifacts that differ between a fresh run in `stage` and the published set in `out`.""" + names = sorted({p.name for d in (stage, out) for p in d.iterdir() if ARTIFACT_RE.match(p.name)}) + return [n for n in names if not ((stage / n).is_file() and (out / n).is_file() + and (stage / n).read_bytes() == (out / n).read_bytes())] + + def main(argv=None): ap = argparse.ArgumentParser(description=__doc__) ap.add_argument("--content", type=Path, default=DEFAULT_CONTENT) ap.add_argument("--out", type=Path, default=DEFAULT_OUT) ap.add_argument("--allow-dirty", action="store_true", help="allow uncommitted changes in the content checkout (experiments only; recorded in the JSON)") + ap.add_argument("--check", action="store_true", + help="regenerate in a temporary directory and fail unless every artifact in --out is " + "byte-identical (binds the published plan, its exit inventory included, to the content; " + "use the --content path the plan records); writes nothing") args = ap.parse_args(argv) out = args.out.resolve() - out.mkdir(parents=True, exist_ok=True) + if not args.check: + out.mkdir(parents=True, exist_ok=True) + elif not out.is_dir(): + print("nothing to check: {} is not a directory".format(out)) + return 1 plan, maps, G, ow_id, ow_relay = build(args.content.resolve(), allow_dirty=args.allow_dirty) print(json.dumps(plan["summary"], indent=1)) if not plan["verification"]["ok"]: @@ -2105,7 +2123,7 @@ def main(argv=None): import render # noqa: E402 import report # noqa: E402 import verify_plan # noqa: E402 - with tempfile.TemporaryDirectory(prefix=".placement-plan-stage-", dir=out) as tmp: + with tempfile.TemporaryDirectory(prefix=".placement-plan-stage-", dir=None if args.check else out) as tmp: stage = Path(tmp) (stage / "placement-plan.json").write_text(json.dumps(plan, indent=1, sort_keys=False) + "\n", encoding="utf-8") @@ -2115,6 +2133,15 @@ def main(argv=None): if not staged["ok"] or staged != plan["verification"]: print("staged plan failed verification; nothing written:", staged["errors"][:10]) return 1 + if args.check: + differ = compare_artifacts(stage, out) + if differ: + print("check FAILED: {} artifact(s) in {} differ from a fresh run: {}".format( + len(differ), out, ", ".join(differ))) + return 1 + print("check: all {} artifacts in {} are byte-identical to a fresh run".format( + sum(1 for p in out.iterdir() if ARTIFACT_RE.match(p.name)), out)) + return 0 written = publish(stage, out) print("wrote {} artifacts to {}".format(len(written), out)) print("verification: OK") diff --git a/migration/placement-plan/scripts/report.py b/migration/placement-plan/scripts/report.py index 8bf94cb..77e0c45 100644 --- a/migration/placement-plan/scripts/report.py +++ b/migration/placement-plan/scripts/report.py @@ -1097,6 +1097,7 @@ def write_reproducing(plan, w): w("python3 -I scripts/build_plan.py --content {} --out .".format(plan["content_root"])) w("python3 -I scripts/verify_plan.py placement-plan.json") w("python3 -I scripts/test_placement_plan.py") + w("python3 -I scripts/build_plan.py --content {} --out . --check".format(plan["content_root"])) w("```") w("") w("`--content` must be a clean checkout of `atrinik/content` at `{}` (the shared checkout was behind `main`, " @@ -1106,7 +1107,10 @@ def write_reproducing(plan, w): "directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the " "PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced " "and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of " - "the committed JSON and checks that the verifier rejects each one; it needs no content checkout.".format( + "the committed JSON and checks that the verifier rejects each one; it needs no content checkout. " + "`verify_plan.py` checks the JSON against itself; `--check` checks it against the content: it regenerates in " + "a temporary directory and fails unless every artifact is byte-identical, so an edit no JSON-internal check " + "can see (an exit dropped from `exit_inventory` together with its count, say) fails there.".format( plan["content_git_head"][:12])) w("") w("The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content " diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 22466e6..44f960b 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -108,6 +108,11 @@ regions, units and final layers; placement_changes_from_v1, view_radius_sensitivity and conventions; and the tile-link, layout and connectivity data problems. +This check uses only the JSON, so it cannot see an edit that keeps the JSON +consistent with itself (an exit dropped from exit_inventory together with +summary.exit_objects, say). `build_plan.py --check` covers that: it +regenerates from the content and fails unless every artifact is +byte-identical. Exit status 0 when every check passes. """ From 3cdf8cdcb58d2dc436dd264c56d2e7034b83e99c Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 16:14:36 -0500 Subject: [PATCH 10/20] fix(placement-plan): enforce search limits and alignment references Address the ninth Codex review round on #47. - No placement may go further from its ideal spot than settings.max_extra_layers and settings.max_shift_tiles allow. - Every configured tentative alignment must name a placed group and a placed anchor, with a layer delta and a reason. - build_plan.py --check compares artifacts with filecmp (sizes first, then fixed-size chunks) instead of reading both whole files. The plan is unchanged; --check passes on the committed artifacts. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/scripts/build_plan.py | 8 ++++++-- .../placement-plan/scripts/test_placement_plan.py | 14 ++++++++++++++ migration/placement-plan/scripts/verify_plan.py | 15 ++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 3d9b367..5c37d56 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -22,6 +22,7 @@ import argparse import collections +import filecmp import functools import hashlib import json @@ -2092,10 +2093,13 @@ def publish(stage: Path, out: Path) -> list: def compare_artifacts(stage: Path, out: Path) -> list: - """Artifacts that differ between a fresh run in `stage` and the published set in `out`.""" + """Artifacts that differ between a fresh run in `stage` and the published set in `out`. + + Sizes are compared first and contents in fixed-size chunks (filecmp), so an + oversized file in `out` is a difference, not a whole-file read.""" names = sorted({p.name for d in (stage, out) for p in d.iterdir() if ARTIFACT_RE.match(p.name)}) return [n for n in names if not ((stage / n).is_file() and (out / n).is_file() - and (stage / n).read_bytes() == (out / n).read_bytes())] + and filecmp.cmp(stage / n, out / n, shallow=False))] def main(argv=None): diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 818238f..15c0713 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -580,6 +580,20 @@ def test_a_fabricated_alignment_hint(self): self.assertCaught(p, "alignment hint of {} is not a configured alignment".format( p["settings"]["tentative_alignments"][0]["group"])) + def test_search_limits(self): + p = self.plan() + p["settings"]["max_extra_layers"] = 0 + self.assertCaught(p, "placement of /shattered_islands/incuna/ellamin goes 4 layer(s)") + p = self.plan() + p["settings"]["max_shift_tiles"] = -1 + self.assertCaught(p, "past the search limits") + + def test_a_dangling_alignment(self): + p = self.plan() + p["settings"]["tentative_alignments"].append( + {"group": "/not/a/group", "anchor": "/also/missing", "layer_delta": 0, "why": "fabricated"}) + self.assertCaught(p, "tentative alignment of /not/a/group to /also/missing does not join two placed groups") + def test_place_group_without_a_placement(self): p = self.plan() group(p, self.GID).pop("placement") diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 44f960b..a7ea67c 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -20,7 +20,9 @@ an earlier placed group; only an alignment-hinted tentative placement has none (a proposed placement carries no hint), and its hint is one of settings.tentative_alignments: its ideal is the anchor's offsets, the - configured layers away + configured layers away; every configured alignment joins two placed groups + * no placement goes further from its ideal spot than max_extra_layers and + max_shift_tiles allow * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -874,6 +876,11 @@ def portal_reason(e, k, s, dist, dl): if a.get("group") in aligns: errors.append("tentative alignment of {} is configured twice".format(a.get("group"))) aligns[a.get("group")] = a + # a configured alignment names real groups and was applied: its anchor and its group are placed + if a.get("group") not in placed_by_id or a.get("anchor") not in placed_by_id or \ + not isinstance(a.get("layer_delta"), int) or not a.get("why"): + errors.append("tentative alignment of {} to {} does not join two placed groups".format( + a.get("group"), a.get("anchor"))) exits_checked = 0 for (src_map, line), e in sorted(records.items()): live = all(x == ow_id or x in placed_ids for x in (e["source_group"], e["target_group"])) @@ -1009,6 +1016,12 @@ def counted(es): ideal, orient = pl.get("ideal") or {}, pl.get("orientation") step = {"below": -1, "above": 1}.get(orient) sh, extra = pl.get("shift_from_ideal") or [None, None], pl.get("extra_layers_from_ideal") + if None not in sh and isinstance(extra, int) and ( + extra > st["max_extra_layers"] or max(abs(sh[0]), abs(sh[1])) > st["max_shift_tiles"]): + errors.append("placement of {} goes {} layer(s) and {} beyond its ideal spot, past the search limits " + "(max_extra_layers {}, max_shift_tiles {})".format(g["id"], extra, sh, + st["max_extra_layers"], + st["max_shift_tiles"])) if step is None or not isinstance(extra, int) or extra < 0 or None in (ideal.get("dx"), ideal.get("dy"), ideal.get("L"), *sh) or \ [pl["x_offset"], pl["y_offset"], pl["layer_offset"]] != [ From ea8907eb9c3f711dc28f7ca9c365bb8d446eb3d6 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 16:30:33 -0500 Subject: [PATCH 11/20] fix(placement-plan): require applied alignments and the 24-tile grid Address the tenth Codex review round on #47. - Every configured tentative alignment must be the hint of its group's tentative placement. A stale or made-up entry between ordinary groups fails. - settings.overworld_map_size must be 24, the grid every overworld coordinate uses, and every overworld rows grid must be 24 x 24. The bounds checks use the same constant. The plan is unchanged; --check passes on the committed artifacts. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 19 +++++++++++++- .../placement-plan/scripts/verify_plan.py | 25 +++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 15c0713..8202aba 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -592,7 +592,24 @@ def test_a_dangling_alignment(self): p = self.plan() p["settings"]["tentative_alignments"].append( {"group": "/not/a/group", "anchor": "/also/missing", "layer_delta": 0, "why": "fabricated"}) - self.assertCaught(p, "tentative alignment of /not/a/group to /also/missing does not join two placed groups") + self.assertCaught(p, "tentative alignment of /not/a/group to /also/missing is not the hint of a tentative " + "placement") + # an entry between two ordinary proposed groups is not applied anywhere + p = self.plan() + a, b = p["summary"]["placement_order"][:2] + p["settings"]["tentative_alignments"].append({"group": b, "anchor": a, "layer_delta": -1, "why": "stale"}) + self.assertCaught(p, "tentative alignment of {} to {} is not the hint of a tentative placement".format(b, a)) + + def test_the_overworld_grid(self): + p = self.plan() + p["settings"]["overworld_map_size"] = 25 + self.assertCaught(p, "settings.overworld_map_size 25 is not the 24-tile grid") + p = self.plan() + ow = next(g for g in p["groups"] if g["classification"] == "overworld") + path = sorted(ow["occupied_rows_by_layer"])[0] + layer = sorted(ow["occupied_rows_by_layer"][path])[0] + ow["occupied_rows_by_layer"][path][layer][0] += "." + self.assertCaught(p, "rows of {} on layer {} are not 24 x 24".format(path, layer)) def test_place_group_without_a_placement(self): p = self.plan() diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index a7ea67c..e268465 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -4,6 +4,7 @@ python3 -I scripts/verify_plan.py [placement-plan.json] Groups: + * overworld maps are 24 x 24 (settings.overworld_map_size, every rows grid) * one overworld group; group ids and member maps (overworld cells included) are unique; every group's classification and reason are known values; the summary's group, map and classification counts are re-derived @@ -20,7 +21,8 @@ an earlier placed group; only an alignment-hinted tentative placement has none (a proposed placement carries no hint), and its hint is one of settings.tentative_alignments: its ideal is the anchor's offsets, the - configured layers away; every configured alignment joins two placed groups + configured layers away; every configured alignment is applied (its + group's tentative placement carries it) * no placement goes further from its ideal spot than max_extra_layers and max_shift_tiles allow * member plane origin == local_offset + (x_offset, y_offset, layer_offset); @@ -128,6 +130,7 @@ from pathlib import Path WORLD_RE = re.compile(r"/world_(-?\d+)_(-?\d+)(?:_(-?\d+))?$") +OW_SIZE = 24 # overworld maps are 24 x 24; plane tile = 24 * filename coordinate + tile N8 = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1) if (dx, dy) != (0, 0)] # Fields of an exit record that come from the content: equal to its inventory entry. SOURCE_FIELDS = ("source_map", "source_tile", "target_map", "target_tile", "arch", "line", "form", "vertical", @@ -247,9 +250,14 @@ def put(owner, mapname, layer, x0, y0, rows): rr.add(*t) ow_rows = ow["occupied_rows_by_layer"] + if st.get("overworld_map_size") != OW_SIZE: + errors.append("settings.overworld_map_size {} is not the {}-tile grid of the overworld coordinates".format( + st.get("overworld_map_size"), OW_SIZE)) for path, by_layer in sorted(ow_rows.items()): X, Y, Z = world_cell(path) for Ls, rows in sorted(by_layer.items(), key=lambda kv: int(kv[0])): + if len(rows) != OW_SIZE or any(len(r) != OW_SIZE for r in rows): + errors.append("rows of {} on layer {} are not {} x {}".format(path, Ls, OW_SIZE, OW_SIZE)) put(path, path, int(Ls), 24 * X, 24 * Y, rows) for y, row in enumerate(rows): for x, ch in enumerate(row): @@ -607,11 +615,11 @@ def owner_of_end(e, side): base.update(severity="info", note="explicitly disabled with last_heal 0 (decorative)") else: base["severity"] = "problem" - w, h = size.get(x["source_map"], (st["overworld_map_size"],) * 2) + w, h = size.get(x["source_map"], (OW_SIZE,) * 2) if not (0 <= tile[0] < w and 0 <= tile[1] < h): want_dp.append(dict(base, kind="exit-outside-map-bounds")) if "target_map" in x: # the reader resolves a target onto a tile of its map - tw, th = size.get(x["target_map"], (st["overworld_map_size"],) * 2) + tw, th = size.get(x["target_map"], (OW_SIZE,) * 2) if not (0 <= x["target_tile"][0] < tw and 0 <= x["target_tile"][1] < th): errors.append("exit {}:{} targets tile {} outside {} ({} x {})".format( k[0], k[1], x["target_tile"], x["target_map"], tw, th)) @@ -876,10 +884,13 @@ def portal_reason(e, k, s, dist, dl): if a.get("group") in aligns: errors.append("tentative alignment of {} is configured twice".format(a.get("group"))) aligns[a.get("group")] = a - # a configured alignment names real groups and was applied: its anchor and its group are placed - if a.get("group") not in placed_by_id or a.get("anchor") not in placed_by_id or \ - not isinstance(a.get("layer_delta"), int) or not a.get("why"): - errors.append("tentative alignment of {} to {} does not join two placed groups".format( + # a configured alignment names real groups and was applied: its group's tentative placement carries + # it (checked against the anchor below), so a stale or made-up entry fails + g_ = placed_by_id.get(a.get("group")) + if g_ is None or a.get("anchor") not in placed_by_id or not isinstance(a.get("layer_delta"), int) or \ + not a.get("why") or g_["placement"].get("status") != "tentative" or \ + g_["placement"].get("tentative_reason") != a["why"]: + errors.append("tentative alignment of {} to {} is not the hint of a tentative placement".format( a.get("group"), a.get("anchor"))) exits_checked = 0 for (src_map, line), e in sorted(records.items()): From 5f5d85f5bb02a091a6bc47834f96fa99defc66b6 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 16:54:38 -0500 Subject: [PATCH 12/20] fix(placement-plan): bound wired targets, slots, reasons and --check Address the eleventh Codex review round on #47. - The target bounds check applies to the target after wiring, so a wired exit's destination must be a tile of its map too. - tiled_slot is present exactly on tiled exits without a link, with a valid slot (1-10). Wired exits go up or down (9 or 10). - A placement object must carry a status. Alignment reasons (settings.tentative_alignments why and the placement's tentative_reason) must be text. - --check stops at 256 artifact names and lists at most 10 differences. - The view-radius sensitivity run counts an alignment group whose anchor is unplaced as unplaced. The plan is unchanged; --check passes on the committed artifacts. Co-Authored-By: Claude Opus 5.5 --- .../placement-plan/scripts/build_plan.py | 40 +++++++++++++------ .../scripts/test_placement_plan.py | 36 +++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 27 +++++++++---- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 5c37d56..04e0e06 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -1241,8 +1241,10 @@ def run_placement(radius): build_relayer_stairwells() o1, f1, l1 = run_pool(to_place, False) o2, f2, l2 = run_pool(tentative_ids, True) - o3, f3 = [], [] + o3, f3, s3 = [], [], [] for gid, (anchor, dl, why) in sorted(TENTATIVE_ALIGN.items()): + if gid in G and gid not in S.placements and anchor not in S.placed: + s3.append(gid) # its anchor is not placed: the group stays unplaced # only for groups no pool tried: a group whose own placement failed stays failed if gid in G and gid not in S.placements and anchor in S.placed: a = S.placed[anchor] @@ -1282,7 +1284,7 @@ def run_placement(radius): sw["id"] = "{}{}".format(prefix[sw["kind"]], n[sw["kind"]]) # A failed group stays unplaced (and fails verification, so the plan is not published). # an uncertain group no pool reached may still get its tentative spot from an alignment hint - return o1, sorted(l1 + f1), o2 + o3, sorted((set(l2) - set(o3)) | set(f2)), sorted(f1 + f2 + f3) + return o1, sorted(l1 + f1), o2 + o3, sorted((set(l2) - set(o3)) | set(f2)), sorted(f1 + f2 + f3), s3 def becomes_counts(): c = collections.Counter() @@ -1367,7 +1369,7 @@ def exit_record(e, with_eval=True): return r # Sensitivity: the same placement with the shipped client's view radius. - _o, sens_left1, _t, sens_left2, sens_failed = run_placement(CLIENT_VIEW_RADIUS) + _o, sens_left1, _t, sens_left2, sens_failed, sens_skipped = run_placement(CLIENT_VIEW_RADIUS) sens_placed = {gid: dict(p) for gid, p in S.placed.items()} sensitivity = { "view_radius_tiles": CLIENT_VIEW_RADIUS, @@ -1376,9 +1378,10 @@ def exit_record(e, with_eval=True): "crossing_exit_objects_after_placement": dict(sorted(becomes_counts().items())), "stairwells": len(S.stairwells), "stairwell_sites_blocked": len(S.blocked_sites), - "groups_unplaced": sorted(set(sens_left1) | set(sens_left2) | set(sens_failed)), + "groups_unplaced": sorted(set(sens_left1) | set(sens_left2) | set(sens_failed) | set(sens_skipped)), } - order1, left1, order_t, left2, failed_placements = run_placement(VIEW_RADIUS) + # an alignment group skipped for want of its anchor fails verification (its hint is not applied) + order1, left1, order_t, left2, failed_placements, _skipped = run_placement(VIEW_RADIUS) place_index = {gid: i for i, gid in enumerate(S.order)} placements = S.placements sensitivity["groups_placed_differently"] = sorted( @@ -2092,14 +2095,24 @@ def publish(stage: Path, out: Path) -> list: return new -def compare_artifacts(stage: Path, out: Path) -> list: - """Artifacts that differ between a fresh run in `stage` and the published set in `out`. +MAX_CHECK_ARTIFACTS = 256 # a published set has a few dozen artifacts; more means --out is not one + + +def compare_artifacts(stage: Path, out: Path): + """Artifacts that differ between a fresh run in `stage` and the published set in `out`, or None if + `out` holds more than MAX_CHECK_ARTIFACTS artifact names. Sizes are compared first and contents in fixed-size chunks (filecmp), so an oversized file in `out` is a difference, not a whole-file read.""" - names = sorted({p.name for d in (stage, out) for p in d.iterdir() if ARTIFACT_RE.match(p.name)}) - return [n for n in names if not ((stage / n).is_file() and (out / n).is_file() - and filecmp.cmp(stage / n, out / n, shallow=False))] + names = set() + for d in (stage, out): + for p in d.iterdir(): + if ARTIFACT_RE.match(p.name): + names.add(p.name) + if len(names) > MAX_CHECK_ARTIFACTS: + return None + return [n for n in sorted(names) if not ((stage / n).is_file() and (out / n).is_file() + and filecmp.cmp(stage / n, out / n, shallow=False))] def main(argv=None): @@ -2139,9 +2152,12 @@ def main(argv=None): return 1 if args.check: differ = compare_artifacts(stage, out) + if differ is None: + print("check FAILED: {} holds more than {} artifact names".format(out, MAX_CHECK_ARTIFACTS)) + return 1 if differ: - print("check FAILED: {} artifact(s) in {} differ from a fresh run: {}".format( - len(differ), out, ", ".join(differ))) + print("check FAILED: {} artifact(s) in {} differ from a fresh run: {}{}".format( + len(differ), out, ", ".join(differ[:10]), ", ..." if len(differ) > 10 else "")) return 1 print("check: all {} artifacts in {} are byte-identical to a fresh run".format( sum(1 for p in out.iterdir() if ARTIFACT_RE.match(p.name)), out)) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 8202aba..18d647b 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -369,6 +369,29 @@ def test_unknown_source_values(self): self.assertCaught(p, "exit {}:{} has form unresolved, direction {} and a target".format( x["source_map"], x["line"], x["vertical"])) + def test_a_wired_target_outside_its_map(self): + p = self.plan() + key = (MINE + "mine_entrance", 181) + d = next(d for d in p["data_problems"]["dangling_exits_wired"] if (d["source_map"], d["line"]) == key) + self.assertEqual((d["target_map"], d["target_tile"]), ("/shattered_islands/world_8_79", [8, 1])) + d.update(target_map="/shattered_islands/world_7_79", target_tile=[32, 1]) + for _g, _lst, e in copies(p, key): + e.update(target_map="/shattered_islands/world_7_79", target_tile=[32, 1]) + self.assertCaught(p, "exit {}:{} targets tile [32, 1] outside /shattered_islands/world_7_79".format(*key)) + + def test_an_invalid_tile_link_slot(self): + p = self.plan() + x = next(x for x in p["exit_inventory"] if x["form"] == "tiled-unresolved") + x["tiled_slot"] = 999 + for d in p["data_problems"]["exits"]: + if (d["source_map"], d["line"]) == (x["source_map"], x["line"]) and d["kind"] == "tiled-exit-without-link": + d["detail"] = "last_heal 999 (None) but the map has no such tile link; the exit leads nowhere" + self.assertCaught(p, "exit {}:{} has form tiled-unresolved and tile-link slot 999".format( + x["source_map"], x["line"])) + p = self.plan() + next(x for x in p["exit_inventory"] if x["form"] == "explicit")["tiled_slot"] = 9 + self.assertCaught(p, "has form explicit and tile-link slot 9") + def test_a_target_outside_its_map(self): p = self.plan() key = ("/shattered_islands/ships/ship_1", 122) @@ -611,6 +634,19 @@ def test_the_overworld_grid(self): ow["occupied_rows_by_layer"][path][layer][0] += "." self.assertCaught(p, "rows of {} on layer {} are not 24 x 24".format(path, layer)) + def test_a_placement_without_status(self): + p = self.plan() + group(p, "/emergency")["placement"] = {"status": None, "x_offset": 123} + self.assertCaught(p, "placement of /emergency has no status") + + def test_alignment_reasons_are_text(self): + p = self.plan() + a = p["settings"]["tentative_alignments"][0] + a["why"] = ["x"] + group(p, a["group"])["placement"]["tentative_reason"] = ["x"] + self.assertCaught(p, "tentative_reason of {} is not a text".format(a["group"]), + "tentative alignment of {} to {} is not the hint".format(a["group"], a["anchor"])) + def test_place_group_without_a_placement(self): p = self.plan() group(p, self.GID).pop("placement") diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index e268465..61e8333 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -320,6 +320,8 @@ def put(owner, mapname, layer, x0, y0, rows): status = {g["id"]: (g.get("placement") or {}).get("status") for g in others} for g in others: gid, s = g["id"], status[g["id"]] + if g.get("placement") is not None and s is None: + errors.append("placement of {} has no status".format(gid)) if s == "failed": errors.append("placement of {} failed; a plan with a failed group must not be published".format(gid)) elif s not in (None, "proposed", "tentative"): @@ -512,6 +514,11 @@ def owner_of_end(e, side): if k in inv: errors.append("exit {}:{} is listed twice in the exit inventory".format(*k)) inv[k] = x + # the tile-link slot of a tiled exit without a link: 1..10, and only on that form; wired ones go up or down + if ("tiled_slot" in x) != (x.get("form") == "tiled-unresolved") or "tiled_slot" in x and \ + x["tiled_slot"] not in SLOT_NAMES: + errors.append("exit {}:{} has form {} and tile-link slot {}".format(k[0], k[1], x.get("form"), + x.get("tiled_slot"))) # a known form and direction; a target exactly when the form resolves one and the map exists if x.get("form") not in TARGET_FORMS | NO_TARGET_FORMS or x.get("vertical") not in ("up", "down", "none") or \ ("target_map" in x) != ("target_tile" in x) or \ @@ -532,12 +539,13 @@ def owner_of_end(e, side): if len(plan["data_problems"]["dangling_exits_wired"]) != summ["wired_exits"]: errors.append("{} wired exits listed, the summary says {}".format( len(plan["data_problems"]["dangling_exits_wired"]), summ["wired_exits"])) + size = {m["path"]: (m["width"], m["height"]) for g in others for m in g["maps"]} expected_copies = set() want_rec = {} # exit key -> the record fields the inventory (and wiring) fix for k, x in sorted(inv.items()): want = {f: x.get(f) for f in SOURCE_FIELDS} if k in wired_dp: - if x["form"] != "tiled-unresolved" or "target_map" in x: + if x["form"] != "tiled-unresolved" or "target_map" in x or x.get("tiled_slot") not in (9, 10): errors.append("wired exit {}:{} is not a legacy exit that leads nowhere".format(*k)) want.update(form="wired", target_map=wired_dp[k].get("target_map"), target_tile=wired_dp[k].get("target_tile")) @@ -551,6 +559,13 @@ def owner_of_end(e, side): if tg is None: errors.append("exit {}:{} leads to {}, which is in no group".format(k[0], k[1], want["target_map"])) continue + # a target (resolved by the reader, or wired) is a tile of its map + tw, th = size.get(want["target_map"], (OW_SIZE,) * 2) + tt = want.get("target_tile") + if not (isinstance(tt, list) and len(tt) == 2 and all(isinstance(c, int) for c in tt) + and 0 <= tt[0] < tw and 0 <= tt[1] < th): + errors.append("exit {}:{} targets tile {} outside {} ({} x {})".format( + k[0], k[1], tt, want["target_map"], tw, th)) want.update(source_group=sg, target_group=tg) want_rec[k] = want if sg == tg: @@ -601,7 +616,6 @@ def owner_of_end(e, side): errors.append("{} exit {}:{} is not a legacy exit that leads nowhere".format( what, d["source_map"], d["line"])) # the exit data problems, re-derived entry by entry from the inventory - size = {m["path"]: (m["width"], m["height"]) for g in others for m in g["maps"]} working = {(x["source_map"], tuple(x["source_tile"])) for x in inv.values() if "target_map" in x} want_dp = [] for k, x in sorted(inv.items()): @@ -618,11 +632,6 @@ def owner_of_end(e, side): w, h = size.get(x["source_map"], (OW_SIZE,) * 2) if not (0 <= tile[0] < w and 0 <= tile[1] < h): want_dp.append(dict(base, kind="exit-outside-map-bounds")) - if "target_map" in x: # the reader resolves a target onto a tile of its map - tw, th = size.get(x["target_map"], (OW_SIZE,) * 2) - if not (0 <= x["target_tile"][0] < tw and 0 <= x["target_tile"][1] < th): - errors.append("exit {}:{} targets tile {} outside {} ({} x {})".format( - k[0], k[1], x["target_tile"], x["target_map"], tw, th)) if form == "tiled-unresolved": want_dp.append(dict(base, kind="tiled-exit-without-link", detail="last_heal {} ({}) but the map has no " "such tile link; the exit leads nowhere".format(x.get("tiled_slot"), @@ -888,7 +897,7 @@ def portal_reason(e, k, s, dist, dl): # it (checked against the anchor below), so a stale or made-up entry fails g_ = placed_by_id.get(a.get("group")) if g_ is None or a.get("anchor") not in placed_by_id or not isinstance(a.get("layer_delta"), int) or \ - not a.get("why") or g_["placement"].get("status") != "tentative" or \ + not isinstance(a.get("why"), str) or not a["why"] or g_["placement"].get("status") != "tentative" or \ g_["placement"].get("tentative_reason") != a["why"]: errors.append("tentative alignment of {} to {} is not the hint of a tentative placement".format( a.get("group"), a.get("anchor"))) @@ -1044,6 +1053,8 @@ def counted(es): # only a placement forced by an alignment hint has none mc = pl.get("main_connection") hinted = bool(pl.get("tentative_reason")) + if "tentative_reason" in pl and not (isinstance(pl["tentative_reason"], str) and pl["tentative_reason"]): + errors.append("tentative_reason of {} is not a text".format(g["id"])) if hinted and pl.get("status") != "tentative": errors.append("{} placement of {} carries an alignment hint".format(pl.get("status"), g["id"])) hinted = False From 64dde928fb456f28267456060baca7f2f3b19c2e Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 17:18:36 -0500 Subject: [PATCH 13/20] fix(placement-plan): type-check the inventory and bound --check entries Address the twelfth Codex review round on #47. - Exit inventory values must have the right types: map paths and archetypes are text, lines and tiles integers (two per tile), flags true, and event scripts a list of text. A resolving form's target can no longer be null, and a source tile can no longer be fractional. Records equal their inventory entry, so this bounds their content fields too. - A dynamic exit must carry its dynamic_target, and a target problem its problem_target. - A relayering blocked site may list only overworld exits. - --check gives up after 4096 directory entries in --out, whatever their names. The plan is unchanged; --check passes on the committed artifacts. Co-Authored-By: Claude Opus 5.5 --- .../placement-plan/scripts/build_plan.py | 15 +++---- .../scripts/test_placement_plan.py | 40 +++++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 33 ++++++++++++--- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 04e0e06..007f2cf 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -2095,22 +2095,23 @@ def publish(stage: Path, out: Path) -> list: return new -MAX_CHECK_ARTIFACTS = 256 # a published set has a few dozen artifacts; more means --out is not one +MAX_CHECK_ENTRIES = 4096 # the output directory holds a few dozen artifacts and the scripts' folder def compare_artifacts(stage: Path, out: Path): """Artifacts that differ between a fresh run in `stage` and the published set in `out`, or None if - `out` holds more than MAX_CHECK_ARTIFACTS artifact names. + `out` has more than MAX_CHECK_ENTRIES entries (it is then not a plan's output directory). Sizes are compared first and contents in fixed-size chunks (filecmp), so an oversized file in `out` is a difference, not a whole-file read.""" names = set() for d in (stage, out): - for p in d.iterdir(): - if ARTIFACT_RE.match(p.name): - names.add(p.name) - if len(names) > MAX_CHECK_ARTIFACTS: + with os.scandir(d) as it: + for n_, entry in enumerate(it): + if n_ >= MAX_CHECK_ENTRIES: return None + if ARTIFACT_RE.match(entry.name): + names.add(entry.name) return [n for n in sorted(names) if not ((stage / n).is_file() and (out / n).is_file() and filecmp.cmp(stage / n, out / n, shallow=False))] @@ -2153,7 +2154,7 @@ def main(argv=None): if args.check: differ = compare_artifacts(stage, out) if differ is None: - print("check FAILED: {} holds more than {} artifact names".format(out, MAX_CHECK_ARTIFACTS)) + print("check FAILED: {} has more than {} entries".format(out, MAX_CHECK_ENTRIES)) return 1 if differ: print("check FAILED: {} artifact(s) in {} differ from a fresh run: {}{}".format( diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 18d647b..1d500c5 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -369,6 +369,31 @@ def test_unknown_source_values(self): self.assertCaught(p, "exit {}:{} has form unresolved, direction {} and a target".format( x["source_map"], x["line"], x["vertical"])) + def test_value_types(self): + for field, value in (("arch", 7), ("source_tile", [1.5, 2]), ("per_player_last_eat_1", "yes"), + ("event_scripts", "script.py"), ("vertical_inferred_from", 3)): + with self.subTest(field=field): + p = self.plan() + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == CROSSING)[field] = value + for _g, _lst, e in copies(p, CROSSING): + e[field] = value + self.assertCaught(p, "exit {!r}:{!r} has a value of the wrong type".format(*CROSSING)) + + def test_a_same_group_exit_with_bad_values(self): + key = ("/planes/creation/creation_0501", 427) + for change in ({"target_map": None, "target_tile": None}, {"source_tile": [2.5, 1]}): + with self.subTest(change=change): + p = self.plan() + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == key).update(change) + self.assertCaught(p, "exit {!r}:{!r} has a value of the wrong type".format(*key)) + + def test_a_dynamic_exit_without_its_target(self): + key = ("/shattered_islands/world_18_4", 1958) + p = self.plan() + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == key).pop("dynamic_target") + next(d for d in p["data_problems"]["exits"] if (d["source_map"], d["line"]) == key)["detail"] = None + self.assertCaught(p, "exit {!r}:{!r} has a value of the wrong type".format(*key)) + def test_a_wired_target_outside_its_map(self): p = self.plan() key = (MINE + "mine_entrance", 181) @@ -514,6 +539,21 @@ def test_other_copies(self): p["relayering"]["exits"][0]["crosses_to_group"] = "/emergency" self.assertCaught(p, "crosses to /emergency") + def test_a_placement_site_moved_to_the_relayering_sites(self): + p = self.plan() + site = p["stairwell_sites_blocked"].pop(0) + x = site["exits"][0] + r = copies(p, (x["source_map"], x["line"]))[0][2] + up_src = r["plane_source"][2] > r["plane_target"][2] + top, bottom = (r["source_map"], r["target_map"]) if up_src else (r["target_map"], r["source_map"]) + p["relayering"]["stairwells_blocked"].append( + {"top_map": top, "bottom_map": bottom, "top": site["top"], "bottom": site["bottom"], + "shape": site["shape"], "exits": site["exits"]}) + p["summary"]["stairwell_sites_blocked"] -= 1 + p["summary"]["relayering_stairwell_sites_blocked"] += 1 + self.assertCaught(p, "blocked relayering site lists exit {}:{}, which is not an overworld exit".format( + x["source_map"], x["line"])) + def test_blocked_site_owners(self): p = self.plan() p["stairwell_sites_blocked"][0]["other"] = "/shattered_islands/world_0_0" diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 61e8333..258f1ca 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -58,8 +58,9 @@ decoration (the exits' stair family) are re-derived; so are the summary's stairwell and blocked-site counts Exits: - * exit_inventory lists every exit object of the content once, with a - known form and direction and a target exactly when its form resolves one, + * exit_inventory lists every exit object of the content once, with values + of the right types, a known form and direction, a target exactly when its + form resolves one, the data-problem facts its form or problem needs, each resolved target on a tile of its target map; the exit records are exactly the copies it calls for (one per group a crossing exit touches, the overworld's own exits in internal_exits; a wired exit as @@ -71,9 +72,9 @@ * every other copy of an exit (blocked sites, relayering exits, wired and kept dangling exits, data problems, main connections) equals its record or inventory entry; a blocked site lists at least one exit, only exits - that need a stairwell, between its own two owners (or top and bottom - maps), and has the shape of its ends; a kept decoration sits at its tile - on the plane; + that need a stairwell, between its own two owners (a relayering site: + overworld exits between its top and bottom maps), and has the shape of + its ends; a kept decoration sits at its tile on the plane; every placement has a main connection to something placed before it, except one forced by an alignment hint * the records of one exit (one per group it touches) are identical; an @@ -514,6 +515,25 @@ def owner_of_end(e, side): if k in inv: errors.append("exit {}:{} is listed twice in the exit inventory".format(*k)) inv[k] = x + # value types: map paths and archetypes are text, lines and tiles integers, flags true when present + def tile_ok(t_): + return isinstance(t_, list) and len(t_) == 2 and all(type(c) is int for c in t_) + + if not (isinstance(x["source_map"], str) and type(x["line"]) is int and tile_ok(x.get("source_tile")) + and isinstance(x.get("arch"), str) and x["arch"] + and ("target_map" not in x or isinstance(x["target_map"], str) and tile_ok(x.get("target_tile"))) + and all(x.get(f, True) is True for f in ("target_is_map_default_entry", "per_player_last_eat_1", + "explicit_last_heal_0")) + and ("event_scripts" not in x or isinstance(x["event_scripts"], list) and x["event_scripts"] + and all(isinstance(v, str) for v in x["event_scripts"])) + and all(f not in x or isinstance(x[f], str) and x[f] for f in ("vertical_inferred_from", "problem", + "dynamic_target")) + # the facts behind data problems are there exactly for their form or problem + and ("dynamic_target" in x) == (x.get("form") == "dynamic") + and ("problem_target" in x) == ("problem" in x) + and ("problem_target" not in x or isinstance(x["problem_target"], list) + and len(x["problem_target"]) == 3)): + errors.append("exit {!r}:{!r} has a value of the wrong type".format(*k)) # the tile-link slot of a tiled exit without a link: 1..10, and only on that form; wired ones go up or down if ("tiled_slot" in x) != (x.get("form") == "tiled-unresolved") or "tiled_slot" in x and \ x["tiled_slot"] not in SLOT_NAMES: @@ -851,6 +871,9 @@ def needs_stairwell(e): if "plane_source" not in r or "plane_target" not in r or not needs_stairwell(r): errors.append("blocked {} stairwell site lists exit {}:{}, which needs no stairwell".format( key_, *k)) + elif key_ == "relayering" and not r["source_group"] == r["target_group"] == ow_id: + errors.append("blocked relayering site lists exit {}:{}, which is not an overworld " + "exit".format(*k)) elif key_ == "relayering": up_src = r["plane_source"][2] > r["plane_target"][2] ends_ = [r["source_map"], r["target_map"]] if up_src else [r["target_map"], r["source_map"]] From 63a5fd6cb723a7de389d8c249c765107700181fb Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 17:54:33 -0500 Subject: [PATCH 14/20] feat(placement-plan): verify the plan against a rebuild from the content Address the twelfth Codex review round on #47 by changing the design rather than patching one more copy check. Round after round found an edit that keeps the JSON consistent with itself. A JSON-only check cannot rule those out, so the verifier now has two checks with separate jobs: - verify(plan), as before: re-derives the placement rules from the JSON alone (tests and CI). - verify_plan.py --content DIR (verify_content): rebuilds the plan from the content checkout at content_git_head with the generator, and requires the published JSON to equal the rebuild as canonical JSON text (so true is not 1), content_root aside. Every copied field, source fact, inventory entry and count is then compared with what the content produces. It works from any checkout path. The JSON-only copy checks stay as defence in depth. Fidelity is --content's job. ContentFidelity tests run Codex's self-consistent edits against it when PLACEMENT_PLAN_CONTENT names a checkout, and skip otherwise. Rule fixes from the same round: - Booleans are rejected as wired target coordinates. - A placement's layer offset must lie within its vertical bounds. - A blocked site's ends must be 2+ layers and at most one tile per layer apart. REPORT.md's Reproducing section describes both checks. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/REPORT.md | 4 +- .../__pycache__/build_plan.cpython-314.pyc | Bin 0 -> 183784 bytes migration/placement-plan/scripts/report.py | 11 +- .../scripts/test_placement_plan.py | 70 +++++++++- .../placement-plan/scripts/verify_plan.py | 123 ++++++++++++++++-- 5 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc diff --git a/migration/placement-plan/REPORT.md b/migration/placement-plan/REPORT.md index 5dda199..19fa525 100644 --- a/migration/placement-plan/REPORT.md +++ b/migration/placement-plan/REPORT.md @@ -881,12 +881,12 @@ Each image covers its regions and every storey or cave unit that moved because o ```sh cd migration/placement-plan python3 -I scripts/build_plan.py --content /home/zoey/dev/worktrees/content-plan-v2-input --out . -python3 -I scripts/verify_plan.py placement-plan.json +python3 -I scripts/verify_plan.py placement-plan.json --content /home/zoey/dev/worktrees/content-plan-v2-input python3 -I scripts/test_placement_plan.py python3 -I scripts/build_plan.py --content /home/zoey/dev/worktrees/content-plan-v2-input --out . --check ``` -`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of the committed JSON and checks that the verifier rejects each one; it needs no content checkout. `verify_plan.py` checks the JSON against itself; `--check` checks it against the content: it regenerates in a temporary directory and fails unless every artifact is byte-identical, so an edit no JSON-internal check can see (an exit dropped from `exit_inventory` together with its count, say) fails there. +`--content` must be a clean checkout of `atrinik/content` at `3ad20150da76` (the shared checkout was behind `main`, so this run used a separate clone at the current `main`). The scripts need only the Python standard library (the PNGs are written by `raster.py`). The content checkout must be clean under `arch/` and `maps/` (`--allow-dirty` is for experiments and marks the JSON). Artifacts are generated in a staging directory, the staged JSON is verified again, and only then are `placement-plan.json`, `REPORT.md` and the PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of the committed JSON and checks that the verifier rejects each one; it needs no content checkout. `verify_plan.py` makes two checks. Without `--content` it re-derives the placement rules from the JSON alone. With `--content` it also rebuilds the plan from the content with the generator and requires the published JSON to equal the rebuild field for field (the checkout path aside), so an edit that keeps the JSON consistent with itself (an exit dropped from `exit_inventory` together with its count, say) fails there. `build_plan.py --check` regenerates every artifact in a temporary directory and fails unless each is byte-identical (run it with the `--content` path the plan records). The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content checkout; rerunning with the same arguments on the same content rewrites `placement-plan.json`, `REPORT.md` and the PNGs byte-for-byte. `content_git_head`, `content_worktree_clean` and `content_inputs_digest` (SHA-256 over every file under `arch/` and `maps/`, checked again after the run) in the JSON identify the input. Scripts: `mapdata.py` (read-only reader, tile links, occupied tiles, floors per sub-layer, `no_pass` tiles), `analysis.py` (components, layout, exit resolution), `relayer.py` (the relayering: regions, units, conflicts, seams), `footprint.py` (gap rule, column and sloped stairwell chains), `build_plan.py` (classification, relayering, placement, stairwells, wiring, JSON, staged publication), `report.py`, `render.py` and `raster.py` (images), and `verify_plan.py` (independent check of the JSON). Tunables (`VIEW_RADIUS`, `CONTACT_MARGIN`, `MAX_EXTRA_LAYERS`, `MAX_SHIFT`, `STAIR_TOLERANCE`, `STAIRWELL_MAX_BEND`, `LAYER_HEIGHT_Z` in `build_plan.py`; `MIN_REGION_TILES`, `WALKABLE_DZ`, `SEAM_CELL` in `relayer.py`) and the hand-written inputs (system maps, owner decisions, the Rockforge mines alignment hint, version 1's placements for the comparison) are constants. diff --git a/migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc b/migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0321748f761b3d25b1b69e6057f7fb8999205451 GIT binary patch literal 183784 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
7PfZ`3b=lCY%>%PGpQ;?3Rd#MlS@#AAU-#bl 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 Jq{_|x{{w3p3Y!1` literal 0 HcmV?d00001 diff --git a/migration/placement-plan/scripts/report.py b/migration/placement-plan/scripts/report.py index 77e0c45..d165421 100644 --- a/migration/placement-plan/scripts/report.py +++ b/migration/placement-plan/scripts/report.py @@ -1095,7 +1095,7 @@ def write_reproducing(plan, w): w("```sh") w("cd migration/placement-plan") w("python3 -I scripts/build_plan.py --content {} --out .".format(plan["content_root"])) - w("python3 -I scripts/verify_plan.py placement-plan.json") + w("python3 -I scripts/verify_plan.py placement-plan.json --content {}".format(plan["content_root"])) w("python3 -I scripts/test_placement_plan.py") w("python3 -I scripts/build_plan.py --content {} --out . --check".format(plan["content_root"])) w("```") @@ -1108,9 +1108,12 @@ def write_reproducing(plan, w): "PNGs replaced; a failed run leaves the old artifacts in place. A group that cannot be placed stays unplaced " "and fails verification, so such a plan is never published. `test_placement_plan.py` tampers with copies of " "the committed JSON and checks that the verifier rejects each one; it needs no content checkout. " - "`verify_plan.py` checks the JSON against itself; `--check` checks it against the content: it regenerates in " - "a temporary directory and fails unless every artifact is byte-identical, so an edit no JSON-internal check " - "can see (an exit dropped from `exit_inventory` together with its count, say) fails there.".format( + "`verify_plan.py` makes two checks. Without `--content` it re-derives the placement rules from the JSON " + "alone. With `--content` it also rebuilds the plan from the content with the generator and requires the " + "published JSON to equal the rebuild field for field (the checkout path aside), so an edit that keeps the " + "JSON consistent with itself (an exit dropped from `exit_inventory` together with its count, say) fails " + "there. `build_plan.py --check` regenerates every artifact in a temporary directory and fails unless each " + "is byte-identical (run it with the `--content` path the plan records).".format( plan["content_git_head"][:12])) w("") w("The run is deterministic (sorted inputs, lexicographic tie-breaks) and read-only against the content " diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 1d500c5..f15b0b7 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -2,16 +2,21 @@ """Negative tests for the placement-plan verifier and generator (standard library only). python3 -I scripts/test_placement_plan.py + PLACEMENT_PLAN_CONTENT=DIR python3 -I scripts/test_placement_plan.py Each verifier test tampers with a copy of the committed placement-plan.json and checks that verify_plan.verify() rejects it with the expected error. The -generator tests drive build_plan.place_pool() with stand-in callbacks, so they -need no content checkout. +generator tests drive build_plan.place_pool() with stand-in callbacks. Neither +needs a content checkout. With PLACEMENT_PLAN_CONTENT naming a checkout at the +plan's content_git_head, ContentFidelity also checks that verify_content() +rejects edits that keep the JSON consistent with itself; without it, those +tests are skipped. """ from __future__ import annotations import json +import os import sys import unittest from pathlib import Path @@ -404,6 +409,15 @@ def test_a_wired_target_outside_its_map(self): e.update(target_map="/shattered_islands/world_7_79", target_tile=[32, 1]) self.assertCaught(p, "exit {}:{} targets tile [32, 1] outside /shattered_islands/world_7_79".format(*key)) + def test_a_boolean_wired_coordinate(self): + p = self.plan() + key = (MINE + "mine_entrance", 181) + next(d for d in p["data_problems"]["dangling_exits_wired"] if (d["source_map"], d["line"]) == key)[ + "target_tile"] = [8, True] + for _g, _lst, e in copies(p, key): + e["target_tile"] = [8, True] + self.assertCaught(p, "exit {}:{} targets tile [8, True] outside".format(*key)) + def test_an_invalid_tile_link_slot(self): p = self.plan() x = next(x for x in p["exit_inventory"] if x["form"] == "tiled-unresolved") @@ -509,6 +523,13 @@ def test_series_bend_and_counts(self): p["summary"]["stairwells_by_kind"]["placement"] += 1 self.assertCaught(p, "summary stairwell and blocked-site counts differ from the lists") + def test_an_unreachable_blocked_slope(self): + p = self.plan() + site = p["sloped_stairwell_sites_blocked"][0] + self.assertEqual((site["top"], site["bottom"]), ([198, 1895, 0], [194, 1895, -4])) + site["top"], site["bottom"] = [199, 1895, 0], [193, 1895, -4] + self.assertCaught(p, "blocked sloped stairwell site [199, 1895, 0] -> [193, 1895, -4] is no stairwell site") + def test_an_empty_blocked_site(self): for key in ("stairwell_sites_blocked", "sloped_stairwell_sites_blocked"): with self.subTest(key=key): @@ -643,6 +664,14 @@ def test_a_fabricated_alignment_hint(self): self.assertCaught(p, "alignment hint of {} is not a configured alignment".format( p["settings"]["tentative_alignments"][0]["group"])) + def test_a_layer_outside_the_vertical_bounds(self): + p = self.plan() + g = group(p, MINE + "mine_unique_a")["placement"] + self.assertEqual((g["ideal"]["L"], g["layer_offset"]), (-4, -5)) + g["vertical_bounds_from_exit_semantics"] = [-4, -4] + self.assertCaught(p, "layer offset -5 of {} is outside its vertical bounds [-4, -4]".format( + MINE + "mine_unique_a")) + def test_search_limits(self): p = self.plan() p["settings"]["max_extra_layers"] = 0 @@ -707,6 +736,43 @@ def test_summary_lists(self): self.assertCaught(p, "summary failed_placements") +@unittest.skipUnless(os.environ.get("PLACEMENT_PLAN_CONTENT"), "set PLACEMENT_PLAN_CONTENT to a content checkout") +class ContentFidelity(VerifierTest): + """Edits that keep the JSON consistent with itself pass verify() but not verify_content().""" + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.content = Path(os.environ["PLACEMENT_PLAN_CONTENT"]).resolve() + cls.fresh = verify_plan.rebuild(cls.content) + + def assertUnfaithful(self, p): + self.assertTrue(verify_plan.verify(p)["ok"], "the JSON-only check should not see this edit") + res = verify_plan.verify_content(p, self.content, self.fresh) + self.assertFalse(res["ok"]) + self.assertIn("the plan differs from a rebuild from the content", res["errors"][0]) + + def test_the_committed_plan_is_the_rebuild(self): + self.assertTrue(verify_plan.verify_content(self.plan(), self.content, self.fresh)["ok"]) + + def test_edits_the_json_only_check_cannot_see(self): + key = ("/planes/creation/creation_0501", 427) + p = self.plan() # an exit dropped from the inventory together with its count + p["exit_inventory"] = [x for x in p["exit_inventory"] if (x["source_map"], x["line"]) != key] + p["summary"]["exit_objects"] -= 1 + self.assertUnfaithful(p) + p = self.plan() # a recordless exit moved to another source line + next(x for x in p["exit_inventory"] if (x["source_map"], x["line"]) == key)["line"] = -1 + self.assertUnfaithful(p) + p = self.plan() # an optional flag present as null in every copy + for _g, _lst, e in copies(p, CROSSING): + e["target_is_map_default_entry"] = None + self.assertUnfaithful(p) + p = self.plan() # a value of another JSON type that compares equal in Python + p["summary"]["wired_exits"] = 5.0 + self.assertUnfaithful(p) + + class PlacePool(unittest.TestCase): """Fix 3 in the generator: a failed group stays unplaced and nothing is placed through it.""" diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 258f1ca..b4f5003 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -1,7 +1,25 @@ #!/usr/bin/env python3 -"""Independent check of placement-plan.json (uses only the JSON). +"""Check placement-plan.json: its rules from the JSON alone, and its fidelity to the content. - python3 -I scripts/verify_plan.py [placement-plan.json] + python3 -I scripts/verify_plan.py [placement-plan.json] [--content DIR] + +Two checks with separate jobs: + + * verify(plan), always: re-derives the placement rules from the plan's own + data (the list below). It needs only the JSON, so tests and CI run it. + * verify_content(plan, DIR), with --content: rebuilds the plan from the + content checkout at content_git_head with the generator and requires the + published JSON to equal the rebuild field for field (content_root, the + checkout's path, aside). This is the fidelity check: every copied field, + source fact, inventory entry and count is compared with what the content + produces, so an edit that keeps the JSON consistent with itself (an exit + dropped from exit_inventory together with summary.exit_objects, a source + value changed in every copy) fails here, even when the rules still hold. + +The JSON-only cross-checks of copies below are defence in depth, not the +fidelity guarantee; that is --content. + +verify(plan) checks: Groups: * overworld maps are 24 x 24 (settings.overworld_map_size, every rows grid) @@ -24,7 +42,7 @@ configured layers away; every configured alignment is applied (its group's tentative placement carries it) * no placement goes further from its ideal spot than max_extra_layers and - max_shift_tiles allow + max_shift_tiles allow, or outside its vertical bounds * member plane origin == local_offset + (x_offset, y_offset, layer_offset); no placed group has a relayered region (its member maps are recorded on one layer each; the generator refuses to publish otherwise) @@ -74,7 +92,8 @@ or inventory entry; a blocked site lists at least one exit, only exits that need a stairwell, between its own two owners (a relayering site: overworld exits between its top and bottom maps), and has the shape of - its ends; a kept decoration sits at its tile on the plane; + its ends, which are 2+ layers and at most one tile per layer apart; a + kept decoration sits at its tile on the plane; every placement has a main connection to something placed before it, except one forced by an alignment hint * the records of one exit (one per group it touches) are identical; an @@ -582,7 +601,7 @@ def tile_ok(t_): # a target (resolved by the reader, or wired) is a tile of its map tw, th = size.get(want["target_map"], (OW_SIZE,) * 2) tt = want.get("target_tile") - if not (isinstance(tt, list) and len(tt) == 2 and all(isinstance(c, int) for c in tt) + if not (isinstance(tt, list) and len(tt) == 2 and all(type(c) is int for c in tt) and 0 <= tt[0] < tw and 0 <= tt[1] < th): errors.append("exit {}:{} targets tile {} outside {} ({} x {})".format( k[0], k[1], tt, want["target_map"], tw, th)) @@ -852,6 +871,10 @@ def needs_stairwell(e): ("relayering", rl["stairwells_blocked"])): for b in sites: shape_ = "sloped" if cheb(b["top"], b["bottom"]) > tol else "column" + span_ = b["top"][2] - b["bottom"][2] + if span_ < 2 or cheb(b["top"], b["bottom"]) > span_ * tol: + errors.append("blocked {} stairwell site {} -> {} is no stairwell site: its ends are {} tiles apart " + "over {} layers".format(key_, b["top"], b["bottom"], cheb(b["top"], b["bottom"]), span_)) if not b["exits"] or b.get("shape") != shape_ or key_ != "relayering" and shape_ != key_: errors.append("blocked {} stairwell site at {} lists {} exits with shape {} ({} by its ends)".format( key_, b["top"], len(b["exits"]), b.get("shape"), shape_)) @@ -1065,6 +1088,10 @@ def counted(es): "(max_extra_layers {}, max_shift_tiles {})".format(g["id"], extra, sh, st["max_extra_layers"], st["max_shift_tiles"])) + vb = pl.get("vertical_bounds_from_exit_semantics") or [None, None] + if vb[0] is not None and pl["layer_offset"] < vb[0] or vb[1] is not None and pl["layer_offset"] > vb[1]: + errors.append("layer offset {} of {} is outside its vertical bounds {}".format( + pl["layer_offset"], g["id"], vb)) if step is None or not isinstance(extra, int) or extra < 0 or None in (ideal.get("dx"), ideal.get("dy"), ideal.get("L"), *sh) or \ [pl["x_offset"], pl["y_offset"], pl["layer_offset"]] != [ @@ -1535,10 +1562,90 @@ def moved_end(g, mp, t): "steep_steps": len(steep), "new_contacts": len(contacts)}} +def canonical(v) -> str: + """JSON text of a value with sorted keys: equal texts mean equal JSON (true is not 1, 1.0 is not 1).""" + return json.dumps(v, sort_keys=True, ensure_ascii=False, separators=(",", ":")) + + +def first_difference(a, b, path="", limit=12): + """Paths where two JSON values differ, at most `limit` of them (deepest common path).""" + if type(a) is not type(b) or not isinstance(a, (dict, list)): + return [path or "/"] if canonical(a) != canonical(b) else [] + if isinstance(a, dict): + out = [] + for k in sorted(set(a) | set(b)): + if k not in a or k not in b: + out.append("{}/{}".format(path, k)) + elif canonical(a[k]) != canonical(b[k]): + out += first_difference(a[k], b[k], "{}/{}".format(path, k), limit - len(out)) + if len(out) >= limit: + break + return out[:limit] + if isinstance(a, list): + if len(a) != len(b): + return ["{} (length {} != {})".format(path or "/", len(a), len(b))] + out = [] + for i, (x, y) in enumerate(zip(a, b)): + if canonical(x) != canonical(y): + out += first_difference(x, y, "{}[{}]".format(path, i), limit - len(out)) + if len(out) >= limit: + break + return out[:limit] + return [path or "/"] if canonical(a) != canonical(b) else [] + + +def rebuild(content: Path) -> dict: + """The plan the generator builds from `content` (read-only), as JSON data.""" + sys.path.insert(0, str(Path(__file__).resolve().parent)) + import build_plan # the generator + + return json.loads(json.dumps(build_plan.build(content)[0])) + + +def compare_with_rebuild(plan: dict, fresh: dict) -> list: + """Errors unless `plan` equals the rebuild as JSON, content_root (the checkout's path) aside.""" + published = canonical({k: v for k, v in plan.items() if k != "content_root"}) + if published == canonical({k: v for k, v in fresh.items() if k != "content_root"}): + return [] + diff = first_difference({k: v for k, v in plan.items() if k != "content_root"}, + {k: v for k, v in fresh.items() if k != "content_root"}) + return ["the plan differs from a rebuild from the content at {}: {}".format( + str(fresh.get("content_git_head"))[:12], ", ".join(diff) or "(value types)")] + + +def verify_content(plan: dict, content: Path, fresh: dict = None) -> dict: + """Fidelity: rebuild the plan from the content with the generator and compare it with `plan`. + + The checkout must be at the plan's content_git_head and clean under the + input trees (the generator also re-checks the inputs digest). Every field + must equal the rebuild as JSON, content_root (the checkout's own path) + aside. `fresh` is a rebuild already made from the same checkout.""" + sys.path.insert(0, str(Path(__file__).resolve().parent)) + import build_plan # the generator (reads the content read-only) + + rev = build_plan.content_revision(content) + if rev["head"] != plan.get("content_git_head") or not rev["clean"]: + return {"ok": False, "errors": ["content checkout {} is at {}{}, the plan was built at {}".format( + content, rev["head"][:12], "" if rev["clean"] else " with uncommitted changes", + str(plan.get("content_git_head"))[:12])]} + errors = compare_with_rebuild(plan, rebuild(content) if fresh is None else fresh) + return {"ok": not errors, "errors": errors} + + def main(argv=None): - argv = argv if argv is not None else sys.argv[1:] - path = Path(argv[0]) if argv else Path(__file__).resolve().parents[1] / "placement-plan.json" - res = verify(json.loads(path.read_text(encoding="utf-8"))) + import argparse + + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("plan", nargs="?", type=Path, + default=Path(__file__).resolve().parents[1] / "placement-plan.json") + ap.add_argument("--content", type=Path, + help="content checkout at the plan's content_git_head: also check the plan against a rebuild") + args = ap.parse_args(argv if argv is not None else sys.argv[1:]) + plan = json.loads(args.plan.read_text(encoding="utf-8")) + res = verify(plan) + if args.content is not None: + res["content"] = verify_content(plan, args.content.resolve()) + res["ok"] = res["ok"] and res["content"]["ok"] print(json.dumps(res, indent=1)) return 0 if res["ok"] else 1 From 791b2c02ea0f8d9e6ed43a0f98fa96259284a4a1 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 17:57:49 -0500 Subject: [PATCH 15/20] fix(placement-plan): keep bytecode caches out of the source tree 63a5fd6 committed scripts/__pycache__/build_plan.cpython-314.pyc. It was written when verify_plan.py --content imported the generator, before any module disabled bytecode writing. That broke tools/test_writable_source.py in CI. Remove the file, and have verify_plan.py set sys.dont_write_bytecode at import, as build_plan.py and the tests already do. Co-Authored-By: Claude Opus 5.5 --- .../__pycache__/build_plan.cpython-314.pyc | Bin 183784 -> 0 bytes .../placement-plan/scripts/verify_plan.py | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc diff --git a/migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc b/migration/placement-plan/scripts/__pycache__/build_plan.cpython-314.pyc deleted file mode 100644 index 0321748f761b3d25b1b69e6057f7fb8999205451..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183784 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
7PfZ`3b=lCY%>%PGpQ;?3Rd#MlS@#AAU-#bl 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 Jq{_|x{{w3p3Y!1` diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index b4f5003..88612d5 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -149,6 +149,8 @@ import sys from pathlib import Path +sys.dont_write_bytecode = True # --content imports the generator; keep the source tree free of caches + WORLD_RE = re.compile(r"/world_(-?\d+)_(-?\d+)(?:_(-?\d+))?$") OW_SIZE = 24 # overworld maps are 24 x 24; plane tile = 24 * filename coordinate + tile N8 = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1) if (dx, dy) != (0, 0)] From cc94be155023facbc51ed3cfac8eee784e52c2ad Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 18:19:57 -0500 Subject: [PATCH 16/20] fix(placement-plan): guard malformed plans, bump the schema, run tests in CI Address the Codex review of 63a5fd6 on #47. - verify() turns any exception from a malformed plan (a missing key, a null where a list belongs) into a failed result rather than a traceback. verify_plan.py --content reports a checkout it cannot rebuild from the same way. - Placement offsets, ideal, shift and extra layers must be integers (booleans and floats are rejected). - The plan's schema is now atrinik-overworld-placement-plan/4, since exit_inventory and the failed-placement bookkeeping are required, and the verifier accepts only that schema. - tools/validate.sh runs test_placement_plan.py after the Cargo steps, like the other tools/test_*.py suites, so CI exercises the verifier's protections. The content-fidelity tests skip there. Regenerated from content 3ad20150da76: only the schema string changes. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/placement-plan.json | 2 +- .../placement-plan/scripts/build_plan.py | 2 +- .../scripts/test_placement_plan.py | 24 ++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 28 +++++++++++++++++-- tools/validate.sh | 1 + 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/migration/placement-plan/placement-plan.json b/migration/placement-plan/placement-plan.json index cbf9a3a..769a586 100644 --- a/migration/placement-plan/placement-plan.json +++ b/migration/placement-plan/placement-plan.json @@ -1,5 +1,5 @@ { - "schema": "atrinik-overworld-placement-plan/3", + "schema": "atrinik-overworld-placement-plan/4", "generated_by": "scripts/build_plan.py", "content_root": "/home/zoey/dev/worktrees/content-plan-v2-input", "content_git_head": "3ad20150da7656516dae06d4f504d09af4cba344", diff --git a/migration/placement-plan/scripts/build_plan.py b/migration/placement-plan/scripts/build_plan.py index 007f2cf..50d20fb 100644 --- a/migration/placement-plan/scripts/build_plan.py +++ b/migration/placement-plan/scripts/build_plan.py @@ -1976,7 +1976,7 @@ def bbox_layers(gid, dx=0, dy=0, L=0): counts["{}:{}".format(g["classification"], g["reason"])] += 1 sw_spans = collections.Counter(sw["top_layer"] - sw["bottom_layer"] for sw in S.stairwells) plan = { - "schema": "atrinik-overworld-placement-plan/3", + "schema": "atrinik-overworld-placement-plan/4", "generated_by": "scripts/build_plan.py", "content_root": str(content), "content_git_head": revision["head"], diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index f15b0b7..b50e23e 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -72,6 +72,21 @@ def test_committed_plan_verifies(self): self.assertTrue(res["ok"], res["errors"][:8]) self.assertEqual(res["exit_inventory_objects"], len(self.plan()["exit_inventory"])) + def test_a_malformed_plan_fails_without_a_crash(self): + for change in (lambda p: p["exit_inventory"][0].update(source_tile=None), + lambda p: p.pop("stairwells"), + lambda p: p["groups"][0].update(maps=None)): + p = self.plan() + change(p) + res = verify_plan.verify(p) + self.assertFalse(res["ok"]) + self.assertTrue(res["errors"]) + + def test_another_schema(self): + p = self.plan() + p["schema"] = "atrinik-overworld-placement-plan/3" + self.assertCaught(p, "schema atrinik-overworld-placement-plan/3 is not atrinik-overworld-placement-plan/4") + class ExitInventory(VerifierTest): """Fix 1: the exit records are checked against the complete source exit inventory.""" @@ -672,6 +687,15 @@ def test_a_layer_outside_the_vertical_bounds(self): self.assertCaught(p, "layer offset -5 of {} is outside its vertical bounds [-4, -4]".format( MINE + "mine_unique_a")) + def test_placement_numbers_are_integers(self): + for field, value in (("extra_layers_from_ideal", True), ("shift_from_ideal", [False, 0]), + ("layer_offset", -5.0)): + with self.subTest(field=field): + p = self.plan() + group(p, MINE + "mine_unique_a")["placement"][field] = value + self.assertCaught(p, "placement of {} has offsets, ideal, shift or extra layers that are not " + "integers".format(MINE + "mine_unique_a")) + def test_search_limits(self): p = self.plan() p["settings"]["max_extra_layers"] = 0 diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 88612d5..7a9a382 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -153,6 +153,7 @@ WORLD_RE = re.compile(r"/world_(-?\d+)_(-?\d+)(?:_(-?\d+))?$") OW_SIZE = 24 # overworld maps are 24 x 24; plane tile = 24 * filename coordinate + tile +SCHEMA = "atrinik-overworld-placement-plan/4" # /4 adds exit_inventory and the failed-placement bookkeeping N8 = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1) if (dx, dy) != (0, 0)] # Fields of an exit record that come from the content: equal to its inventory entry. SOURCE_FIELDS = ("source_map", "source_tile", "target_map", "target_tile", "arch", "line", "form", "vertical", @@ -244,6 +245,18 @@ def flood(keys, same): def verify(plan: dict) -> dict: + """The JSON-only check. A malformed plan (a missing key, a value of the wrong shape) fails it with an + error rather than an exception.""" + try: + return _verify(plan) + except Exception as exc: # noqa: BLE001 - any malformation is a failed check, not a crash + return {"ok": False, "errors": ["the plan is malformed: {}: {}".format(type(exc).__name__, exc)]} + + +def _verify(plan: dict) -> dict: + if plan.get("schema") != SCHEMA: + return {"ok": False, "errors": ["schema {} is not {}, the shape this verifier checks".format( + plan.get("schema"), SCHEMA)]} st = plan["settings"] R, M, tol = st["view_radius_tiles"], st["contact_margin_tiles"], st["stair_tolerance_tiles"] bend = st["stairwell_max_bend_tiles"] @@ -1084,8 +1097,13 @@ def counted(es): ideal, orient = pl.get("ideal") or {}, pl.get("orientation") step = {"below": -1, "above": 1}.get(orient) sh, extra = pl.get("shift_from_ideal") or [None, None], pl.get("extra_layers_from_ideal") - if None not in sh and isinstance(extra, int) and ( - extra > st["max_extra_layers"] or max(abs(sh[0]), abs(sh[1])) > st["max_shift_tiles"]): + if not (type(extra) is int and isinstance(sh, list) and len(sh) == 2 and all(type(c) is int for c in sh) + and all(type(pl.get(f)) is int for f in ("x_offset", "y_offset", "layer_offset")) + and all(type(ideal.get(f)) is int for f in ("dx", "dy", "L"))): + errors.append("placement of {} has offsets, ideal, shift or extra layers that are not integers".format( + g["id"])) + continue + if extra > st["max_extra_layers"] or max(abs(sh[0]), abs(sh[1])) > st["max_shift_tiles"]: errors.append("placement of {} goes {} layer(s) and {} beyond its ideal spot, past the search limits " "(max_extra_layers {}, max_shift_tiles {})".format(g["id"], extra, sh, st["max_extra_layers"], @@ -1646,7 +1664,11 @@ def main(argv=None): plan = json.loads(args.plan.read_text(encoding="utf-8")) res = verify(plan) if args.content is not None: - res["content"] = verify_content(plan, args.content.resolve()) + try: + res["content"] = verify_content(plan, args.content.resolve()) + except Exception as exc: # noqa: BLE001 - a checkout that cannot be rebuilt fails the check + res["content"] = {"ok": False, "errors": ["cannot rebuild from {}: {}: {}".format( + args.content, type(exc).__name__, exc)]} res["ok"] = res["ok"] and res["content"]["ok"] print(json.dumps(res, indent=1)) return 0 if res["ok"] else 1 diff --git a/tools/validate.sh b/tools/validate.sh index 973f9da..5a6811d 100755 --- a/tools/validate.sh +++ b/tools/validate.sh @@ -16,6 +16,7 @@ cargo clippy --locked --workspace --all-targets -- -D warnings cargo test --locked --workspace --all-targets cargo test --locked --workspace --doc python3 tools/test_writable_source.py +python3 -I migration/placement-plan/scripts/test_placement_plan.py cargo build --locked --release --package atrinik-content cargo run --locked --quiet --package atrinik-content -- --version cargo run --locked --quiet --package atrinik-content -- \ From 85ed583018d593abab7a7d88262b9dd419408e95 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 18:55:32 -0500 Subject: [PATCH 17/20] fix(placement-plan): keep the plan's numbers integers and bound comparisons Address the Codex review of cc94be1 on #47, fixing each class rather than each field. - One pass over the plan rejects floats anywhere, and booleans outside the known flag keys. Python's true == 1 can then no longer stand in for a distance, a layer or a count in any JSON-only comparison (layer_delta and xy_distance included). - The fidelity comparison checks JSON type for type in place, without serializing both plans. verify_plan.py refuses a plan file over 64 MiB before parsing it. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 36 +++++++++- .../placement-plan/scripts/verify_plan.py | 72 +++++++++++++++---- 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index b50e23e..03a0935 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -82,6 +82,32 @@ def test_a_malformed_plan_fails_without_a_crash(self): self.assertFalse(res["ok"]) self.assertTrue(res["errors"]) + def test_booleans_and_floats_are_not_numbers(self): + key = ("/shattered_islands/berri_lur/temple_loki/tol_0201", 441) + p = self.plan() + for _g, _lst, e in copies(p, key): + e["layer_delta"] = True + for r in p["relayering"]["exits"]: + if (r["source_map"], r["line"]) == key: + r["layer_delta_after"] = True + self.assertCaught(p, "the plan has booleans or floats where integers belong") + p = self.plan() + p["stairwells"][0]["tiles"][1]["layer"] = float(p["stairwells"][0]["tiles"][1]["layer"]) + self.assertCaught(p, "the plan has booleans or floats where integers belong, e.g. /stairwells[0]/tiles[1]/layer") + + def test_strict_json_comparison(self): + self.assertTrue(verify_plan.same_json({"a": [1, {"b": None}]}, {"a": [1, {"b": None}]})) + for a, b in (([1], [True]), ({"a": 1}, {"a": 1.0}), ({"a": None}, {}), ([1, 2], [2, 1])): + self.assertFalse(verify_plan.same_json(a, b), (a, b)) + + def test_an_oversized_plan_file(self): + limit = verify_plan.MAX_PLAN_BYTES + try: + verify_plan.MAX_PLAN_BYTES = 1000 + self.assertEqual(verify_plan.main([str(PLAN)]), 1) + finally: + verify_plan.MAX_PLAN_BYTES = limit + def test_another_schema(self): p = self.plan() p["schema"] = "atrinik-overworld-placement-plan/3" @@ -792,10 +818,16 @@ def test_edits_the_json_only_check_cannot_see(self): for _g, _lst, e in copies(p, CROSSING): e["target_is_map_default_entry"] = None self.assertUnfaithful(p) - p = self.plan() # a value of another JSON type that compares equal in Python - p["summary"]["wired_exits"] = 5.0 + p = self.plan() # explanatory text the JSON-only check does not re-derive + group(p, "/emergency")["evidence"][0] = "made up" self.assertUnfaithful(p) + def test_type_strict_comparison(self): + p = self.plan() # caught by both checks, and by the rebuild comparison type for type + p["summary"]["wired_exits"] = 5.0 + self.assertFalse(verify_plan.verify(p)["ok"]) + self.assertFalse(verify_plan.verify_content(p, self.content, self.fresh)["ok"]) + class PlacePool(unittest.TestCase): """Fix 3 in the generator: a failed group stays unplaced and nothing is placed through it.""" diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 7a9a382..0568b3a 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -21,6 +21,9 @@ verify(plan) checks: +Values: + * the plan's numbers are integers: no floats, and booleans only under + the flag keys (BOOL_KEYS); the plan file is at most MAX_PLAN_BYTES Groups: * overworld maps are 24 x 24 (settings.overworld_map_size, every rows grid) * one overworld group; group ids and member maps (overworld cells @@ -154,6 +157,30 @@ WORLD_RE = re.compile(r"/world_(-?\d+)_(-?\d+)(?:_(-?\d+))?$") OW_SIZE = 24 # overworld maps are 24 x 24; plane tile = 24 * filename coordinate + tile SCHEMA = "atrinik-overworld-placement-plan/4" # /4 adds exit_inventory and the failed-placement bookkeeping +MAX_PLAN_BYTES = 64 * 1024 * 1024 # the plan is about 3 MB; a larger file is not a plan +# The plan's numbers are integers; JSON booleans appear only under these flag keys, and no value is a float. +BOOL_KEYS = {"content_worktree_clean", "segments_drawn_only_on_player_layer", "existing", "exit", "walkable", + "landing_blocked", "per_player_last_eat_1", "explicit_last_heal_0", "target_is_map_default_entry", + "unique_getpath", "test_only", "house_tuple_unique", "main_stairwell_blocked", "slope_reachable", + "relayered", "ok"} + + +def number_type_errors(plan, limit=5) -> list: + """Paths of booleans outside the flag keys and of floats: a boolean is not a count or a coordinate, + however Python compares it.""" + bad, stack = [], [(plan, None, "")] + while stack and len(bad) < limit: + x, key, path = stack.pop() + if isinstance(x, bool): + if key not in BOOL_KEYS: + bad.append(path) + elif isinstance(x, float): + bad.append(path) + elif isinstance(x, dict): + stack.extend((v, k, "{}/{}".format(path, k)) for k, v in x.items()) + elif isinstance(x, list): + stack.extend((v, key, "{}[{}]".format(path, i)) for i, v in enumerate(x)) + return sorted(bad) N8 = [(dx, dy) for dy in (-1, 0, 1) for dx in (-1, 0, 1) if (dx, dy) != (0, 0)] # Fields of an exit record that come from the content: equal to its inventory entry. SOURCE_FIELDS = ("source_map", "source_tile", "target_map", "target_tile", "arch", "line", "form", "vertical", @@ -257,6 +284,10 @@ def _verify(plan: dict) -> dict: if plan.get("schema") != SCHEMA: return {"ok": False, "errors": ["schema {} is not {}, the shape this verifier checks".format( plan.get("schema"), SCHEMA)]} + errors = [] + bad_numbers = number_type_errors(plan) + if bad_numbers: + errors.append("the plan has booleans or floats where integers belong, e.g. {}".format(", ".join(bad_numbers))) st = plan["settings"] R, M, tol = st["view_radius_tiles"], st["contact_margin_tiles"], st["stair_tolerance_tiles"] bend = st["stairwell_max_bend_tiles"] @@ -264,7 +295,6 @@ def _verify(plan: dict) -> dict: ow = next(g for g in plan["groups"] if g["classification"] == "overworld") ow_id = ow["id"] rl = plan["relayering"] - errors = [] # ------------------------------------------------------------ occupancy owners = collections.defaultdict(dict) # layer -> owner -> Rows @@ -1582,21 +1612,36 @@ def moved_end(g, mp, t): "steep_steps": len(steep), "new_contacts": len(contacts)}} -def canonical(v) -> str: - """JSON text of a value with sorted keys: equal texts mean equal JSON (true is not 1, 1.0 is not 1).""" - return json.dumps(v, sort_keys=True, ensure_ascii=False, separators=(",", ":")) +def same_json(a, b) -> bool: + """JSON equality without Python's coercions (true is not 1, 1.0 is not 1), compared in place.""" + stack = [(a, b)] + while stack: + x, y = stack.pop() + if type(x) is not type(y): + return False + if isinstance(x, dict): + if x.keys() != y.keys(): + return False + stack.extend((x[k], y[k]) for k in x) + elif isinstance(x, list): + if len(x) != len(y): + return False + stack.extend(zip(x, y)) + elif x != y: + return False + return True def first_difference(a, b, path="", limit=12): """Paths where two JSON values differ, at most `limit` of them (deepest common path).""" if type(a) is not type(b) or not isinstance(a, (dict, list)): - return [path or "/"] if canonical(a) != canonical(b) else [] + return [path or "/"] if not same_json(a, b) else [] if isinstance(a, dict): out = [] for k in sorted(set(a) | set(b)): if k not in a or k not in b: out.append("{}/{}".format(path, k)) - elif canonical(a[k]) != canonical(b[k]): + elif not same_json(a[k], b[k]): out += first_difference(a[k], b[k], "{}/{}".format(path, k), limit - len(out)) if len(out) >= limit: break @@ -1606,12 +1651,12 @@ def first_difference(a, b, path="", limit=12): return ["{} (length {} != {})".format(path or "/", len(a), len(b))] out = [] for i, (x, y) in enumerate(zip(a, b)): - if canonical(x) != canonical(y): + if not same_json(x, y): out += first_difference(x, y, "{}[{}]".format(path, i), limit - len(out)) if len(out) >= limit: break return out[:limit] - return [path or "/"] if canonical(a) != canonical(b) else [] + return [path or "/"] if not same_json(a, b) else [] def rebuild(content: Path) -> dict: @@ -1624,11 +1669,11 @@ def rebuild(content: Path) -> dict: def compare_with_rebuild(plan: dict, fresh: dict) -> list: """Errors unless `plan` equals the rebuild as JSON, content_root (the checkout's path) aside.""" - published = canonical({k: v for k, v in plan.items() if k != "content_root"}) - if published == canonical({k: v for k, v in fresh.items() if k != "content_root"}): + published = {k: v for k, v in plan.items() if k != "content_root"} + rebuilt = {k: v for k, v in fresh.items() if k != "content_root"} + if same_json(published, rebuilt): return [] - diff = first_difference({k: v for k, v in plan.items() if k != "content_root"}, - {k: v for k, v in fresh.items() if k != "content_root"}) + diff = first_difference(published, rebuilt) return ["the plan differs from a rebuild from the content at {}: {}".format( str(fresh.get("content_git_head"))[:12], ", ".join(diff) or "(value types)")] @@ -1661,6 +1706,9 @@ def main(argv=None): ap.add_argument("--content", type=Path, help="content checkout at the plan's content_git_head: also check the plan against a rebuild") args = ap.parse_args(argv if argv is not None else sys.argv[1:]) + if args.plan.stat().st_size > MAX_PLAN_BYTES: + print(json.dumps({"ok": False, "errors": ["{} is larger than {} bytes".format(args.plan, MAX_PLAN_BYTES)]})) + return 1 plan = json.loads(args.plan.read_text(encoding="utf-8")) res = verify(plan) if args.content is not None: From 24e13292493f4b436713eab81c26dc58ccfcbcf2 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 19:26:30 -0500 Subject: [PATCH 18/20] test(placement-plan): run the verifier tests in parallel workers Address the Codex review of 85ed583 on #47. Each of the suite's 134 verifications re-runs the full verifier on the 3 MB plan, about 1.2 s locally, so the serial suite took about 165 s here and about 10 min in Codex's environment. The tests are independent and CPU-bound, so the suite's entry point now runs one test per task in up to 8 forked worker processes, with --jobs 1 for serial. Each case still verifies its own tampered plan in isolation. Locally the suite takes 29 s with 8 workers and 48 s with 4, the size of CI's runners. Failures are reported with their unittest output and a nonzero exit. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 03a0935..d5220f0 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Negative tests for the placement-plan verifier and generator (standard library only). - python3 -I scripts/test_placement_plan.py + python3 -I scripts/test_placement_plan.py [--jobs N] PLACEMENT_PLAN_CONTENT=DIR python3 -I scripts/test_placement_plan.py Each verifier test tampers with a copy of the committed placement-plan.json @@ -10,7 +10,8 @@ needs a content checkout. With PLACEMENT_PLAN_CONTENT naming a checkout at the plan's content_git_head, ContentFidelity also checks that verify_content() rejects edits that keep the JSON consistent with itself; without it, those -tests are skipped. +tests are skipped. Each test runs the full verifier, so the tests run in up +to 8 worker processes (--jobs 1 runs them in this process). """ from __future__ import annotations @@ -101,12 +102,17 @@ def test_strict_json_comparison(self): self.assertFalse(verify_plan.same_json(a, b), (a, b)) def test_an_oversized_plan_file(self): - limit = verify_plan.MAX_PLAN_BYTES + import contextlib + import io + + limit, out = verify_plan.MAX_PLAN_BYTES, io.StringIO() try: verify_plan.MAX_PLAN_BYTES = 1000 - self.assertEqual(verify_plan.main([str(PLAN)]), 1) + with contextlib.redirect_stdout(out): + self.assertEqual(verify_plan.main([str(PLAN)]), 1) finally: verify_plan.MAX_PLAN_BYTES = limit + self.assertIn("is larger than 1000 bytes", out.getvalue()) def test_another_schema(self): p = self.plan() @@ -866,5 +872,54 @@ def test_no_placement_record_is_a_failure(self): self.assertEqual((order, failed, left), (["d"], ["a"], ["b", "c"])) +def _run_one(test_id): + """Run one test in a worker process; return (id, outcome, report).""" + import io + + out = io.StringIO() + result = unittest.TextTestRunner(stream=out, verbosity=0).run( + unittest.defaultTestLoader.loadTestsFromName(test_id, sys.modules[__name__])) + outcome = "ok" if result.wasSuccessful() else "fail" + if result.wasSuccessful() and result.skipped: + outcome = "skip" + return test_id, outcome, "" if result.wasSuccessful() else out.getvalue() + + +def main(argv=None): + """Run the suite, by default in parallel worker processes (one test per task; results are the same).""" + import argparse + import multiprocessing + import time + from concurrent.futures import ProcessPoolExecutor + + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--jobs", type=int, default=min(os.cpu_count() or 1, 8), + help="worker processes (1 runs the tests in this process)") + args = ap.parse_args(argv) + if args.jobs <= 1 or "fork" not in multiprocessing.get_all_start_methods(): + return 0 if unittest.main(argv=[sys.argv[0]], exit=False).result.wasSuccessful() else 1 + + def ids(suite): + for t in suite: + if isinstance(t, unittest.TestSuite): + yield from ids(t) + else: + yield t.id().split(".", 1)[1] # Class.method, looked up in this module by the worker + + tests = sorted(ids(unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]))) + start = time.monotonic() + with ProcessPoolExecutor(args.jobs, mp_context=multiprocessing.get_context("fork")) as pool: + results = list(pool.map(_run_one, tests)) + failed = [(t, report) for t, outcome, report in results if outcome == "fail"] + for t, report in failed: + sys.stderr.write("{}\nFAIL: {}\n{}\n".format("=" * 70, t, report)) + skipped = sum(1 for _t, outcome, _r in results if outcome == "skip") + sys.stderr.write("{}\nRan {} tests in {:.3f}s ({} workers)\n\n{}\n".format( + "-" * 70, len(results), time.monotonic() - start, args.jobs, + "FAILED (failures={})".format(len(failed)) if failed else + "OK" + (" (skipped={})".format(skipped) if skipped else ""))) + return 1 if failed else 0 + + if __name__ == "__main__": - unittest.main() + raise SystemExit(main()) From 3d04cb7f8705259fc0e17a378f7a6d840b90565b Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 19:56:52 -0500 Subject: [PATCH 19/20] perf(placement-plan): stop re-deriving the same relayering in every check Address the Codex review of 24e1329 on #47. Each test verified its own tampered plan in full, and about 80% of a verification is the relayering re-derivation (242,824 overworld tiles), whose inputs almost no test touches. - relayering_rules() is now a pure function of explicit inputs: the overworld rows, the relayering record, the overworld's internal exits, the seam pieces' exits that have a record, and four settings. _relayering() memoizes the last result in the process, keyed by a SHA-256 of the canonical JSON of exactly those inputs, so the results are the same with or without the cache. A test asserts that each input is in the key, and that a changed relayering input is recomputed. - The occupancy pass finds '#' tiles with str.find and computes bounding boxes once per owner instead of per tile. The number-type pass checks first without building paths. - The test runner verifies the committed plan before forking, so every worker starts with the cache warm. verify_plan.py's output is byte-identical. A verification of an unchanged relayering takes 0.22 s instead of 1.15 s. The suite takes 39 s serially (was 166 s), 14 s with 4 workers and 10 s with 8. Co-Authored-By: Claude Opus 5.5 --- .../scripts/test_placement_plan.py | 33 + .../placement-plan/scripts/verify_plan.py | 1080 +++++++++-------- 2 files changed, 598 insertions(+), 515 deletions(-) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index d5220f0..813eded 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -114,6 +114,38 @@ def test_an_oversized_plan_file(self): verify_plan.MAX_PLAN_BYTES = limit self.assertIn("is larger than 1000 bytes", out.getvalue()) + def test_the_relayering_cache_follows_its_inputs(self): + # a changed relayering input is recomputed, never served from the cache + p = self.plan() + c = p["relayering"]["new_contacts"][0] + verify_plan.verify(self.plan()) + c["walkable"] = not c["walkable"] + self.assertCaught(p, "new contact {} / {} records walkable".format(tuple(c["tiles"][0]), tuple(c["tiles"][1]))) + # every input is in the key: each change below computes the relayering again + calls, rules = [], verify_plan.relayering_rules + ow_rows = next(g for g in self.plan()["groups"] if g["classification"] == "overworld")["occupied_rows_by_layer"] + args = [ow_rows, {}, self.plan()["relayering"], [], {("m", 1)}, [50, 50, 10, 1]] + + def counting(*a): + calls.append(a) + return [], {}, {} + + try: + verify_plan.relayering_rules = counting + verify_plan._RELAYER_CACHE.clear() + verify_plan._relayering(*args) + verify_plan._relayering(*args) + self.assertEqual(len(calls), 1) + for i, changed in ((0, dict(ow_rows, extra={})), (2, dict(args[2], seam_cell=25)), + (3, [{"source_map": "m"}]), (4, {("m", 2)}), (5, [50, 50, 10, 2])): + a = list(args) + a[i] = changed + verify_plan._relayering(*a) + self.assertEqual(len(calls), 2 + [0, 2, 3, 4, 5].index(i), "input {} is not in the key".format(i)) + finally: + verify_plan.relayering_rules = rules + verify_plan._RELAYER_CACHE.clear() + def test_another_schema(self): p = self.plan() p["schema"] = "atrinik-overworld-placement-plan/3" @@ -908,6 +940,7 @@ def ids(suite): tests = sorted(ids(unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__]))) start = time.monotonic() + verify_plan.verify(json.loads(PLAN.read_text(encoding="utf-8"))) # warm the relayering cache the workers fork with ProcessPoolExecutor(args.jobs, mp_context=multiprocessing.get_context("fork")) as pool: results = list(pool.map(_run_one, tests)) failed = [(t, report) for t, outcome, report in results if outcome == "fail"] diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 0568b3a..4997baa 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -147,6 +147,7 @@ import bisect import collections +import hashlib import json import re import sys @@ -165,9 +166,26 @@ "relayered", "ok"} +def _has_bad_number(plan) -> bool: + """Whether the plan holds a float, or a boolean outside the flag keys (fast pass, no paths).""" + stack = [(plan, None)] + while stack: + x, key = stack.pop() + t = type(x) + if t is dict: + stack.extend((v, k) for k, v in x.items() if type(v) is not str) + elif t is list: + stack.extend((v, key) for v in x if type(v) is not str) + elif t is float or t is bool and key not in BOOL_KEYS: + return True + return False + + def number_type_errors(plan, limit=5) -> list: """Paths of booleans outside the flag keys and of floats: a boolean is not a count or a coordinate, however Python compares it.""" + if not _has_bad_number(plan): + return [] bad, stack = [], [(plan, None, "")] while stack and len(bad) < limit: x, key, path = stack.pop() @@ -226,12 +244,14 @@ def __init__(self): def add(self, x, y): self.rows[y].append(x) self.count += 1 - b = self.box - self.box = (x, y, x, y) if b is None else (min(b[0], x), min(b[1], y), max(b[2], x), max(b[3], y)) def done(self): + """Sort each row and compute the bounding box; called once every tile is added.""" for xs in self.rows.values(): xs.sort() + if self.rows: + self.box = (min(xs[0] for xs in self.rows.values()), min(self.rows), + max(xs[-1] for xs in self.rows.values()), max(self.rows)) def any_within(self, x, y, r) -> bool: b = self.box @@ -271,238 +291,562 @@ def flood(keys, same): return out -def verify(plan: dict) -> dict: - """The JSON-only check. A malformed plan (a missing key, a value of the wrong shape) fails it with an - error rather than an exception.""" - try: - return _verify(plan) - except Exception as exc: # noqa: BLE001 - any malformation is a failed check, not a crash - return {"ok": False, "errors": ["the plan is malformed: {}: {}".format(type(exc).__name__, exc)]} +_RELAYER_CACHE = {} # one entry: the last relayering re-derived in this process, by the hash of its inputs -def _verify(plan: dict) -> dict: - if plan.get("schema") != SCHEMA: - return {"ok": False, "errors": ["schema {} is not {}, the shape this verifier checks".format( - plan.get("schema"), SCHEMA)]} - errors = [] - bad_numbers = number_type_errors(plan) - if bad_numbers: - errors.append("the plan has booleans or floats where integers belong, e.g. {}".format(", ".join(bad_numbers))) - st = plan["settings"] - R, M, tol = st["view_radius_tiles"], st["contact_margin_tiles"], st["stair_tolerance_tiles"] - bend = st["stairwell_max_bend_tiles"] - HZ, MINR, WDZ = st["layer_height_z"], st["relayer_min_region_tiles"], st["walkable_dz"] - ow = next(g for g in plan["groups"] if g["classification"] == "overworld") - ow_id = ow["id"] - rl = plan["relayering"] +def _relayering(ow_rows, leg, rl, internal_exits, record_keys, settings): + """relayering_rules() for these inputs, memoized: tests and the generator verify plans whose relayering + inputs are identical, and the result is a pure function of them (the key covers every input).""" + key = hashlib.sha256(json.dumps([ow_rows, rl, internal_exits, sorted(record_keys), settings], sort_keys=True, + separators=(",", ":")).encode()).hexdigest() + if key not in _RELAYER_CACHE: + _RELAYER_CACHE.clear() + _RELAYER_CACHE[key] = relayering_rules(leg, rl, internal_exits, record_keys, *settings) + return _RELAYER_CACHE[key] - # ------------------------------------------------------------ occupancy - owners = collections.defaultdict(dict) # layer -> owner -> Rows - claim = collections.defaultdict(dict) # layer -> (x, y) -> (owner, map) - origin = {} - leg = {} # (legacy layer, x, y) -> [map, final layers] - def put(owner, mapname, layer, x0, y0, rows): - rr = owners[layer].setdefault(owner, Rows()) - for y, row in enumerate(rows): - for x, ch in enumerate(row): - if ch == "#": - t = (x0 + x, y0 + y) - prev = claim[layer].get(t) - if prev is not None and prev != (owner, mapname): - errors.append("tile {} layer {} claimed by {} and {}".format(t, layer, prev[1], mapname)) - claim[layer][t] = (owner, mapname) - rr.add(*t) +def relayering_rules(leg, rl, internal_exits, record_keys, HZ, MINR, WDZ, tol): + """The relayering, re-derived from the overworld rows (leg) and the tile heights. - ow_rows = ow["occupied_rows_by_layer"] - if st.get("overworld_map_size") != OW_SIZE: - errors.append("settings.overworld_map_size {} is not the {}-tile grid of the overworld coordinates".format( - st.get("overworld_map_size"), OW_SIZE)) - for path, by_layer in sorted(ow_rows.items()): + Returns (errors, final layer of every legacy overworld tile, counts). A pure + function of its arguments; record_keys holds the seam pieces' exits that have an exit record.""" + errors = [] + heights, blocked, ground = {}, set(), {} + for path, rows in sorted(rl["tile_heights"].items()): X, Y, Z = world_cell(path) - for Ls, rows in sorted(by_layer.items(), key=lambda kv: int(kv[0])): - if len(rows) != OW_SIZE or any(len(r) != OW_SIZE for r in rows): - errors.append("rows of {} on layer {} are not {} x {}".format(path, Ls, OW_SIZE, OW_SIZE)) - put(path, path, int(Ls), 24 * X, 24 * Y, rows) - for y, row in enumerate(rows): - for x, ch in enumerate(row): - if ch == "#": - leg.setdefault((Z, 24 * X + x, 24 * Y + y), [path, []])[1].append(int(Ls)) - placed = [] - for g in plan["groups"]: - p = g.get("placement") - if not p or p.get("status") not in ("proposed", "tentative"): - continue - placed.append(g) - for m in g["maps"]: - lo = m["local_offset"] - exp = [lo[0] + p["x_offset"], lo[1] + p["y_offset"], lo[2] + p["layer_offset"]] - got = p["member_origins"][m["path"]] - if exp != got: - errors.append("origin mismatch {} {} != {}".format(m["path"], got, exp)) - origin[m["path"]] = got - if "occupied_rows" not in m: - errors.append("no occupied rows for placed map {}".format(m["path"])) + for y, row in enumerate(rows): + for x, tok in enumerate(row.split(" ")): + if tok == ".": + continue + k = (Z, 24 * X + x, 24 * Y + y) + if tok.endswith("b"): + blocked.add(k) + tok = tok[:-1] + if "/" in tok: + tok, g = tok.split("/") + ground[k] = int(g) + heights[k] = int(tok) + split = {} + for sp in rl["split_tiles"]: + X, Y, Z = world_cell(sp["map"]) + split[(Z, 24 * X + sp["tile"][0], 24 * Y + sp["tile"][1])] = sp + # a split tile's raised part is its upper layer; which tiles split is derived below + main = {k: max(v[1]) for k, v in leg.items()} + # regions + q = {k: h // HZ for k, h in heights.items() if h >= HZ} + comps = flood(q, lambda a, b: q[a] == q[b]) + comps.sort(key=lambda c: (-len(c), c[0])) + relay_comp = [len(c) >= MINR for c in comps] + comp_of = {k: i for i, c in enumerate(comps) for k in c} + changed = True + while changed: + changed = False + for i, c in enumerate(comps): + if relay_comp[i]: continue - put(g["id"], m["path"], got[2], got[0], got[1], m["occupied_rows"]) - for layer in owners.values(): - for rr in layer.values(): - rr.done() - placed_ids = {g["id"] for g in placed} - summ = plan["summary"] + if any((k[0], k[1] + dx, k[2] + dy) in comp_of and relay_comp[comp_of[(k[0], k[1] + dx, k[2] + dy)]] + for k in c for dx, dy in N8): + relay_comp[i] = True + changed = True + relayered = {k for i, c in enumerate(comps) if relay_comp[i] for k in c} + got_r = sorted((c[0][0], q[c[0]], len(c)) for i, c in enumerate(comps) if relay_comp[i]) + got_k = sorted((c[0][0], q[c[0]], len(c)) for i, c in enumerate(comps) if not relay_comp[i]) + if got_r != sorted((r["layer_before"], r["q"], r["tiles"]) for r in rl["regions"]): + errors.append("relayered regions differ from the tile heights") + if got_k != sorted((r["layer_before"], r["q"], r["tiles"]) for r in rl["kept_regions"]): + errors.append("kept regions differ from the tile heights") + # units and shifts + units = [] + unit_of = {} + bylayer = collections.defaultdict(list) + for k in leg: + if k[0] != 0: + bylayer[k[0]].append(k) + for L in sorted(bylayer): + for c in flood(bylayer[L], lambda a, b: True): + for k in c: + unit_of[k] = len(units) + units.append(c) + du = {} + for i, c in enumerate(units): + offs = {main[k] - k[0] for k in c if k not in relayered} + if len(offs) > 1: + errors.append("unit with {} tiles on layer {} does not move as one: shifts {}".format( + len(c), c[0][0], sorted(offs))) + du[i] = min(offs) if offs else 0 + if du[i] > 0 and c[0][0] < 1 or du[i] < 0 and c[0][0] >= 0: + errors.append("unit on layer {} moves {} (storeys move only up, caves only down)".format(c[0][0], du[i])) + dr = {} + for k in sorted(relayered): + d = main[k] - k[0] - q[k] - (du[unit_of[k]] if k in unit_of else 0) + ci = comp_of[k] + if dr.setdefault(ci, d) != d: + errors.append("region tile {} adjustment {} differs from its region's {}".format(k, d, dr[ci])) + if d > 0: + errors.append("region tile {} is adjusted up by {}".format(k, d)) + for k in leg: + if k in relayered: + continue + if k[0] == 0 and main[k] != 0: + errors.append("surface tile {} of {} moved to layer {} without a region".format(k, leg[k][0], main[k])) + # split tiles: a relayered tile whose sub-layer 0 floor stays below its raised floor keeps + # that ground part on its unit's layer; derived from the tile heights, not the records + derived_split = {} + for k in relayered: + g = ground.get(k) + base_layer = k[0] + (du[unit_of[k]] if k in unit_of else 0) + if g is not None and g // HZ < q[k] and base_layer != main[k]: + derived_split[k] = base_layer + if set(derived_split) != set(split): + errors.append("split tiles differ: {} derived from the tile heights, {} recorded".format( + len(derived_split), len(split))) + for k, v in leg.items(): + want = sorted({derived_split[k], main[k]}) if k in derived_split else [main[k]] + if sorted(v[1]) != want: + errors.append("legacy tile {} of {} appears on layers {}, expected {}".format(k, v[0], sorted(v[1]), want)) + for k, sp in split.items(): + if k not in derived_split or sp["ground_layer"] != derived_split[k] or sp["upper_layer"] != main[k] or \ + sp["floors"].get("0") != ground.get(k): + errors.append("split tile {} is inconsistent".format(k)) + if sorted((r["layer_before"], r["q"], r["tiles"], r["in_place_adjustment"]) for r in rl["regions"]) != \ + sorted((comps[i][0][0], q[comps[i][0]], len(comps[i]), dr.get(i, 0)) for i in range(len(comps)) + if relay_comp[i]): + errors.append("region in-place adjustments differ from the final layers") + got_u = sorted((units[i][0][0], d, len(units[i])) for i, d in du.items() if d) + if got_u != sorted((u["layer_before"], u["shift"], u["tiles"]) for u in rl["units_moved"]): + errors.append("moved units differ from the final layers") + # column order, caves underground, nothing lands on the surface + cols = collections.defaultdict(list) + for k, v in leg.items(): + cols[(k[1], k[2])].append((k[0], min(v[1]), max(v[1]))) + for (x, y), lst in cols.items(): + lst.sort() + for (la, _mna, mxa), (lb, mnb, _mxb) in zip(lst, lst[1:]): + if not mxa < mnb: + errors.append("column ({}, {}): layer {} ends at or above layer {}".format(x, y, la, lb)) + for k, v in leg.items(): + if k[0] < 0 and max(v[1]) >= 0: + errors.append("cave tile {} of {} reaches layer {}".format(k, v[0], max(v[1]))) + if main[k] != k[0] and main[k] == 0: + errors.append("moved tile {} of {} ends on the surface layer".format(k, v[0])) + # maps moved + mm = collections.defaultdict(collections.Counter) + moved_maps = set() + for k, v in leg.items(): + mm[v[0]][main[k]] += 1 + if main[k] != k[0]: + moved_maps.add(v[0]) + exp_maps = sorted(moved_maps) + if exp_maps != sorted(m["path"] for m in rl["maps_moved"]): + errors.append("moved maps differ from the final layers") + for m in rl["maps_moved"]: + if {str(L): n for L, n in sorted(mm[m["path"]].items())} != m["final_layers"]: + errors.append("final layers of {} differ".format(m["path"])) - # ----------------------------------------------------- placement status - # A failed placement must not be published; every place group is placed - # (proposed) and every uncertain group tentatively. The summary's order and - # unplaced lists are re-derived from the placement records. - others = [g for g in plan["groups"] if g["classification"] != "overworld"] - reasons = {"overworld": {"overworld"}, "place": {"place"}, "uncertain": {"uncertain"}, - "separate": {"guild", "other_world", "per_player", "ship", "system", "unreachable"}} - for g in plan["groups"]: - if g.get("reason") not in reasons.get(g["classification"], ()): - errors.append("group {} has classification {} with reason {}".format( - g["id"], g["classification"], g.get("reason"))) - if sum(1 for g in plan["groups"] if g["classification"] == "overworld") != 1: - errors.append("the plan has no single overworld group") - # identities are unique before anything is counted - for what, items in ( - ("group id", [g["id"] for g in plan["groups"]]), - ("member map", [m["path"] for g in others for m in g["maps"]] + [ - "{}_{}_{}{}".format(ow_id, X, Y, "" if int(z) == 0 else "_{}".format(z)) - for z, cells in ow["cells_by_layer"].items() for X, Y in cells])): - dup = sorted(k for k, n in collections.Counter(items).items() if n > 1) - if dup: - errors.append("{} {} {} listed {} times".format(what, dup[0], "and {} more are".format(len(dup) - 1) - if len(dup) > 1 else "is", "several")) - if summ.get("groups_total") != len(plan["groups"]) or summ.get("non_overworld_groups") != len(others) or \ - summ.get("maps_total") != sum(g["map_count"] for g in plan["groups"]) or \ - any(g["map_count"] != len(g["maps"]) for g in others) or \ - ow["map_count"] != sum(len(c) for c in ow["cells_by_layer"].values()) or \ - ow.get("layers") != {z: len(c) for z, c in sorted(ow["cells_by_layer"].items(), key=lambda kv: int(kv[0]))} or \ - summ.get("by_classification") != dict(sorted(collections.Counter( - "{}:{}".format(g["classification"], g["reason"]) for g in others).items())): - errors.append("summary group and map counts differ from the groups") - status = {g["id"]: (g.get("placement") or {}).get("status") for g in others} - for g in others: - gid, s = g["id"], status[g["id"]] - if g.get("placement") is not None and s is None: - errors.append("placement of {} has no status".format(gid)) - if s == "failed": - errors.append("placement of {} failed; a plan with a failed group must not be published".format(gid)) - elif s not in (None, "proposed", "tentative"): - errors.append("placement of {} has an unknown status {}".format(gid, s)) - want = {"place": "proposed", "uncertain": "tentative"}.get(g["classification"]) - if want is not None and s != want: - errors.append("{} group {} has no {} placement".format(g["classification"], gid, want)) - if s == "proposed" and g["classification"] != "place": - errors.append("{} group {} has a proposed placement".format(g["classification"], gid)) - # tentative: an uncertain group, or an unreachable group placed only by an alignment hint - if s == "tentative" and g["classification"] != "uncertain" and not ( - g["classification"] == "separate" and g["reason"] == "unreachable" - and g["placement"].get("tentative_reason")): - errors.append("{} group {} ({}) has a tentative placement without an alignment hint".format( - g["classification"], gid, g["reason"])) - index = {g["id"]: g["placement"].get("placement_index") for g in placed} - order = summ["placement_order"] + summ["tentative_placement_order"] - if sorted(order) != sorted(index) or [index[gid] for gid in order] != list(range(len(order))) or \ - sorted(summ["placement_order"]) != sorted(gid for gid, s in status.items() if s == "proposed"): - errors.append("placement order in the summary differs from the placed groups") - for key, cls, s_ok in (("unplaced_place_groups", "place", "proposed"), - ("unplaced_uncertain_groups", "uncertain", "tentative")): - want = sorted(g["id"] for g in others if g["classification"] == cls and status[g["id"]] != s_ok) - if summ.get(key) != want: - errors.append("summary {} {} != {}".format(key, summ.get(key), want)) - if summ.get("failed_placements") != sorted(gid for gid, s in status.items() if s == "failed"): - errors.append("summary failed_placements differs from the failed placement records") + def walkable(a, b): + return a in heights and b in heights and a not in blocked and b not in blocked and \ + abs(heights[a] - heights[b]) < WDZ - # rows coverage near every placed or stairwell tile - used_cells = collections.defaultdict(set) - for g in placed: - for z, by in owners.items(): - rr = by.get(g["id"]) - if rr is not None: - used_cells[z].update((x // 24, y // 24) for x, y in rr.tiles()) - for sw in plan["stairwells"]: - for t in sw["tiles"][1:-1]: - used_cells[t["layer"]].add((t["x"] // 24, t["y"] // 24)) - prefix = ow_id + "_" - for z, cells in sorted(used_cells.items()): - have = {tuple(c) for c in ow["cells_by_layer"].get(str(z), [])} - for cx, cy in sorted(cells): + def rest(k): + return heights[k] - HZ * q[k] if k in relayered else heights[k] + + moved = sorted(k for k in leg if main[k] != k[0]) + moved_set = set(moved) + cut = set() + for k in moved: + for dx, dy in N8: + n = (k[0], k[1] + dx, k[2] + dy) + if n in leg and walkable(k, n) and main[k] != main[n]: + a, b = min(k, n), max(k, n) + cut.add((a, b) if main[a] < main[b] else (b, a)) + edges = sorted(cut) + parent = list(range(len(edges))) + + def find(i): + while parent[i] != i: + parent[i] = parent[parent[i]] + i = parent[i] + return i + + lay = [(main[lo], main[hi]) for lo, hi in edges] + for side in (0, 1): + at = collections.defaultdict(list) + for i, e in enumerate(edges): + at[(lay[i], e[side][1], e[side][2])].append(i) + for i, e in enumerate(edges): for dx in (-1, 0, 1): for dy in (-1, 0, 1): - if (cx + dx, cy + dy) in have: - path = "{}{}_{}{}".format(prefix, cx + dx, cy + dy, "" if z == 0 else "_{}".format(z)) - if path not in ow_rows: # an included map carries every one of its final layers - errors.append("rows missing for {} near used layer {}".format(path, z)) - for mm in rl["maps_moved"]: - if mm["path"] not in ow_rows: - errors.append("rows missing for relayered map {}".format(mm["path"])) - stacked = set() - for z, cells in ow["cells_by_layer"].items(): - if int(z) != 0: - stacked.update(tuple(c) for c in cells) - for z, cells in ow["cells_by_layer"].items(): - for X, Y in cells: - path = "{}{}_{}{}".format(prefix, X, Y, "" if int(z) == 0 else "_{}".format(z)) - if (int(z) != 0 or (X, Y) in stacked) and path not in ow_rows: - errors.append("rows missing for {} (off the surface or stacked)".format(path)) + for j in at.get((lay[i], e[side][1] + dx, e[side][2] + dy), ()): + a, b = find(i), find(j) + if a != b: + parent[max(a, b)] = min(a, b) + seams = collections.defaultdict(list) + for i in range(len(edges)): + seams[find(i)].append(i) + cell = rl.get("seam_cell", 24) + pieces = {} + for idx in seams.values(): + sid = min(edges[i] for i in idx) + for i in idx: + lo = edges[i][0] + pieces.setdefault((sid, lo[1] // cell, lo[2] // cell), set()).add(edges[i]) + if len(seams) != len(rl["seams"]) or sum(s["cut_edges"] for s in rl["seams"]) != len(edges): + errors.append("seams differ: {} seams with {} cut edges recomputed, {} with {} recorded".format( + len(seams), len(edges), len(rl["seams"]), sum(s["cut_edges"] for s in rl["seams"]))) + piece_by_edge = {} + for key, es in pieces.items(): + for e in es: + piece_by_edge[e] = key + seen_pieces = set() + ns_by_id = {n["id"]: n for n in rl["new_stairs"]} + for s_ in rl["seams"]: + for pc in s_["pieces"]: + e = (tuple(pc["low_tile"]), tuple(pc["high_tile"])) + key = piece_by_edge.get(e) + if key is None: + errors.append("seam piece edge {} of {} is not a cut walkable edge".format(e, s_["id"])) + continue + if key in seen_pieces or len(pieces[key]) != pc["edges"] or [key[1], key[2]] != pc["cell"]: + errors.append("seam piece {} of {} does not match the recomputed piece".format(pc["cell"], s_["id"])) + seen_pieces.add(key) + if pc["continuity"] == "new stairs" and pc["stairs"] and pc["stairs"][0] in ns_by_id: + first = ns_by_id[pc["stairs"][0]] + if "exit" not in pc and (first["low_tile_legacy"], first["high_tile_legacy"]) != \ + (pc["low_tile"], pc["high_tile"]): + errors.append("seam piece {} of {} does not put its first stairs on its representative edge".format( + pc["cell"], s_["id"])) + elif pc["continuity"] != "existing exit" or tuple(pc.get("exit", ())) not in record_keys: + errors.append("seam piece {} of {} has no continuity".format(pc["cell"], s_["id"])) + for sid in pc["stairs"]: + n = ns_by_id.get(sid) + objs = {o.get("tile"): o for o in (n or {}).get("objects", [])} + lo_o, hi_o = objs.get("low", {}), objs.get("high", {}) + if n is None or len(n["objects"]) != 2 or not ( + lo_o.get("exit") is True and lo_o.get("exit_to") == "high" + and re.match(r"^(stairs|ladder)_.*up\d*$", lo_o.get("archetype", "")) + and hi_o.get("exit") is True and hi_o.get("exit_to") == "low" + and re.match(r"^(stairs|ladder)_.*down\d*$", hi_o.get("archetype", ""))): + errors.append("new stairs {} are not an up stair on the low tile and a down stair on the high " + "tile targeting each other".format(sid)) + continue + lo, hi = (tuple(n["low_tile_legacy"]), tuple(n["high_tile_legacy"])) if n else (None, None) + if n is None or piece_by_edge.get((lo, hi)) != key or n["low"] != [lo[1], lo[2], main[lo]] \ + or n["high"] != [hi[1], hi[2], main[hi]] or n["high"][2] - n["low"][2] != 1 \ + or cheb(n["low"], n["high"]) > tol: + errors.append("new stairs {} of seam piece {} of {} are not plain stairs across one of its " + "cut edges".format(sid, pc["cell"], s_["id"])) + if len(seen_pieces) != len(pieces): + errors.append("{} seam pieces recomputed, {} recorded".format(len(pieces), len(seen_pieces))) + if sorted(n["id"] for n in rl["new_stairs"]) != sorted(i for s_ in rl["seams"] for pc in s_["pieces"] + for i in pc["stairs"]): + errors.append("new stairs and seam pieces do not list the same stairs") + # continuity: the two tiles of every cut edge stay connected on the final layers + # (walking, legacy overworld exits, new stairs) within the maps with tile heights + hmaps = set(rl["tile_heights"]) + parent = {} - # ---------------------------------------------------- final overworld layer - void_ends = {(v["map"], tuple(v["tile"])): v["layer"] for v in rl["exit_ends_on_void_tiles"]} + def uf_find(a): + parent.setdefault(a, a) + while parent[a] != a: + parent[a] = parent[parent[a]] + a = parent[a] + return a - def ow_layer(path, tx, ty): - X, Y, Z = world_cell(path) - k = (Z, 24 * X + tx, 24 * Y + ty) - if k in leg: - return max(leg[k][1]) - return void_ends.get((path, (tx, ty)), Z) + def uf_union(a, b): + ra, rb = uf_find(a), uf_find(b) + if ra != rb: + parent[rb] = ra - # ------------------------------------------------------ related owners - def end_of(e, side): - g = e["source_group"] if side == "s" else e["target_group"] - m = e["source_map"] if side == "s" else e["target_map"] - t = e["source_tile"] if side == "s" else e["target_tile"] - if g == ow_id: - return m, ow_layer(m, *t) - return g, None + gpos = collections.defaultdict(list) + for k, v in leg.items(): + if v[0] in hmaps and k in heights and k not in blocked: + gpos[(main[k], k[1], k[2])].append((k, rest(k))) + if k in derived_split: + gpos[(derived_split[k], k[1], k[2])].append((("ground",) + k, ground[k])) + for (L, x, y), here in gpos.items(): + for dx, dy in ((1, 0), (-1, 1), (0, 1), (1, 1)): + for a, ra in here: + for b, rb in gpos.get((L, x + dx, y + dy), ()): + if abs(ra - rb) < WDZ: + uf_union(a, b) + for r in internal_exits: + X, Y, Z = world_cell(r["source_map"]) + X2, Y2, Z2 = world_cell(r["target_map"]) + uf_union((Z, 24 * X + r["source_tile"][0], 24 * Y + r["source_tile"][1]), + (Z2, 24 * X2 + r["target_tile"][0], 24 * Y2 + r["target_tile"][1])) + for n in rl["new_stairs"]: + uf_union(tuple(n["low_tile_legacy"]), tuple(n["high_tile_legacy"])) + apart = [e for e in edges if uf_find(e[0]) != uf_find(e[1])] + if apart: + errors.append("{} cut walkable edges are left without continuity, e.g. {}".format(len(apart), apart[0])) + steep = set() + for k in sorted(set(moved) | relayered): + for dx, dy in N8: + n = (k[0], k[1] + dx, k[2] + dy) + if n in leg and walkable(k, n) and main[k] == main[n] and abs(rest(k) - rest(n)) >= WDZ: + steep.add((min(k, n), max(k, n))) + rec_steep = {tuple(sorted(((s_["layer"], *s_["tiles"][0]), (s_["layer"], *s_["tiles"][1])))) for s_ in rl["steep_steps"]} + if steep != rec_steep: + errors.append("steep steps differ: {} recomputed, {} recorded".format(len(steep), len(rec_steep))) + finals = collections.defaultdict(list) + for k, v in leg.items(): + for L in v[1]: + finals[(L, k[1], k[2])].append(k) + contacts = set() + for k in moved: + for dx, dy in N8: + for t in finals.get((main[k], k[1] + dx, k[2] + dy), ()): + if t[0] != k[0]: + contacts.add((min(k, t), max(k, t))) + if contacts != {(tuple(c["tiles"][0]), tuple(c["tiles"][1])) for c in rl["new_contacts"]}: + errors.append("new contacts differ: {} recomputed, {} recorded".format(len(contacts), len(rl["new_contacts"]))) + for c in rl["new_contacts"]: + a, b = tuple(c["tiles"][0]), tuple(c["tiles"][1]) + walk = a in heights and b in heights and a not in blocked and b not in blocked and \ + abs(rest(a) - rest(b)) < WDZ + if c["walkable"] != walk or c["final_layer"] != main.get(a if a in moved_set else b): + errors.append("new contact {} / {} records walkable {} on layer {}; recomputed {}".format( + a, b, c["walkable"], c["final_layer"], walk)) + return errors, main, {"tile_heights": len(heights), "regions": len(got_r), "kept_regions": len(got_k), + "units": len(units), "moved_units": len(got_u), "moved_tiles": len(moved), + "cut_edges": len(edges), "seam_pieces": len(pieces), "steep_steps": len(steep), + "new_contacts": len(contacts)} - rel = collections.defaultdict(set) # owner -> {(owner, layer or None)} - for g in placed: - for key in ("entrances_from_overworld", "exits_to_overworld", "entrances_from_other_groups", - "exits_to_other_groups"): - for e in g.get(key, []): - a, b = end_of(e, "s"), end_of(e, "t") - rel[a[0]].add(b) - rel[b[0]].add(a) - for g in placed: - got = rel[g["id"]] - if sorted({o for o, _L in got}) != g.get("related_owners"): - errors.append("related owners of {} differ from the exit records".format(g["id"])) - lay = collections.defaultdict(set) - for o, L in got: - if L is not None: - lay[o].add(L) - if {m: sorted(v) for m, v in sorted(lay.items())} != g.get("related_overworld_layers"): - errors.append("related overworld layers of {} differ from the exit records".format(g["id"])) - group_ids = {g["id"] for g in plan["groups"] if g["classification"] != "overworld"} - def related(a, b, z): - """a is a placed group; b an owner on layer z.""" - if b in group_ids: - return (b, None) in rel[a] or (a, None) in rel[b] - return (b, z) in rel[a] +def verify(plan: dict) -> dict: + """The JSON-only check. A malformed plan (a missing key, a value of the wrong shape) fails it with an + error rather than an exception.""" + try: + return _verify(plan) + except Exception as exc: # noqa: BLE001 - any malformation is a failed check, not a crash + return {"ok": False, "errors": ["the plan is malformed: {}: {}".format(type(exc).__name__, exc)]} - # ------------------------------------------------------------ gap rule - def min_distance(a, b, cap): - small, big = (a, b) if a.count <= b.count else (b, a) - def hit(r): - return any(big.any_within(x, y, r) for (x, y) in small.tiles()) +def _verify(plan: dict) -> dict: + if plan.get("schema") != SCHEMA: + return {"ok": False, "errors": ["schema {} is not {}, the shape this verifier checks".format( + plan.get("schema"), SCHEMA)]} + errors = [] + bad_numbers = number_type_errors(plan) + if bad_numbers: + errors.append("the plan has booleans or floats where integers belong, e.g. {}".format(", ".join(bad_numbers))) + st = plan["settings"] + R, M, tol = st["view_radius_tiles"], st["contact_margin_tiles"], st["stair_tolerance_tiles"] + bend = st["stairwell_max_bend_tiles"] + HZ, MINR, WDZ = st["layer_height_z"], st["relayer_min_region_tiles"], st["walkable_dz"] + ow = next(g for g in plan["groups"] if g["classification"] == "overworld") + ow_id = ow["id"] + rl = plan["relayering"] - if not hit(cap): - return cap + 1 - lo, hi = 0, cap + # ------------------------------------------------------------ occupancy + owners = collections.defaultdict(dict) # layer -> owner -> Rows + claim = collections.defaultdict(dict) # layer -> (x, y) -> (owner, map) + origin = {} + leg = {} # (legacy layer, x, y) -> [map, final layers] + + def put(owner, mapname, layer, x0, y0, rows): + """Claim the '#' tiles of `rows` (at x0, y0) on `layer` for owner; return them in plane coordinates.""" + rr, here, me, out = owners[layer].setdefault(owner, Rows()), claim[layer], (owner, mapname), [] + for y, row in enumerate(rows): + x = row.find("#") + while x >= 0: + t = (x0 + x, y0 + y) + prev = here.get(t) + if prev is not None and prev != me: + errors.append("tile {} layer {} claimed by {} and {}".format(t, layer, prev[1], mapname)) + here[t] = me + rr.add(t[0], t[1]) + out.append(t) + x = row.find("#", x + 1) + return out + + ow_rows = ow["occupied_rows_by_layer"] + if st.get("overworld_map_size") != OW_SIZE: + errors.append("settings.overworld_map_size {} is not the {}-tile grid of the overworld coordinates".format( + st.get("overworld_map_size"), OW_SIZE)) + for path, by_layer in sorted(ow_rows.items()): + X, Y, Z = world_cell(path) + for Ls, rows in sorted(by_layer.items(), key=lambda kv: int(kv[0])): + if len(rows) != OW_SIZE or any(len(r) != OW_SIZE for r in rows): + errors.append("rows of {} on layer {} are not {} x {}".format(path, Ls, OW_SIZE, OW_SIZE)) + L = int(Ls) + for t in put(path, path, L, 24 * X, 24 * Y, rows): + leg.setdefault((Z,) + t, [path, []])[1].append(L) + placed = [] + for g in plan["groups"]: + p = g.get("placement") + if not p or p.get("status") not in ("proposed", "tentative"): + continue + placed.append(g) + for m in g["maps"]: + lo = m["local_offset"] + exp = [lo[0] + p["x_offset"], lo[1] + p["y_offset"], lo[2] + p["layer_offset"]] + got = p["member_origins"][m["path"]] + if exp != got: + errors.append("origin mismatch {} {} != {}".format(m["path"], got, exp)) + origin[m["path"]] = got + if "occupied_rows" not in m: + errors.append("no occupied rows for placed map {}".format(m["path"])) + continue + put(g["id"], m["path"], got[2], got[0], got[1], m["occupied_rows"]) + for layer in owners.values(): + for rr in layer.values(): + rr.done() + placed_ids = {g["id"] for g in placed} + summ = plan["summary"] + + # ----------------------------------------------------- placement status + # A failed placement must not be published; every place group is placed + # (proposed) and every uncertain group tentatively. The summary's order and + # unplaced lists are re-derived from the placement records. + others = [g for g in plan["groups"] if g["classification"] != "overworld"] + reasons = {"overworld": {"overworld"}, "place": {"place"}, "uncertain": {"uncertain"}, + "separate": {"guild", "other_world", "per_player", "ship", "system", "unreachable"}} + for g in plan["groups"]: + if g.get("reason") not in reasons.get(g["classification"], ()): + errors.append("group {} has classification {} with reason {}".format( + g["id"], g["classification"], g.get("reason"))) + if sum(1 for g in plan["groups"] if g["classification"] == "overworld") != 1: + errors.append("the plan has no single overworld group") + # identities are unique before anything is counted + for what, items in ( + ("group id", [g["id"] for g in plan["groups"]]), + ("member map", [m["path"] for g in others for m in g["maps"]] + [ + "{}_{}_{}{}".format(ow_id, X, Y, "" if int(z) == 0 else "_{}".format(z)) + for z, cells in ow["cells_by_layer"].items() for X, Y in cells])): + dup = sorted(k for k, n in collections.Counter(items).items() if n > 1) + if dup: + errors.append("{} {} {} listed {} times".format(what, dup[0], "and {} more are".format(len(dup) - 1) + if len(dup) > 1 else "is", "several")) + if summ.get("groups_total") != len(plan["groups"]) or summ.get("non_overworld_groups") != len(others) or \ + summ.get("maps_total") != sum(g["map_count"] for g in plan["groups"]) or \ + any(g["map_count"] != len(g["maps"]) for g in others) or \ + ow["map_count"] != sum(len(c) for c in ow["cells_by_layer"].values()) or \ + ow.get("layers") != {z: len(c) for z, c in sorted(ow["cells_by_layer"].items(), key=lambda kv: int(kv[0]))} or \ + summ.get("by_classification") != dict(sorted(collections.Counter( + "{}:{}".format(g["classification"], g["reason"]) for g in others).items())): + errors.append("summary group and map counts differ from the groups") + status = {g["id"]: (g.get("placement") or {}).get("status") for g in others} + for g in others: + gid, s = g["id"], status[g["id"]] + if g.get("placement") is not None and s is None: + errors.append("placement of {} has no status".format(gid)) + if s == "failed": + errors.append("placement of {} failed; a plan with a failed group must not be published".format(gid)) + elif s not in (None, "proposed", "tentative"): + errors.append("placement of {} has an unknown status {}".format(gid, s)) + want = {"place": "proposed", "uncertain": "tentative"}.get(g["classification"]) + if want is not None and s != want: + errors.append("{} group {} has no {} placement".format(g["classification"], gid, want)) + if s == "proposed" and g["classification"] != "place": + errors.append("{} group {} has a proposed placement".format(g["classification"], gid)) + # tentative: an uncertain group, or an unreachable group placed only by an alignment hint + if s == "tentative" and g["classification"] != "uncertain" and not ( + g["classification"] == "separate" and g["reason"] == "unreachable" + and g["placement"].get("tentative_reason")): + errors.append("{} group {} ({}) has a tentative placement without an alignment hint".format( + g["classification"], gid, g["reason"])) + index = {g["id"]: g["placement"].get("placement_index") for g in placed} + order = summ["placement_order"] + summ["tentative_placement_order"] + if sorted(order) != sorted(index) or [index[gid] for gid in order] != list(range(len(order))) or \ + sorted(summ["placement_order"]) != sorted(gid for gid, s in status.items() if s == "proposed"): + errors.append("placement order in the summary differs from the placed groups") + for key, cls, s_ok in (("unplaced_place_groups", "place", "proposed"), + ("unplaced_uncertain_groups", "uncertain", "tentative")): + want = sorted(g["id"] for g in others if g["classification"] == cls and status[g["id"]] != s_ok) + if summ.get(key) != want: + errors.append("summary {} {} != {}".format(key, summ.get(key), want)) + if summ.get("failed_placements") != sorted(gid for gid, s in status.items() if s == "failed"): + errors.append("summary failed_placements differs from the failed placement records") + + # rows coverage near every placed or stairwell tile + used_cells = collections.defaultdict(set) + for g in placed: + for z, by in owners.items(): + rr = by.get(g["id"]) + if rr is not None: + used_cells[z].update((x // 24, y // 24) for x, y in rr.tiles()) + for sw in plan["stairwells"]: + for t in sw["tiles"][1:-1]: + used_cells[t["layer"]].add((t["x"] // 24, t["y"] // 24)) + prefix = ow_id + "_" + for z, cells in sorted(used_cells.items()): + have = {tuple(c) for c in ow["cells_by_layer"].get(str(z), [])} + for cx, cy in sorted(cells): + for dx in (-1, 0, 1): + for dy in (-1, 0, 1): + if (cx + dx, cy + dy) in have: + path = "{}{}_{}{}".format(prefix, cx + dx, cy + dy, "" if z == 0 else "_{}".format(z)) + if path not in ow_rows: # an included map carries every one of its final layers + errors.append("rows missing for {} near used layer {}".format(path, z)) + for mm in rl["maps_moved"]: + if mm["path"] not in ow_rows: + errors.append("rows missing for relayered map {}".format(mm["path"])) + stacked = set() + for z, cells in ow["cells_by_layer"].items(): + if int(z) != 0: + stacked.update(tuple(c) for c in cells) + for z, cells in ow["cells_by_layer"].items(): + for X, Y in cells: + path = "{}{}_{}{}".format(prefix, X, Y, "" if int(z) == 0 else "_{}".format(z)) + if (int(z) != 0 or (X, Y) in stacked) and path not in ow_rows: + errors.append("rows missing for {} (off the surface or stacked)".format(path)) + + # ---------------------------------------------------- final overworld layer + void_ends = {(v["map"], tuple(v["tile"])): v["layer"] for v in rl["exit_ends_on_void_tiles"]} + + def ow_layer(path, tx, ty): + X, Y, Z = world_cell(path) + k = (Z, 24 * X + tx, 24 * Y + ty) + if k in leg: + return max(leg[k][1]) + return void_ends.get((path, (tx, ty)), Z) + + # ------------------------------------------------------ related owners + def end_of(e, side): + g = e["source_group"] if side == "s" else e["target_group"] + m = e["source_map"] if side == "s" else e["target_map"] + t = e["source_tile"] if side == "s" else e["target_tile"] + if g == ow_id: + return m, ow_layer(m, *t) + return g, None + + rel = collections.defaultdict(set) # owner -> {(owner, layer or None)} + for g in placed: + for key in ("entrances_from_overworld", "exits_to_overworld", "entrances_from_other_groups", + "exits_to_other_groups"): + for e in g.get(key, []): + a, b = end_of(e, "s"), end_of(e, "t") + rel[a[0]].add(b) + rel[b[0]].add(a) + for g in placed: + got = rel[g["id"]] + if sorted({o for o, _L in got}) != g.get("related_owners"): + errors.append("related owners of {} differ from the exit records".format(g["id"])) + lay = collections.defaultdict(set) + for o, L in got: + if L is not None: + lay[o].add(L) + if {m: sorted(v) for m, v in sorted(lay.items())} != g.get("related_overworld_layers"): + errors.append("related overworld layers of {} differ from the exit records".format(g["id"])) + group_ids = {g["id"] for g in plan["groups"] if g["classification"] != "overworld"} + + def related(a, b, z): + """a is a placed group; b an owner on layer z.""" + if b in group_ids: + return (b, None) in rel[a] or (a, None) in rel[b] + return (b, z) in rel[a] + + # ------------------------------------------------------------ gap rule + def min_distance(a, b, cap): + small, big = (a, b) if a.count <= b.count else (b, a) + + def hit(r): + return any(big.any_within(x, y, r) for (x, y) in small.tiles()) + + if not hit(cap): + return cap + 1 + lo, hi = 0, cap while lo < hi: mid = (lo + hi) // 2 if hit(mid): @@ -1264,302 +1608,11 @@ def chain_exists(top, bottom, shape): errors.append("blocked {} stairwell site at {} has a void chain after all".format(shape, b["top"])) # ----------------------------------------------------------- relayering - heights, blocked, ground = {}, set(), {} - for path, rows in sorted(rl["tile_heights"].items()): - X, Y, Z = world_cell(path) - for y, row in enumerate(rows): - for x, tok in enumerate(row.split(" ")): - if tok == ".": - continue - k = (Z, 24 * X + x, 24 * Y + y) - if tok.endswith("b"): - blocked.add(k) - tok = tok[:-1] - if "/" in tok: - tok, g = tok.split("/") - ground[k] = int(g) - heights[k] = int(tok) - split = {} - for sp in rl["split_tiles"]: - X, Y, Z = world_cell(sp["map"]) - split[(Z, 24 * X + sp["tile"][0], 24 * Y + sp["tile"][1])] = sp - # a split tile's raised part is its upper layer; which tiles split is derived below - main = {k: max(v[1]) for k, v in leg.items()} - # regions - q = {k: h // HZ for k, h in heights.items() if h >= HZ} - comps = flood(q, lambda a, b: q[a] == q[b]) - comps.sort(key=lambda c: (-len(c), c[0])) - relay_comp = [len(c) >= MINR for c in comps] - comp_of = {k: i for i, c in enumerate(comps) for k in c} - changed = True - while changed: - changed = False - for i, c in enumerate(comps): - if relay_comp[i]: - continue - if any((k[0], k[1] + dx, k[2] + dy) in comp_of and relay_comp[comp_of[(k[0], k[1] + dx, k[2] + dy)]] - for k in c for dx, dy in N8): - relay_comp[i] = True - changed = True - relayered = {k for i, c in enumerate(comps) if relay_comp[i] for k in c} - got_r = sorted((c[0][0], q[c[0]], len(c)) for i, c in enumerate(comps) if relay_comp[i]) - got_k = sorted((c[0][0], q[c[0]], len(c)) for i, c in enumerate(comps) if not relay_comp[i]) - if got_r != sorted((r["layer_before"], r["q"], r["tiles"]) for r in rl["regions"]): - errors.append("relayered regions differ from the tile heights") - if got_k != sorted((r["layer_before"], r["q"], r["tiles"]) for r in rl["kept_regions"]): - errors.append("kept regions differ from the tile heights") - # units and shifts - units = [] - unit_of = {} - bylayer = collections.defaultdict(list) - for k in leg: - if k[0] != 0: - bylayer[k[0]].append(k) - for L in sorted(bylayer): - for c in flood(bylayer[L], lambda a, b: True): - for k in c: - unit_of[k] = len(units) - units.append(c) - du = {} - for i, c in enumerate(units): - offs = {main[k] - k[0] for k in c if k not in relayered} - if len(offs) > 1: - errors.append("unit with {} tiles on layer {} does not move as one: shifts {}".format( - len(c), c[0][0], sorted(offs))) - du[i] = min(offs) if offs else 0 - if du[i] > 0 and c[0][0] < 1 or du[i] < 0 and c[0][0] >= 0: - errors.append("unit on layer {} moves {} (storeys move only up, caves only down)".format(c[0][0], du[i])) - dr = {} - for k in sorted(relayered): - d = main[k] - k[0] - q[k] - (du[unit_of[k]] if k in unit_of else 0) - ci = comp_of[k] - if dr.setdefault(ci, d) != d: - errors.append("region tile {} adjustment {} differs from its region's {}".format(k, d, dr[ci])) - if d > 0: - errors.append("region tile {} is adjusted up by {}".format(k, d)) - for k in leg: - if k in relayered: - continue - if k[0] == 0 and main[k] != 0: - errors.append("surface tile {} of {} moved to layer {} without a region".format(k, leg[k][0], main[k])) - # split tiles: a relayered tile whose sub-layer 0 floor stays below its raised floor keeps - # that ground part on its unit's layer; derived from the tile heights, not the records - derived_split = {} - for k in relayered: - g = ground.get(k) - base_layer = k[0] + (du[unit_of[k]] if k in unit_of else 0) - if g is not None and g // HZ < q[k] and base_layer != main[k]: - derived_split[k] = base_layer - if set(derived_split) != set(split): - errors.append("split tiles differ: {} derived from the tile heights, {} recorded".format( - len(derived_split), len(split))) - for k, v in leg.items(): - want = sorted({derived_split[k], main[k]}) if k in derived_split else [main[k]] - if sorted(v[1]) != want: - errors.append("legacy tile {} of {} appears on layers {}, expected {}".format(k, v[0], sorted(v[1]), want)) - for k, sp in split.items(): - if k not in derived_split or sp["ground_layer"] != derived_split[k] or sp["upper_layer"] != main[k] or \ - sp["floors"].get("0") != ground.get(k): - errors.append("split tile {} is inconsistent".format(k)) - if sorted((r["layer_before"], r["q"], r["tiles"], r["in_place_adjustment"]) for r in rl["regions"]) != \ - sorted((comps[i][0][0], q[comps[i][0]], len(comps[i]), dr.get(i, 0)) for i in range(len(comps)) - if relay_comp[i]): - errors.append("region in-place adjustments differ from the final layers") - got_u = sorted((units[i][0][0], d, len(units[i])) for i, d in du.items() if d) - if got_u != sorted((u["layer_before"], u["shift"], u["tiles"]) for u in rl["units_moved"]): - errors.append("moved units differ from the final layers") - # column order, caves underground, nothing lands on the surface - cols = collections.defaultdict(list) - for k, v in leg.items(): - cols[(k[1], k[2])].append((k[0], min(v[1]), max(v[1]))) - for (x, y), lst in cols.items(): - lst.sort() - for (la, _mna, mxa), (lb, mnb, _mxb) in zip(lst, lst[1:]): - if not mxa < mnb: - errors.append("column ({}, {}): layer {} ends at or above layer {}".format(x, y, la, lb)) - for k, v in leg.items(): - if k[0] < 0 and max(v[1]) >= 0: - errors.append("cave tile {} of {} reaches layer {}".format(k, v[0], max(v[1]))) - if main[k] != k[0] and main[k] == 0: - errors.append("moved tile {} of {} ends on the surface layer".format(k, v[0])) - # maps moved - mm = collections.defaultdict(collections.Counter) - moved_maps = set() - for k, v in leg.items(): - mm[v[0]][main[k]] += 1 - if main[k] != k[0]: - moved_maps.add(v[0]) - exp_maps = sorted(moved_maps) - if exp_maps != sorted(m["path"] for m in rl["maps_moved"]): - errors.append("moved maps differ from the final layers") - for m in rl["maps_moved"]: - if {str(L): n for L, n in sorted(mm[m["path"]].items())} != m["final_layers"]: - errors.append("final layers of {} differ".format(m["path"])) - - def walkable(a, b): - return a in heights and b in heights and a not in blocked and b not in blocked and \ - abs(heights[a] - heights[b]) < WDZ - - def rest(k): - return heights[k] - HZ * q[k] if k in relayered else heights[k] - - moved = sorted(k for k in leg if main[k] != k[0]) - moved_set = set(moved) - cut = set() - for k in moved: - for dx, dy in N8: - n = (k[0], k[1] + dx, k[2] + dy) - if n in leg and walkable(k, n) and main[k] != main[n]: - a, b = min(k, n), max(k, n) - cut.add((a, b) if main[a] < main[b] else (b, a)) - edges = sorted(cut) - parent = list(range(len(edges))) - - def find(i): - while parent[i] != i: - parent[i] = parent[parent[i]] - i = parent[i] - return i - - lay = [(main[lo], main[hi]) for lo, hi in edges] - for side in (0, 1): - at = collections.defaultdict(list) - for i, e in enumerate(edges): - at[(lay[i], e[side][1], e[side][2])].append(i) - for i, e in enumerate(edges): - for dx in (-1, 0, 1): - for dy in (-1, 0, 1): - for j in at.get((lay[i], e[side][1] + dx, e[side][2] + dy), ()): - a, b = find(i), find(j) - if a != b: - parent[max(a, b)] = min(a, b) - seams = collections.defaultdict(list) - for i in range(len(edges)): - seams[find(i)].append(i) - cell = rl.get("seam_cell", 24) - pieces = {} - for idx in seams.values(): - sid = min(edges[i] for i in idx) - for i in idx: - lo = edges[i][0] - pieces.setdefault((sid, lo[1] // cell, lo[2] // cell), set()).add(edges[i]) - if len(seams) != len(rl["seams"]) or sum(s["cut_edges"] for s in rl["seams"]) != len(edges): - errors.append("seams differ: {} seams with {} cut edges recomputed, {} with {} recorded".format( - len(seams), len(edges), len(rl["seams"]), sum(s["cut_edges"] for s in rl["seams"]))) - piece_by_edge = {} - for key, es in pieces.items(): - for e in es: - piece_by_edge[e] = key - seen_pieces = set() - ns_by_id = {n["id"]: n for n in rl["new_stairs"]} - for s_ in rl["seams"]: - for pc in s_["pieces"]: - e = (tuple(pc["low_tile"]), tuple(pc["high_tile"])) - key = piece_by_edge.get(e) - if key is None: - errors.append("seam piece edge {} of {} is not a cut walkable edge".format(e, s_["id"])) - continue - if key in seen_pieces or len(pieces[key]) != pc["edges"] or [key[1], key[2]] != pc["cell"]: - errors.append("seam piece {} of {} does not match the recomputed piece".format(pc["cell"], s_["id"])) - seen_pieces.add(key) - if pc["continuity"] == "new stairs" and pc["stairs"] and pc["stairs"][0] in ns_by_id: - first = ns_by_id[pc["stairs"][0]] - if "exit" not in pc and (first["low_tile_legacy"], first["high_tile_legacy"]) != \ - (pc["low_tile"], pc["high_tile"]): - errors.append("seam piece {} of {} does not put its first stairs on its representative edge".format( - pc["cell"], s_["id"])) - elif pc["continuity"] != "existing exit" or tuple(pc.get("exit", ())) not in records: - errors.append("seam piece {} of {} has no continuity".format(pc["cell"], s_["id"])) - for sid in pc["stairs"]: - n = ns_by_id.get(sid) - objs = {o.get("tile"): o for o in (n or {}).get("objects", [])} - lo_o, hi_o = objs.get("low", {}), objs.get("high", {}) - if n is None or len(n["objects"]) != 2 or not ( - lo_o.get("exit") is True and lo_o.get("exit_to") == "high" - and re.match(r"^(stairs|ladder)_.*up\d*$", lo_o.get("archetype", "")) - and hi_o.get("exit") is True and hi_o.get("exit_to") == "low" - and re.match(r"^(stairs|ladder)_.*down\d*$", hi_o.get("archetype", ""))): - errors.append("new stairs {} are not an up stair on the low tile and a down stair on the high " - "tile targeting each other".format(sid)) - continue - lo, hi = (tuple(n["low_tile_legacy"]), tuple(n["high_tile_legacy"])) if n else (None, None) - if n is None or piece_by_edge.get((lo, hi)) != key or n["low"] != [lo[1], lo[2], main[lo]] \ - or n["high"] != [hi[1], hi[2], main[hi]] or n["high"][2] - n["low"][2] != 1 \ - or cheb(n["low"], n["high"]) > tol: - errors.append("new stairs {} of seam piece {} of {} are not plain stairs across one of its " - "cut edges".format(sid, pc["cell"], s_["id"])) - if len(seen_pieces) != len(pieces): - errors.append("{} seam pieces recomputed, {} recorded".format(len(pieces), len(seen_pieces))) - if sorted(n["id"] for n in rl["new_stairs"]) != sorted(i for s_ in rl["seams"] for pc in s_["pieces"] - for i in pc["stairs"]): - errors.append("new stairs and seam pieces do not list the same stairs") - # continuity: the two tiles of every cut edge stay connected on the final layers - # (walking, legacy overworld exits, new stairs) within the maps with tile heights - hmaps = set(rl["tile_heights"]) - parent = {} - - def uf_find(a): - parent.setdefault(a, a) - while parent[a] != a: - parent[a] = parent[parent[a]] - a = parent[a] - return a - - def uf_union(a, b): - ra, rb = uf_find(a), uf_find(b) - if ra != rb: - parent[rb] = ra - - gpos = collections.defaultdict(list) - for k, v in leg.items(): - if v[0] in hmaps and k in heights and k not in blocked: - gpos[(main[k], k[1], k[2])].append((k, rest(k))) - if k in derived_split: - gpos[(derived_split[k], k[1], k[2])].append((("ground",) + k, ground[k])) - for (L, x, y), here in gpos.items(): - for dx, dy in ((1, 0), (-1, 1), (0, 1), (1, 1)): - for a, ra in here: - for b, rb in gpos.get((L, x + dx, y + dy), ()): - if abs(ra - rb) < WDZ: - uf_union(a, b) - for r in ow.get("internal_exits", []): - X, Y, Z = world_cell(r["source_map"]) - X2, Y2, Z2 = world_cell(r["target_map"]) - uf_union((Z, 24 * X + r["source_tile"][0], 24 * Y + r["source_tile"][1]), - (Z2, 24 * X2 + r["target_tile"][0], 24 * Y2 + r["target_tile"][1])) - for n in rl["new_stairs"]: - uf_union(tuple(n["low_tile_legacy"]), tuple(n["high_tile_legacy"])) - apart = [e for e in edges if uf_find(e[0]) != uf_find(e[1])] - if apart: - errors.append("{} cut walkable edges are left without continuity, e.g. {}".format(len(apart), apart[0])) - steep = set() - for k in sorted(set(moved) | relayered): - for dx, dy in N8: - n = (k[0], k[1] + dx, k[2] + dy) - if n in leg and walkable(k, n) and main[k] == main[n] and abs(rest(k) - rest(n)) >= WDZ: - steep.add((min(k, n), max(k, n))) - rec_steep = {tuple(sorted(((s_["layer"], *s_["tiles"][0]), (s_["layer"], *s_["tiles"][1])))) for s_ in rl["steep_steps"]} - if steep != rec_steep: - errors.append("steep steps differ: {} recomputed, {} recorded".format(len(steep), len(rec_steep))) - finals = collections.defaultdict(list) - for k, v in leg.items(): - for L in v[1]: - finals[(L, k[1], k[2])].append(k) - contacts = set() - for k in moved: - for dx, dy in N8: - for t in finals.get((main[k], k[1] + dx, k[2] + dy), ()): - if t[0] != k[0]: - contacts.add((min(k, t), max(k, t))) - if contacts != {(tuple(c["tiles"][0]), tuple(c["tiles"][1])) for c in rl["new_contacts"]}: - errors.append("new contacts differ: {} recomputed, {} recorded".format(len(contacts), len(rl["new_contacts"]))) - for c in rl["new_contacts"]: - a, b = tuple(c["tiles"][0]), tuple(c["tiles"][1]) - walk = a in heights and b in heights and a not in blocked and b not in blocked and \ - abs(rest(a) - rest(b)) < WDZ - if c["walkable"] != walk or c["final_layer"] != main.get(a if a in moved_set else b): - errors.append("new contact {} / {} records walkable {} on layer {}; recomputed {}".format( - a, b, c["walkable"], c["final_layer"], walk)) + # the relayering looks up only the seam pieces' exits among the records: pass just their membership + seam_exits = {tuple(pc.get("exit", ())) for s_ in rl["seams"] for pc in s_["pieces"]} + rl_errors, main, rl_counts = _relayering(ow_rows, leg, rl, ow.get("internal_exits", []), + {k for k in seam_exits if k in records}, [HZ, MINR, WDZ, tol]) + errors.extend(rl_errors) # every exit with an overworld end on a moved tile must be listed, and nothing else def moved_end(g, mp, t): if g != ow_id: @@ -1606,10 +1659,7 @@ def moved_end(g, mp, t): "stairwell_exits_required": required, "stairwell_intermediate_tiles": intermediate, "exit_endpoints_checked": exits_checked, "exit_inventory_objects": len(inv), "exit_record_copies_checked": sum(copies.values()), - "relayering": {"overworld_tiles": len(leg), "tile_heights": len(heights), "regions": len(got_r), - "kept_regions": len(got_k), "units": len(units), "moved_units": len(got_u), - "moved_tiles": len(moved), "cut_edges": len(edges), "seam_pieces": len(pieces), - "steep_steps": len(steep), "new_contacts": len(contacts)}} + "relayering": dict({"overworld_tiles": len(leg)}, **rl_counts)} def same_json(a, b) -> bool: From 75641528cd89a5869c3ee1d896accb074cbd7f22 Mon Sep 17 00:00:00 2001 From: Zoey Rose Date: Sun, 18 Oct 2026 20:19:37 -0500 Subject: [PATCH 20/20] fix(placement-plan): count wired crossing stairs from the records Address the Codex review of 3d04cb7 on #47. - report.py lists every wired exit, including one wired between two overworld maps (internal_exits), which the Wired exits section used to omit. The crossing-stairs sentence counts only crossing wired exits that became stairs, not summary.wired_exits. A report test with such an exit fails on the old code. - verify() requires the plan to identify its input: a non-empty content_root, a 40-hex content_git_head, a boolean content_worktree_clean and a well-formed inputs digest. A plan that has lost its content_root no longer passes because --content ignores the value. The plan and REPORT.md are unchanged; --check passes. Co-Authored-By: Claude Opus 5.5 --- migration/placement-plan/scripts/report.py | 7 +++-- .../scripts/test_placement_plan.py | 29 +++++++++++++++++++ .../placement-plan/scripts/verify_plan.py | 13 +++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/migration/placement-plan/scripts/report.py b/migration/placement-plan/scripts/report.py index d165421..fe5dc9e 100644 --- a/migration/placement-plan/scripts/report.py +++ b/migration/placement-plan/scripts/report.py @@ -160,10 +160,12 @@ def write_report(plan, out: Path): sum(1 for c in rl["caves"] if c["choice"] == "lower-regions-in-place"), rs["maps_moved"], plain_kept, plain_well, rs["new_stairs"], rs["seams"])) c = s["crossing_exit_objects_after_placement"] + wired_crossing = sum(1 for e in wired_exits(plan) + if e["source_group"] != e["target_group"] and e.get("becomes") == "stairs") w("- Exit objects crossing group boundaries after placement (both directions, every exit object counted): " "**{} become plain stairs** ({} of them wired dangling exits), **{} are replaced by stairwells**, **{} stay " "portals**. Another {} exit objects lead into separate planes and stay portals by definition.".format( - c.get("stairs", 0), s["wired_exits"], c.get("stairwell", 0), c.get("portal", 0), + c.get("stairs", 0), wired_crossing, c.get("stairwell", 0), c.get("portal", 0), s["crossing_exit_objects_into_separate_planes"])) w("- **Stairwells: {}**: {} for placed groups (SW), {} sloped (SL), {} for overworld exits the relayering " "put 2 or more layers apart (SR). Every one is drawn only on the layer the player is on.".format( @@ -492,9 +494,10 @@ def placed_table(rows): def wired_exits(plan): + """Every wired exit: crossing ones and those wired between two overworld maps (internal_exits).""" out, seen = [], set() for g in plan["groups"]: - for _key, e in crossing(g): + for e in [e for _key, e in crossing(g)] + g.get("internal_exits", []): k = (e["source_map"], e["line"]) if e.get("form") == "wired" and k not in seen: seen.add(k) diff --git a/migration/placement-plan/scripts/test_placement_plan.py b/migration/placement-plan/scripts/test_placement_plan.py index 813eded..d09bb86 100644 --- a/migration/placement-plan/scripts/test_placement_plan.py +++ b/migration/placement-plan/scripts/test_placement_plan.py @@ -146,6 +146,15 @@ def counting(*a): verify_plan.relayering_rules = rules verify_plan._RELAYER_CACHE.clear() + def test_the_plan_identifies_its_input(self): + for change in (lambda p: p.pop("content_root"), lambda p: p.update(content_root=None), + lambda p: p.update(content_root=""), lambda p: p.update(content_git_head="3ad2015"), + lambda p: p.update(content_worktree_clean="yes"), + lambda p: p["content_inputs_digest"].update(sha256="x")): + p = self.plan() + change(p) + self.assertCaught(p, "the plan does not identify its input") + def test_another_schema(self): p = self.plan() p["schema"] = "atrinik-overworld-placement-plan/3" @@ -867,6 +876,26 @@ def test_type_strict_comparison(self): self.assertFalse(verify_plan.verify_content(p, self.content, self.fresh)["ok"]) +class Report(VerifierTest): + """The report counts wired exits from the records.""" + + def test_an_overworld_wired_exit_is_no_crossing_stairs(self): + import tempfile + + import report + + p = self.plan() + ow = next(g for g in p["groups"] if g["classification"] == "overworld") + wired = next(e for g in p["groups"] for key in INDEX for e in g.get(key, []) if e.get("form") == "wired") + ow["internal_exits"].append(dict(ow["internal_exits"][0], form="wired", wired=wired["wired"])) + p["summary"]["wired_exits"] += 1 # as the generator counts it: every wired exit + with tempfile.TemporaryDirectory() as tmp: + report.write_report(p, Path(tmp)) + text = (Path(tmp) / "REPORT.md").read_text(encoding="utf-8") + self.assertIn("**66 become plain stairs** (5 of them wired dangling exits)", text) + self.assertIn("**Wired exits**: 6 legacy exits", text) + + class PlacePool(unittest.TestCase): """Fix 3 in the generator: a failed group stays unplaced and nothing is placed through it.""" diff --git a/migration/placement-plan/scripts/verify_plan.py b/migration/placement-plan/scripts/verify_plan.py index 4997baa..acbdddd 100644 --- a/migration/placement-plan/scripts/verify_plan.py +++ b/migration/placement-plan/scripts/verify_plan.py @@ -22,6 +22,8 @@ verify(plan) checks: Values: + * the plan identifies its input: a content_root path, a 40-hex + content_git_head, content_worktree_clean, and the inputs digest * the plan's numbers are integers: no floats, and booleans only under the flag keys (BOOL_KEYS); the plan file is at most MAX_PLAN_BYTES Groups: @@ -627,6 +629,17 @@ def _verify(plan: dict) -> dict: return {"ok": False, "errors": ["schema {} is not {}, the shape this verifier checks".format( plan.get("schema"), SCHEMA)]} errors = [] + # what the plan was built from: the checkout path (shown in the report; --content ignores its value), the + # commit, whether the inputs were clean, and the digest of the input trees + dig = plan.get("content_inputs_digest") + if not (isinstance(plan.get("content_root"), str) and plan["content_root"] + and isinstance(plan.get("content_git_head"), str) and re.fullmatch(r"[0-9a-f]{40}", plan["content_git_head"]) + and type(plan.get("content_worktree_clean")) is bool + and isinstance(dig, dict) and isinstance(dig.get("trees"), list) and dig["trees"] + and all(isinstance(t, str) and t for t in dig["trees"]) and type(dig.get("files")) is int + and isinstance(dig.get("sha256"), str) and re.fullmatch(r"[0-9a-f]{64}", dig["sha256"])): + errors.append("the plan does not identify its input (content_root, content_git_head, content_worktree_clean, " + "content_inputs_digest)") bad_numbers = number_type_errors(plan) if bad_numbers: errors.append("the plan has booleans or floats where integers belong, e.g. {}".format(", ".join(bad_numbers)))