ap_map.c3622 lines · 142.6 KB · raw
1#include <string.h>
2#include "ap_map.h"
3#include "ap_item.h"
4#include "ap_math.h"
5#include "ap_macro.h"
6#include "ap_snes.h"
7#include "ap_plan.h"
8#include "pq.h"
9#include "pm.h"
10
11static struct ap_screen * map_screens[0x100 * 0x100];
12static bool map_screen_mask_x[0x100];
13static bool map_screen_mask_y[0x100];
14static volatile const struct ap_screen * last_screen = NULL;
15
16static struct ap_target {
17    struct xy tl;
18    uint16_t joypad;
19    uint16_t joypad_mask;
20} ap_targets[2048];
21static size_t ap_target_count = 0;
22static int ap_target_timeout = 0;
23static int ap_target_subtimeout = 0;
24static bool ap_target_scripted = false;
25static struct xy ap_target_dst_tl;
26static struct xy ap_target_dst_br;
27static uint8_t ap_target_sprite_index = 0;
28static struct ap_screen * ap_target_screen;
29
30const char * const ap_node_type_names[] = {
31#define X(type) [CONCAT(NODE_, type)] = #type,
32NODE_TYPE_LIST
33#undef X
34};
35
36static const struct ap_script ap_scripts[] = {
37    {
38        .start_tl = XY(0x9af8, 0x21c0),
39        .start_item = -1,
40        .sequence = "^^<^v>>^<^",
41        .type = SCRIPT_SEQUENCE,
42        .name = "Dam Chest Block Puzzle",
43    },
44    {
45        .start_tl = XY(0xaaf0, 0x2340),
46        .start_item = INVENTORY_BOMBS,
47        .sequence = "Yvvv>v<vv^^>>>vv<UA>^^<<<^<<vvv>vv>>>>>",
48        .type = SCRIPT_SEQUENCE,
49        .name = "Blind's House Block Puzzle",
50    },
51    {
52        .start_tl = XY(0x52f8, 0x0e60),
53        .start_item = -1,
54        .sequence = NULL,
55        .type = SCRIPT_KILLDROPS,
56        .name = "HC kill guard for key",
57    },
58    /*
59    {
60        .start_tl = XY(0x4878, 0x0fb0),
61        .start_item = -1,
62        .sequence = NULL,
63        .type = SCRIPT_KILLALL,
64        .name = "HC kill green guard for doors",
65    },
66    {
67        .start_tl = XY(0x4950, 0x0f80),
68        .start_item = -1,
69        .sequence = NULL,
70        .type = SCRIPT_KILLALL,
71        .name = "HC kill blue guard for door",
72    },
73    */
74    {
75        .start_tl = XY(0x4250, 0x1060),
76        .start_item = -1,
77        .sequence = NULL,
78        .type = SCRIPT_KILLDROPS,
79        .name = "HC kill miniboss free Zelda",
80    },
81    {
82        .start_tl = XY(0x4330, 0x1880),
83        .start_item = -1,
84        .sequence = NULL,
85        .type = SCRIPT_KILLDROPS,
86        .name = "AgTower small key",
87    },
88    {
89        .start_tl = XY(0x4278, 0x173C),
90        .start_item = -1,
91        .sequence = NULL,
92        .type = SCRIPT_KILLDROPS,
93        .name = "AgTower small key 2",
94    },
95    {
96        .start_tl = XY(0x4190, 0x0958),
97        .start_item = -1,
98        .sequence = "<<>",
99        .type = SCRIPT_SEQUENCE,
100        .name = "AgTower push statues",
101    },
102    {
103        .start_tl = XY(0x4278, 0x063D),
104        .start_item = -1,
105        .sequence = "UB><",
106        .type = SCRIPT_SEQUENCE,
107        .name = "AgTower open curtain",
108    },
109    {
110        .start_tl = XY(0x4278, 0x05C8),
111        .start_item = -1,
112        .sequence = NULL,
113        .type = SCRIPT_KILLALL,
114        .name = "AgTower boss",
115    },
116    {
117        .start_tl = XY(0x0058, 0x0681),
118        .start_item = -1,
119        .sequence = "DDDDDD",
120        .type = SCRIPT_SEQUENCE,
121        .name = "Jump into Kak well",
122    },
123    /*
124    {
125        .start_tl = XY(0x5a78, 0x25c0),
126        .start_item = -1,
127        .sequence = NULL,
128        .type = SCRIPT_KILLALL,
129        .name = "Mini Moldorm Cave",
130    },
131    */
132    /*
133    {
134        .start_tl = XY(0x8278, 0x1580),
135        .start_item = -1,
136        .sequence = NULL,
137        .type = SCRIPT_KILLALL,
138        .name = "EP Stalfos Room",
139    },
140    */
141    {
142        .start_tl = XY(0x8af8, 0x1390),
143        .start_item = -1,
144        .sequence = NULL,
145        .type = SCRIPT_KILLDROPS,
146        .name = "EP Igor Key",
147    },
148    {
149        .start_tl = XY(0x8378, 0x19B8),
150        .start_item = INVENTORY_BOW,
151        .sequence = NULL,
152        .type = SCRIPT_KILLALL,
153        .name = "EP Armos Knights Boss",
154    },
155    {
156        .start_tl = XY(0x83c0, 0x1b80),
157        .start_item = INVENTORY_BOW,
158        .sequence = NULL,
159        .type = SCRIPT_KILLALL,
160        .name = "EP Red Igor Room",
161    },
162    /* Need to bring old man to "door 0x30"
163    {
164        .start_tl = XY(0x4278, 0x1fc6),
165        .start_item = -1,
166        .sequence = "^^^^^^",
167        .type = SCRIPT_SEQUENCE,
168        .name = "Dark cave ledge jump",
169    },
170    */
171};
172
173static bool add_explore_goals_global = true;
174static const struct ap_screen_info {
175    uint16_t id;
176    char name[40];
177    bool add_explore_goals;
178    bool include_borders;
179} ap_screen_infos[] = {
180    // Light Overworld
181    { .id = 0x0402, .name = "North of Kak", },
182    { .id = 0x0406, .name = "Sanctuary Yard", },
183    { .id = 0x0408, .name = "Graveyard", },
184    { .id = 0x040c, .name = "Witches' Yard", },
185    { .id = 0x0606, .name = "Castle Yard", },
186    { .id = 0x060c, .name = "Ruins", },
187    { .id = 0x0600, .name = "Kak Village", .add_explore_goals = true, },
188    { .id = 0x0804, .name = "Smith's Yard", .add_explore_goals = true, },
189    { .id = 0x0a00, .name = "Maze Race" },
190    { .id = 0x0a02, .name = "Library Yard", .add_explore_goals = true, },
191    { .id = 0x0a04, .name = "Flute Grove", },
192    { .id = 0x0a08, .name = "Link's Yard", },
193    { .id = 0x0a68, .name = "Well Uncle", },
194    { .id = 0x0b68, .name = "Well Chest", },
195    { .id = 0x0c0a, .name = "Fortune Shore", },
196    // Overworld Interiors
197    { .id = 0x2161, .name = "Link's House", },
198    { .id = 0x2178, .name = "Library", },
199    { .id = 0x2188, .name = "Witches' Hut", },
200    { .id = 0x2198, .name = "Dam Chest", },
201    { .id = 0x2550, .name = "Fortune Teller", },
202    { .id = 0x1d58, .name = "Bat Cave Exit", },
203    { .id = 0x2548, .name = "Smiths' House", },
204    { .id = 0x1f68, .name = "Maze Race Entrance", },
205    { .id = 0x2388, .name = "Blind's House", .add_explore_goals = true, },
206    { .id = 0x23a8, .name = "Blind's Basement", .add_explore_goals = true, },
207    { .id = 0x22a8, .name = "Blind's Storage",},
208    { .id = 0x1e40, .name = "DM Dark Cave 1",},
209    { .id = 0x2369, .name = "Fairy Fountain; Stateful!!!",},
210    { .id = 0x2140, .name = "Master Sword Pedestal", },
211    // Hyrule Castle
212    { .id = 0x0c48, .name = "HC Entrance", },
213    { .id = 0x0e50, .name = "HC First Key", .add_explore_goals = true},
214    { .id = 0x0f50, .name = "HC Walkway", .include_borders = true, },
215    { .id = 0x0f48, .name = "HC Green Guard"},
216    { .id = 0x1040, .name = "HC Zelda Jail"},
217    { .id = 0x0650, .name = "Sewer Key Door" },
218    // Eastern Palace
219    { .id = 0x1988, .name = "EP Entrance", },
220    { .id = 0x1888, .name = "EP Hall 1", },
221    { .id = 0x1688, .name = "EP Dodgeball", },
222    { .id = 0x1488, .name = "EP Bigchest", },
223    { .id = 0x1490, .name = "EP Catwalk", },
224    { .id = 0x1491, .name = "EP 5 pots", },
225    { .id = 0x1591, .name = "EP Chest&Ledge", },
226    { .id = 0x1481, .name = "EP Crossover", },
227    { .id = 0x1580, .name = "EP Stalfos", },
228    { .id = -1 },
229};
230
231
232struct point_state {
233    // Parts that need to be cleared each time this ap_pathfind_local is called
234    struct point_state * from;
235    uint32_t gscore;
236    uint32_t fscore;
237
238    // Parts that can be cached for the same (screen, frame #)
239    struct xy xy;
240    uint16_t tile_attrs;
241    uint8_t raw_tile;
242    uint8_t ledge;
243    uint8_t corner;
244    uint32_t cost;
245};
246
247static int
248ap_pathfind_local(struct ap_screen * screen, struct xy start_xy, struct xy destination_tl, struct xy destination_br, bool commit);
249static void
250ap_print_pathfind_local_state(struct point_state *buf, struct xy grid);
251
252static void
253ap_map_add_sprite_nodes_to_screen(struct ap_screen * screen);
254
255static struct xy
256ap_box_edge(struct xy tl, struct xy br, int dir) {
257    // Return a point that is on the `dir` edge of the bounding box `tl, br`
258    struct xy dxy = dir_dxy[dir];
259    return XY(
260        ((dir_dx[dir] <= 0) ? tl.x : br.x),
261        ((dir_dy[dir] <= 0) ? tl.y : br.y));
262}
263
264struct xy
265ap_link_xy()
266{
267    // Returns the effective tl coordinate that link interacts with
268    // Which is 8 pixels below his head
269    struct xy link = XY(*ap_ram.link_x, *ap_ram.link_y + 8);
270    if (*ap_ram.in_building) {
271        link.x = ((link.x & ~0x1FF) << 2) | (link.x & 0x1FF);
272        link.x += 0x4000;
273        if (!*ap_ram.link_lower_level) {
274            link.x += 0x200;
275        }
276    } else if (*ap_ram.overworld_dark) {
277        link.y += 0x1000;
278    }
279    return link;
280}
281
282static void
283ap_map_room_bounds(struct xy base, struct xy * tl, struct xy * br) {
284    // Only works in current "room"
285    uint8_t layout = *ap_ram.room_layout;
286    struct xy size = XY(0x200, 0x200);
287    bool in_right = (base.x & 0x100) != 0;
288    bool in_bottom = (base.y & 0x100) != 0;
289    switch (layout >> 2) {
290    case ROOM_LAYOUT_A_B_C_D: size = XY(0x100, 0x100); break;
291    case ROOM_LAYOUT_AC_BD: size = XY(0x100, 0x200); break;
292    case ROOM_LAYOUT_AB_CD: size = XY(0x200, 0x100); break;
293    case ROOM_LAYOUT_ABCD: size = XY(0x200, 0x200); break;
294    case ROOM_LAYOUT_A_BD_C: size = XY(0x100, (in_right) ? 0x200 : 0x100); break;
295    case ROOM_LAYOUT_AC_B_D: size = XY(0x100, (in_right) ? 0x100 : 0x200); break;
296    case ROOM_LAYOUT_A_B_CD: size = XY((in_bottom) ? 0x200 : 0x100, 0x100); break;
297    case ROOM_LAYOUT_AB_C_D: size = XY((in_bottom) ? 0x100 : 0x200, 0x100); break;
298    default: assert_bp(false);
299    }
300    size = XYOP1(size, - 1);
301    struct xy mask = XY(~size.x, ~size.y);
302    *tl = XYOP2(base, &, mask);
303    *br = XYOP2(*tl, +, size);
304}
305
306void
307ap_map_bounds(struct xy * topleft, struct xy * bottomright)
308{
309    if (*ap_ram.in_building) {
310        struct xy link = ap_link_xy();
311        link.y = CAST16(link.y - 8);
312        ap_map_room_bounds(link, topleft, bottomright);
313    } else {
314        topleft->x = (uint16_t) (*ap_ram.map_x_offset * 8);
315        topleft->y = (uint16_t) (*ap_ram.map_y_offset);
316        bottomright->x = (uint16_t) (topleft->x + ((*ap_ram.map_x_mask * 8) | 0xF));
317        bottomright->y = (uint16_t) (topleft->y + (*ap_ram.map_y_mask | 0xF));
318        if (*ap_ram.overworld_dark) {
319            topleft->y += 0x1000;
320            bottomright->y += 0x1000;
321        }
322    }
323}
324
325uint16_t
326ap_map_attr(struct xy xy)
327{
328    struct ap_screen * screen = map_screens[XYMAPSCREEN(xy)];
329    if (screen == NULL) {
330        //LOG("null screen on xy:" PRIXYV, PRIXYVF(xy));
331        return 0;
332    }
333
334    // Read from cache
335    assert(XYIN(xy, screen->tl, screen->br));
336    uint16_t y = (xy.y - screen->tl.y) / 8;
337    uint16_t x = (xy.x - screen->tl.x) / 8;
338    return screen->attr_cache[y][x];
339}
340
341static uint16_t
342ap_link_tile_attr(struct xy link)
343{
344    uint16_t tile = 0;
345    tile |= ap_tile_attrs[ap_map_attr(XYOP2(link, +, XY(0, 0)))];
346    tile |= ap_tile_attrs[ap_map_attr(XYOP2(link, +, XY(0, 8)))];
347    tile |= ap_tile_attrs[ap_map_attr(XYOP2(link, +, XY(8, 0)))];
348    tile |= ap_tile_attrs[ap_map_attr(XYOP2(link, +, XY(8, 8)))];
349    return tile;
350}
351
352static uint16_t
353ap_map_attr_from_ram(struct xy point)
354{
355    if (*ap_ram.in_building) {
356        assert(XYINDOORS(point));
357        if (point.x & 0x200) {
358            return ap_ram.dngn_bg2_tattr[XYMAP8(point)];
359        } else  {
360            // lower level
361            return ap_ram.dngn_bg1_tattr[XYMAP8(point)];
362        }
363    } else {
364#define LOAD(x) (*(uint16_t *)ap_emu->base((x) | 0x7E0000))
365#define LOAD2(x) (*(uint16_t *)ap_emu->base((x) ))
366        //uint16_t six = ((point.y - LOAD(0x708)) & LOAD(0x70A)) << 3;
367        //uint16_t x = (((point.x/8) - LOAD(0x70C)) & LOAD(0x70E)) | six;
368        uint16_t a, x, m06, tile;
369
370//CODE_008830:        A5 00         LDA $00                   ;
371        a = point.y;
372//CODE_008832:        38            SEC                       ;
373//CODE_008833:        ED 08 07      SBC $0708                 ;
374        a -= LOAD(0x708);
375//CODE_008836:        2D 0A 07      AND $070A                 ;
376        a &= LOAD(0x70A);
377//CODE_008839:        0A            ASL A                     ;
378//CODE_00883A:        0A            ASL A                     ;
379//CODE_00883B:        0A            ASL A                     ;
380        a <<= 3;
381//CODE_00883C:        85 06         STA $06                   ;
382        m06 = a;
383//CODE_00883E:        A5 02         LDA $02                   ;
384        a = point.x / 8;
385//CODE_008840:        38            SEC                       ;
386//CODE_008841:        ED 0C 07      SBC $070C                 ;
387        a -= LOAD(0x70C);
388//CODE_008844:        2D 0E 07      AND $070E                 ;
389        a &= LOAD(0x70E);
390//CODE_008847:        05 06         ORA $06                   ;
391        a |= m06;
392//CODE_008849:        AA            TAX                       ;
393        x = a;
394//CODE_00884A:        BF 00 20 7E   LDA $7E2000,x             ;
395        a = tile = LOAD2(0x7E2000 + x);
396//CODE_00884E:        0A            ASL A                     ;
397//CODE_00884F:        0A            ASL A                     ;
398        a <<= 2;
399//CODE_008850:        85 06         STA $06                   ;
400        m06 = a;
401//CODE_008852:        A5 00         LDA $00                   ;
402        a = point.y;
403//CODE_008854:        29 08 00      AND #$0008                ;
404        a &= 0x8;
405//CODE_008857:        4A            LSR A                     ;
406//CODE_008858:        4A            LSR A                     ;
407        a >>= 2;
408//CODE_008859:        04 06         TSB $06                   ;
409        m06 |= a;
410//CODE_00885B:        A5 02         LDA $02                   ;
411        a = point.x / 8;
412//CODE_00885D:        29 01 00      AND #$0001                ;
413        a &= 0x1;
414//CODE_008860:        05 06         ORA $06                   ;
415        a |= m06;
416//CODE_008862:        0A            ASL A                     ;
417        a <<= 1;
418//CODE_008863:        AA            TAX                       ;
419        x = a;
420//CODE_008864:        BF 00 80 0F   LDA.l DATA_0F8000,x       ;
421        a = LOAD2(0xF8000 + x);
422//CODE_008868:        85 06         STA $06                   ;
423        m06 = a;
424//CODE_00886A:        29 FF 01      AND #$01FF                ;
425        a &= 0x1ff;
426//CODE_00886D:        AA            TAX                       ;
427        x = a;
428//CODE_00886E:        BF 59 94 0E   LDA.l DATA_0E9459,x       ;
429        a = LOAD2(0xFFD94 + x);
430//CODE_008872:        E2 30         SEP #$30                  ;
431        a &= 0xFF;
432//CODE_008874:        C9 10         CMP #$10                  ;
433//CODE_008876:        90 0F         BCC CODE_008887           ;
434//CODE_008878:        C9 1C         CMP #$1C                  ;
435//CODE_00887A:        B0 0B         BCS CODE_008887           ;
436        //if (a < 0x10 || a >= 0x1C) return a;
437        if (a >= 0x10 && a < 0x1C) {
438//CODE_00887C:        85 06         STA $06                   ;
439            m06 = (m06 & 0xFF00) | (a & 0xFF);
440//CODE_00887E:        A5 07         LDA $07                   ;
441            a = (m06 & 0xFF) >> 8;
442//CODE_008880:        29 40         AND #$40                  ;
443            a &= 0x40;
444//CODE_008882:        0A            ASL A                     ;
445//CODE_008883:        2A            ROL A                     ;
446//CODE_008884:        2A            ROL A                     ;
447            a <<= 3;
448            a = (a & 0xF8) | ((a >> 8) & 0x7); // XXX is this right w/ carry?
449//CODE_008885:        05 06         ORA $06                   ;
450            a |= m06 & 0xFF;
451//CODE_008887:        6B            RTL                       ;
452        }
453        //return a;
454
455        // Patch: make 0x27 always mean a hammerable peg; fences etc turned to 0x01
456        if (a == 0x27 && tile != 0x021B) {
457            return 0x01;
458        }
459
460        return a;
461
462/*
463        uint16_t cy = (uint16_t) (point.y - *ap_ram.map_y_offset) & *ap_ram.map_y_mask;
464        uint16_t cx = (uint16_t) ((point.x / 8) - *ap_ram.map_x_offset) & *ap_ram.map_x_mask;
465        uint16_t offset = (uint16_t) (cy << 3) | cx;
466        uint16_t map16 = (uint16_t) (ap_ram.over_map16[offset/2] << 2);
467        uint16_t q = (uint16_t) (((point.y & 0x8) >> 2) | ((point.x & 0x8) >> 3));
468        uint16_t x = (uint16_t) (map16 | q);
469        uint16_t af = ((uint16_t *) ap_emu->base(0x0F8000))[x];
470        uint8_t a = ap_ram.over_tattr[af & 0x1ff];
471        if (a < 0x10 || a > 0x1C) return a;
472        return 0xff;
473        */
474    }
475}
476
477static bool
478ap_screen_refresh_cache(struct ap_screen * screen)
479{
480    bool change = false;
481    for (uint16_t y = 0; y < 0x80; y++) {
482        struct xy xy;
483        xy.y = screen->tl.y + 8 * y;
484        if (xy.y > screen->br.y) break;
485        for (uint16_t x = 0; x < 0x80; x++) {
486            xy.x = screen->tl.x + 8 * x;
487            if (xy.x > screen->br.x) break;
488            uint16_t attr = ap_map_attr_from_ram(xy);
489            if (attr != screen->attr_cache[y][x]) {
490                screen->attr_cache[y][x] = attr;
491                change = true;
492            }
493        }
494    }
495    return change;
496}
497
498struct xy
499ap_map16_to_xy(struct xy tl, uint16_t map16)
500{
501    struct xy xy;
502    //xy.x = tl.x + ((map16 & 0x03F) << 4);
503    //xy.y = tl.y + ((map16 & 0xFC0) >> 2);
504    xy.x = tl.x + ((map16 & 0x07E) << 3) + 8;
505    xy.y = tl.y + ((map16 & 0x1F80) >> 3) + 16;
506    if (XYEQ(tl, XY(0x0c00, 0x0600))) {
507        // XXX: Not sure why this correction is nessassary?
508        //xy = XYOP2(xy, -, XY(8, 16));
509        xy.x = tl.x + ((map16 & 0x07F) << 3);
510        xy.y = tl.y + ((map16 & 0xFC0) >> 3);
511    }
512    return xy;
513}
514
515struct xy
516ap_tilemap_to_xy(struct xy tl, uint16_t tilemap) 
517{
518    assert_bp(XYINDOORS(tl));
519    assert_bp(tilemap < 0x4000);
520    // TODO: What do bits 0x0040 and 0x0001 do?
521    //assert_bp((tilemap & 0x0041) == 0);
522
523    struct xy xy = XYOP1(tl, &~0x1FF);
524    if (tilemap & 0x2000) {
525        xy = XYTOLOWER(xy);
526    } else {
527        xy = XYTOUPPER(xy);
528    }
529    xy.x += ((tilemap & 0x007E) << 2) + 8;
530    xy.y += ((tilemap & 0x1F80) >> 4) + 16;
531    return xy;
532}
533
534void
535ap_print_map_screen_pair() {
536    struct xy link = ap_link_xy();
537    ap_print_map_screen(map_screens[XYMAPSCREEN(link)]);
538    if (XYINDOORS(link)) {
539        ap_print_map_screen(map_screens[XYMAPSCREEN(XYFLIPBG(link))]);
540    }
541}
542
543void
544ap_print_map_screen(struct ap_screen * screen)
545{
546    FILE * mapf = NONNULL(fopen("map", "w"));
547    FILE * mapimg = NONNULL(fopen("map.pbm.tmp", "w"));
548
549    struct xy link = ap_link_xy();
550    struct xy link_tl = link;
551    struct xy link_br = XYOP1(link, + 15);
552    if (screen == NULL) {
553        screen = map_screens[XYMAPSCREEN(link_tl)];
554    }
555
556    struct xy screen_size = XYOP1(XYOP2(screen->br, -, screen->tl), / 8 + 1);
557    fprintf(mapimg, "P6 %u %u %u\n", screen_size.x, screen_size.y, 255);
558
559    uint16_t lift_mask = TILE_ATTR_LFT0;
560    if (*ap_ram.inventory_gloves >= 1) lift_mask |= TILE_ATTR_LFT1;
561    if (*ap_ram.inventory_gloves >= 2) lift_mask |= TILE_ATTR_LFT2;
562    if (*ap_ram.inventory_hammer >= 1) lift_mask |= TILE_ATTR_HMMR;
563
564    uint16_t semi_tile_attrs = lift_mask | TILE_ATTR_DOOR;
565    bool inside = XYINDOORS(screen->tl);
566
567    for (struct xy xy = screen->tl; xy.y < screen->br.y; xy.y += 8) {
568        for (xy.x = screen->tl.x; xy.x < screen->br.x; xy.x += 8) {
569            uint8_t tile_attr = ap_map_attr(xy);
570            bool tile_walk = ap_tile_attrs[tile_attr] & TILE_ATTR_WALK;
571            bool tile_semi = ap_tile_attrs[tile_attr] & semi_tile_attrs;
572            uint8_t px_shade = tile_walk ? 255 : (tile_semi ? 127 : 0);
573            if (XYIN(xy, link_tl, link_br)) {
574                fprintf(mapf, TERM_GREEN(TERM_BOLD("%02x ")), tile_attr);
575                fprintf(mapimg, "%c%c%c", 0, 128, 0);
576                goto next_point;
577            } 
578            for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
579                if (XYIN(xy, node->tl, node->br)) {
580                    if (node->type == NODE_OVERLAY) {
581                        fprintf(mapf, TERM_CYAN(TERM_BOLD("%02x ")), tile_attr);
582                        goto next_point;
583                    }
584                }
585            }
586            for (uint8_t i = 0; i < 16; i++) {
587                if (ap_sprites[i].type == 0) {
588                    continue;
589                }
590                if (XYIN(xy, ap_sprites[i].tl, ap_sprites[i].br) ||
591                    (inside && XYIN(XY(xy.x ^ 0x200, xy.y), ap_sprites[i].tl, ap_sprites[i].br))) {
592                    if ((xy.x ^ xy.y) & 0x8) {
593                        fprintf(mapf, TERM_MAGENTA("%02x "), tile_attr);
594                    } else {
595                        fprintf(mapf, TERM_MAGENTA(TERM_BOLD("%02x ")), ap_sprites[i].type);
596                    }
597                    fprintf(mapimg, "%c%c%c", 255, px_shade / 4, 255);
598                    goto next_point;
599                }
600            }
601            for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
602                if (goal->node == NULL) {
603                    continue;
604                }
605                if (XYIN(xy, goal->node->locked_xy, XYOP1(goal->node->locked_xy, +7))) {
606                    fprintf(mapf, TERM_CYAN(TERM_BOLD("%02x ")), tile_attr);
607                    fprintf(mapimg, "%c%c%c", 64, 64, 0);
608                    goto next_point;
609                }
610                if (XYIN(xy, goal->node->tl, goal->node->br)) {
611                    fprintf(mapf, TERM_YELLOW("%02x "), tile_attr);
612                    fprintf(mapimg, "%c%c%c", 255, 255 - goal->attempts * 64, 0);
613                    goto next_point;
614                }
615            }
616            for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
617                if (XYIN(xy, node->locked_xy, XYOP1(node->locked_xy, +7))) {
618                    fprintf(mapf, TERM_CYAN(TERM_BOLD("%02x ")), tile_attr);
619                    fprintf(mapimg, "%c%c%c", 64, 64, 0);
620                    goto next_point;
621                }
622                if (XYIN(xy, node->tl, node->br)) {
623                    fprintf(mapf, TERM_RED(TERM_BOLD("%02x ")), tile_attr);
624                    if (node->adjacent_node == NULL) {
625                        fprintf(mapimg, "%c%c%c", 255, 0, px_shade / 4);
626                    } else {
627                        fprintf(mapimg, "%c%c%c", 128, 255, 0);
628                    }
629                    goto next_point;
630                }
631            }
632            for (size_t i = 0; i < ap_target_count; i++) {
633                if (XYIN(xy, ap_targets[i].tl, XYOP1(ap_targets[i].tl, +8))) {
634                    fprintf(mapf, TERM_BLUE(TERM_BOLD("%02x ")), tile_attr);
635                    fprintf(mapimg, "%c%c%c", 0, px_shade / 4, 255);
636                    goto next_point;
637                }
638            }
639            fprintf(mapimg, "%c%c%c", px_shade, px_shade, px_shade);
640            if (!(ap_tile_attrs[tile_attr] & TILE_ATTR_WALK)) {
641                fprintf(mapf, TERM_BOLD("%02x "), ap_map_attr(xy));
642                goto next_point;
643            }
644            fprintf(mapf, "%02x ", ap_map_attr(xy));
645next_point:;
646        }
647        fprintf(mapf, "\n");
648    }
649    fprintf(mapf, "--\n");
650    fprintf(mapf, "Screen '%s': " PRIBBV "\n", screen->name, PRIBBVF(*screen));
651    fclose(mapf);
652    fclose(mapimg);
653    rename("map.pbm.tmp", "map.pbm");
654}
655
656static int
657ap_set_targets(size_t count)
658{
659    assert(count < sizeof(ap_targets) / sizeof(*ap_targets));
660    ap_target_count = count;
661    ap_target_timeout = 0;
662    if (count == 0) 
663        return 0;
664
665    ap_targets[count] = (struct ap_target) { .tl = ap_link_xy()};
666    //fprintf(stderr, "(dest) ");
667    for (size_t i = 0; i < count; i++) {
668        if (XYEQ(ap_targets[i].tl, XY(0,0))) {
669            LOG("target 0");
670            assert_bp(false);
671        }
672        ap_target_timeout += XYL1DIST(ap_targets[i].tl, ap_targets[i+1].tl) * 2;
673        if (ap_targets[i].joypad != ap_targets[i+1].joypad) {
674            ap_target_timeout += 1;
675        }
676        //fprintf(stderr, PRIXYV ", ", PRIXYVF(ap_targets[i]));
677    }
678    //fprintf(stderr, PRIXYV " (link)\n", PRIXYVF(ap_targets[count]));
679
680    for (size_t i = count; i < sizeof(ap_targets) / sizeof(*ap_targets); i++) {
681        ap_targets[count] = (struct ap_target) {.tl = XY(0, 0), .joypad = 0, .joypad_mask = 0};
682    }
683
684    ap_target_subtimeout = 32;
685    ap_target_timeout += 64;
686    fflush(NULL);
687    LOG("Following %zu targets with timeout: %d", ap_target_count, ap_target_timeout);
688    ap_print_map_screen(NULL);
689    return ap_target_timeout;
690}
691
692int
693ap_follow_targets(uint16_t * joypad, enum ap_inventory * equip_out)
694{
695    static struct xy last_link = XY(0, 0);
696    static int stationary_link_count = 0;
697    struct xy link = ap_link_xy();
698
699    if (XYEQ(last_link, link)) {
700        stationary_link_count++;
701    } else {
702        stationary_link_count = 0;
703    }
704    last_link = link;
705
706    if (stationary_link_count >= 128) {
707        LOGB("Stuck Link Detected! " PRIXY " en route to " PRIXY,
708            PRIXYF(link), PRIXYF(ap_targets[0].tl));
709        ap_target_timeout = 0;
710        stationary_link_count = 0;
711    }
712
713    if (ap_target_timeout <= 0) {
714        //INFO("L:" PRIXY "; " PRIXY " timeout", PRIXYF(link), PRIXYF(ap_targets[0].tl));
715        LOG("L:" PRIXY "; " PRIXY " timeout", PRIXYF(link), PRIXYF(ap_targets[0].tl));
716        ap_target_count = 0;
717        return RC_FAIL;
718    }
719
720    bool on_stairs = *ap_ram.submodule_index == 0x10 || *ap_ram.submodule_index == 0x08;
721    if (!ap_target_scripted && !on_stairs && (ap_target_subtimeout-- <= 0 || ap_sprites_changed)) {
722        int prev_timeout = ap_target_timeout;
723        int rc = ap_pathfind_local(NULL, link, ap_target_dst_tl, ap_target_dst_br, true);
724        if (rc < 0) {
725            LOGB("Path is now blocked");
726            ap_target_count = 0;
727            ap_target_timeout = -1;
728            return RC_FAIL;
729        }
730        ap_target_timeout = MIN(prev_timeout, ap_target_timeout);
731    }
732
733    struct ap_target target;
734    uint16_t joypad_mask = 0;
735    while (true) {
736        if (ap_target_count < 1) {
737            //INFO("L:" PRIXY "; done", PRIXYF(link));
738            //LOG("L:" PRIXY "; done", PRIXYF(link));
739            return RC_DONE;
740        }
741
742        target = ap_targets[ap_target_count-1];
743        if (target.joypad_mask & joypad_mask) {
744            break;
745        }
746
747        *joypad &= (uint16_t) ~target.joypad_mask;
748        *joypad |= target.joypad;
749        joypad_mask |= target.joypad_mask;
750
751        if (XYL1DIST(link, target.tl) <= 1) {
752            ap_target_count--;
753            ap_target_subtimeout = 32;
754            continue;
755        }
756        if (!ap_target_scripted && ap_target_count > 1) {
757            struct ap_target next_target = ap_targets[ap_target_count-2];
758            if (XYIN(link, XYFN2(MIN, target.tl, next_target.tl), XYFN2(MAX, target.tl, next_target.tl))) {
759                ap_target_count--;
760                ap_target_subtimeout = 32;
761                continue;
762            }
763        }
764
765        break;
766    }
767
768
769    if (!ap_target_scripted && XYLINKIN(link, ap_target_dst_tl, ap_target_dst_br)) {
770        ap_target_count = 0;
771        return RC_DONE;
772    }
773
774    if (!XYIN(link, ap_target_screen->tl, ap_target_screen->br)) {
775        if (ap_target_scripted) {
776            LOG("Marking script done because link left screen");
777            ap_target_count = 0;
778            return RC_DONE;
779        }
780
781        LOGB("Link left the screen!");
782        static int leave_count = 0;
783        leave_count++;
784        assert_bp(leave_count % 32 != 0);
785        return RC_FAIL;
786    }
787
788    //INFO("L:" PRIXY "; %zu:" PRIXY "; %d %#x", PRIXYF(link), ap_target_count, PRIXYF(ap_targets[ap_target_count-1]), ap_target_timeout, *ap_ram.push_dir_bitmask);
789    //LOG("L:" PRIXY "; %zu:" PRIXY "; %d", PRIXYF(link), ap_target_count, PRIXYF(ap_targets[ap_target_count-1]), ap_target_timeout);
790
791    const uint16_t dir_mask = SNES_MASK(UP) | SNES_MASK(DOWN) | SNES_MASK(LEFT) | SNES_MASK(RIGHT);
792    struct xy next_xy = target.tl;
793    if ((joypad_mask & dir_mask) == 0) {
794        if (link.x > target.tl.x) {
795            JOYPAD_SET(LEFT);
796        } else if (link.x < target.tl.x) {
797            JOYPAD_SET(RIGHT);
798            next_xy.x += 8;
799        }
800        if (link.y > target.tl.y) {
801            JOYPAD_SET(UP);
802        } else if (link.y < target.tl.y) {
803            JOYPAD_SET(DOWN);
804            next_xy.y += 8;
805        }
806    }
807
808    uint16_t lift_mask = TILE_ATTR_LFT0;
809    if (*ap_ram.inventory_gloves >= 1) lift_mask |= TILE_ATTR_LFT1;
810    if (*ap_ram.inventory_gloves >= 2) lift_mask |= TILE_ATTR_LFT2;
811
812    uint8_t tile = ap_map_attr(next_xy);
813    if (tile == 0x50 && *ap_ram.inventory_sword > 0) {
814        JOYPAD_MASH(B, 0x04); // Sword
815    } else if ((ap_tile_attrs[tile] & lift_mask) && *ap_ram.push_timer != 0x20) {
816        JOYPAD_MASH(A, 0x01); // Lift
817    } else if (*ap_ram.carrying_bit7) {
818        JOYPAD_MASH(A, 0x01);
819    } else if ((ap_tile_attrs[tile] & TILE_ATTR_HMMR) && *ap_ram.inventory_hammer != 0) {
820        if (*equip_out == INVENTORY_HAMMER) {
821            JOYPAD_MASH(Y, 0x1);
822        }
823        *equip_out = INVENTORY_HAMMER;
824    } else {
825        JOYPAD_CLEAR(A);
826        JOYPAD_CLEAR(B);
827        JOYPAD_CLEAR(Y);
828    }
829
830
831    /*
832    uint8_t dir = 0;
833    if (link.x > target.x) {
834        if (link.y > target.y)
835            dir = DIR_LU;
836        else if (link.y < target.y)
837            dir = DIR_LD;
838        else
839            dir = DIR_L;
840    } else if (link.x < target.x) {
841        if (link.y > target.y)
842            dir = DIR_RU;
843        else if (link.y < target.y)
844            dir = DIR_RD;
845        else
846            dir = DIR_R;
847    } else {
848        if (link.y > target.y)
849            dir = DIR_U;
850        else if (link.y < target.y)
851            dir = DIR_D;
852        else
853            dir = 0;
854    }
855    if (dir != 0) {
856        *joypad |= dir_joypad[dir];
857    }
858    */
859    
860    /*
861    uint16_t tile_mask = TILE_ATTR_WALK | TILE_ATTR_DOOR | TILE_ATTR_LDGE | TILE_ATTR_LFT0;
862    if (link.x > target.x && (ap_link_tile_attr(XYOP2(link, -, XY(8, 0))) & tile_mask))
863        JOYPAD_SET(LEFT);
864    else if (link.x < target.x && (ap_link_tile_attr(XYOP2(link, +, XY(8, 0))) & tile_mask))
865        JOYPAD_SET(RIGHT);
866    if (link.y > target.y && (ap_link_tile_attr(XYOP2(link, -, XY(0, 8))) & tile_mask))
867        JOYPAD_SET(UP);
868    else if (link.y < target.y && (ap_link_tile_attr(XYOP2(link, +, XY(0, 8))) & tile_mask))
869        JOYPAD_SET(DOWN);
870    */
871
872    uint8_t d = (*ap_ram.link_direction / 2) + 1;
873    struct xy sword_up = XYOP1(dir_dxy[d], * 16 + 8);
874    //struct xy sword_rt = XYOP1(dir_dxy[dir_cw[d]], * 16 + 8);
875    struct xy link_sword_up = XYOP2(link, +, sword_up);
876    //struct xy link_sword_rt = XYOP2(link, +, sword_rt);
877    //
878    if (ap_sprites[ap_target_sprite_index].attrs & SPRITE_ATTR_VBOW) {
879        uint8_t i = ap_target_sprite_index;
880        if (ap_sprites[i].type == 0x84 && ap_sprites[i].interaction == 0x07 &&
881                XYL1BOXDIST(link_sword_up, ap_sprites[i].tl, ap_sprites[i].br) <= 8) {
882            JOYPAD_MASH(Y, 0x08);
883            JOYPAD_CLEAR(B);
884        }
885    } else {
886        if ((joypad_mask & SNES_MASK(B)) == 0) {
887            for (uint8_t i = 0; i < 16; i++) {
888                if (!*ap_ram.inventory_sword) {
889                    break;
890                }
891                if (!ap_sprites[i].active) {
892                    continue;
893                }
894                if ((ap_sprites[i].attrs & SPRITE_ATTR_ENMY) && !(ap_sprites[i].attrs & SPRITE_ATTR_NVUL)) {
895                    if (XYL1BOXDIST(link_sword_up, ap_sprites[i].tl, ap_sprites[i].br) <= 8) {
896                        JOYPAD_MASH(B, 0x08); // Sword
897                        break;
898                    }
899                }
900            }
901        }
902    }
903
904    ap_target_timeout--;
905
906    return RC_INPR;
907}
908
909void
910ap_joypad_setdir(uint16_t * joypad, uint8_t dir)
911{
912    // 0 U D L R LU LD RU RD 0
913    JOYPAD_CLEAR(UP);
914    JOYPAD_CLEAR(DOWN);
915    JOYPAD_CLEAR(LEFT);
916    JOYPAD_CLEAR(RIGHT);
917    switch (dir) {
918    case 1: JOYPAD_SET(UP); break;
919    case 2: JOYPAD_SET(DOWN); break;
920    case 3: JOYPAD_SET(LEFT); break;
921    case 4: JOYPAD_SET(RIGHT); break;
922    case 5: JOYPAD_SET(UP); JOYPAD_SET(LEFT); break;
923    case 6: JOYPAD_SET(UP); JOYPAD_SET(LEFT); break;
924    case 7: JOYPAD_SET(DOWN); JOYPAD_SET(RIGHT); break;
925    case 8: JOYPAD_SET(DOWN); JOYPAD_SET(RIGHT); break;
926    }
927}
928
929uint32_t
930ap_path_heuristic(struct xy src, struct xy dst_tl, struct xy dst_br)
931{
932    uint32_t distance = 0;
933    if (XYINDOORS(src) && XYINDOORS(dst_tl) && XYINDOORS(dst_br)) {
934        // collapse BG1 & BG2
935        if ((src.x & ~0x200) != (dst_tl.x & ~0x200)) {
936            distance += 1;
937        }
938        distance += 1;
939        src.x &= ~0x200;
940        dst_tl.x &= ~0x200;
941        dst_br.x &= ~0x200;
942    }
943    if (src.x < dst_tl.x)
944        distance += dst_tl.x - src.x;
945    else if (src.x > dst_br.x)
946        distance += src.x - dst_br.x;
947    if (src.y < dst_tl.y)
948        distance += dst_tl.y - src.y;
949    else if (src.y > dst_br.y)
950        distance += src.y - dst_br.y;
951    return distance;
952}
953
954static volatile bool save_local_state = false;
955
956static int
957ap_pathfind_local(struct ap_screen * screen, struct xy start_xy, struct xy destination_tl, struct xy destination_br, bool commit)
958{
959    if (commit) {
960        ap_set_targets(0);
961        ap_update_map_screen(true);
962    }
963
964    if (screen == NULL) {
965        screen = map_screens[XYMAPSCREEN(start_xy)];
966    } else {
967        assert_bp(screen == map_screens[XYMAPSCREEN(start_xy)]);
968    }
969
970    if (!XYIN(start_xy, screen->tl, screen->br)) {
971        LOG("error: Start " PRIXYV " out of screen", PRIXYVF(start_xy));
972        return -1;
973    }
974    if (!XYIN(destination_tl, screen->tl, screen->br)) {
975        LOG("error: TL destination " PRIXYV " out of screen", PRIXYVF(destination_tl));
976        assert_bp(false);
977        return -1;
978    }
979    if (!XYIN(destination_br, screen->tl, screen->br)) {
980        LOG("error: BR destination " PRIXYV " out of screen", PRIXYVF(destination_br));
981        return -1;
982    }
983
984    if (commit) {
985        //LOG("Starting pathfind in screen " PRIBBV ", start " PRIXYV, PRIBBVF(*screen), PRIXYVF(start_xy));
986    }
987
988    start_xy = XYFN2(MAX, start_xy, XYOP1(screen->tl, + 16));
989    start_xy = XYFN2(MIN, start_xy, XYOP1(screen->br, - 16));
990
991    struct xy size = XYOP2(screen->br, -, screen->tl);
992    struct xy grid = XYOP1(size, / 8 + 1);
993    struct xy tl_offset = XYOP1(screen->tl, / 8);
994
995    struct xy src = XYOP2(XYOP1(start_xy, / 8), -, tl_offset);
996    struct xy dst_tl = XYOP2(XYOP1(destination_tl, / 8), -, tl_offset);
997    struct xy dst_br = XYOP2(XYOP1(destination_br, / 8), -, tl_offset);
998
999    uint32_t distance = ap_path_heuristic(src, dst_tl, dst_br);
1000    if (distance > (size.x + size.y)) {
1001        printf("error: invalid target " PRIXYV " x " PRIXYV, PRIXYVF(destination_tl), PRIXYVF(destination_br));
1002        return -1;
1003    }
1004    /*
1005    if (distance < 8) {
1006        ap_targets[0] = destination;
1007        return ap_target_count = 1;
1008    }
1009    */
1010
1011    uint16_t lift_mask = TILE_ATTR_LFT0;
1012    if (*ap_ram.inventory_gloves >= 1) lift_mask |= TILE_ATTR_LFT1;
1013    if (*ap_ram.inventory_gloves >= 2) lift_mask |= TILE_ATTR_LFT2;
1014    if (*ap_ram.inventory_hammer >= 1) lift_mask |= TILE_ATTR_HMMR;
1015
1016    static struct point_state buf[0x82 * 0x82]; 
1017
1018    // CORNER is used to identify boundaries of 2x2 tiles (like bushes)
1019    enum corner {
1020        CORNER_NONE = 0,
1021        CORNER_TL,
1022        CORNER_TR,
1023        CORNER_BL,
1024        CORNER_BR,
1025        CORNER_TA, // Left adjacent squares
1026        CORNER_BA,
1027        CORNER_AL, // Top adjacent squares
1028        CORNER_AR,
1029        CORNER_AA, // Up-Left diagonally adjacent
1030    };
1031    if (grid.x * grid.y > 0x80 * 0x80) {
1032        printf("error: grid too large: " PRIXY "\n", PRIXYF(grid));
1033        return -1;
1034    }
1035
1036    // Make a state[y][x]
1037    struct point_state (*state)[grid.x + 2] = (void *) &buf[0x83];
1038
1039    // Check if cache is valid
1040    static struct ap_screen *last_pf_screen = NULL;
1041    static uint32_t last_frame = 0;
1042    if (false && last_pf_screen == screen && last_frame == ap_frame) {
1043        for (uint16_t y = 0; y < grid.y; y++) {
1044            for (uint16_t x = 0; x < grid.x; x++) {
1045                assert_bp(&state[y][x] == &buf[0x83 + (grid.x + 2) * y + x]);
1046                state[y][x].from = NULL;
1047                state[y][x].gscore = (uint32_t) -1;
1048                state[y][x].fscore = (uint32_t) -1;
1049            }
1050        }
1051    } else {
1052        last_pf_screen = screen;
1053        last_frame = ap_frame;
1054
1055        for (size_t i = 0; i < 0x82 * 0x82; i++) {
1056            buf[i].ledge = 0xFF;
1057        } 
1058
1059        for (uint16_t y = 0; y < grid.y; y++) {
1060            for (uint16_t x = 0; x < grid.x; x++) {
1061                assert_bp(&state[y][x] == &buf[0x83 + (grid.x + 2) * y + x]);
1062                state[y][x].from = NULL;
1063                state[y][x].xy = XYOP1(XYOP2(XY(x, y), +, tl_offset), * 8);
1064                state[y][x].gscore = (uint32_t) -1;
1065                state[y][x].fscore = (uint32_t) -1;
1066                state[y][x].cost = 0;
1067                state[y][x].corner = CORNER_NONE;
1068                state[y][x].ledge = 0;
1069                state[y][x].tile_attrs = 0;
1070            }
1071        }
1072        for (uint16_t y = 0; y < grid.y; y++) {
1073            for (uint16_t x = 0; x < grid.x; x++) {
1074                uint32_t cost = 0;
1075                struct xy mapxy = XYOP2(XYOP1(XY(x, y), * 8), +, screen->tl);
1076                uint8_t raw_tile = ap_map_attr(mapxy);
1077                uint16_t tile = ap_tile_attrs[raw_tile];
1078                state[y][x].tile_attrs = tile;
1079                state[y][x].raw_tile = raw_tile;
1080                uint8_t ledge_mask = 0;
1081                uint8_t ledge_dir = DIR_NONE;
1082                //if (tile & (TILE_ATTR_WALK | TILE_ATTR_DOOR)) {
1083                if (tile & (TILE_ATTR_WALK)) {
1084                    cost = 0;
1085                } else if ((tile & TILE_ATTR_DOOR) && XYL1DIST(XY(x, y), src) <= 3) {
1086                    cost = 0;
1087                } else if (tile & TILE_ATTR_LDGE) {
1088                    cost = (1 << 20); // calculated at search time
1089                    //cost = 0;
1090                    assert_bp((raw_tile &~0x7) == 0x28);
1091                    ledge_mask = 1ul << (raw_tile & 0x7);
1092                    ledge_dir = (raw_tile & 0x7) + 1;
1093                } else if (tile & lift_mask) {
1094                    cost = 20;
1095                    if (state[y][x].corner == CORNER_NONE && raw_tile != 0x55)  {
1096                        state[y][x].corner = CORNER_TL;
1097                        state[y][x+1].corner = CORNER_TR;
1098                        state[y+1][x].corner = CORNER_BL;
1099                        state[y+1][x+1].corner = CORNER_BR;
1100                        if (state[y][x-1].corner == CORNER_NONE) {
1101                            state[y][x-1].corner = CORNER_TA;
1102                            state[y+1][x-1].corner = CORNER_BA;
1103                        }
1104                        if (state[y-1][x-1].corner == CORNER_NONE) {
1105                            state[y-1][x-1].corner = CORNER_AA;
1106                        }
1107                        if (state[y-1][x-0].corner == CORNER_NONE) {
1108                            state[y-1][x-0].corner = CORNER_AL;
1109                        }
1110                        if (state[y-1][x+1].corner == CORNER_NONE) {
1111                            state[y-1][x+1].corner = CORNER_AR;
1112                        }
1113                    }
1114                // XXX This was correct; but it breaks caching
1115                } else if (XYIN(mapxy, destination_tl, destination_br)) {
1116                    cost = 0;
1117                } else if (raw_tile == 0x1C) {
1118                    cost = 1000;
1119                } else {
1120                    cost = (1 << 20);
1121                }
1122
1123                // Pit collision is weird
1124                state[y-1][x-1].cost += cost;
1125                if (!(tile & TILE_ATTR_EDGE)) {
1126                    state[y-0][x-0].cost += cost;
1127                    state[y-0][x-1].cost += cost;
1128                    state[y-1][x-0].cost += cost;
1129                }
1130
1131                //state[y+1][x+1].ledge |= ledge_mask;
1132                //state[y+1][x-0].ledge |= ledge_mask;
1133                //state[y+1][x-1].ledge |= ledge_mask;
1134                //state[y+1][x-2].ledge |= ledge_mask;
1135                //state[y-0][x+1].ledge |= ledge_mask;
1136                state[y-0][x-0].ledge |= ledge_mask;
1137                state[y-0][x-1].ledge |= ledge_mask;
1138                //state[y-0][x-2].ledge |= ledge_mask;
1139                //state[y-1][x+1].ledge |= ledge_mask;
1140                state[y-1][x-0].ledge |= ledge_mask;
1141                state[y-1][x-1].ledge |= ledge_mask;
1142                //state[y-1][x-2].ledge |= ledge_mask;
1143                //state[y-2][x+1].ledge |= ledge_mask;
1144                //state[y-2][x-0].ledge |= ledge_mask;
1145                //state[y-2][x-1].ledge |= ledge_mask;
1146                //state[y-2][x-2].ledge |= ledge_mask;
1147                // There's something particuarly thorny about 0x2D ledges (LD)
1148                if (raw_tile == 0x2D) {
1149                    state[y][x+1].cost += cost;
1150                    state[y][x+1].ledge |= ledge_mask;
1151                    state[y][x+2].cost += cost;
1152                    state[y][x+2].ledge |= ledge_mask;
1153                }
1154
1155                /*
1156                if (ledge_dir != DIR_NONE) {
1157                    if (ledge_dir == DIR_U || ledge_dir == DIR_LU || ledge_dir == DIR_RU) {
1158                        state[y-2][x+0].cost += cost;
1159                        state[y-2][x+0].ledge |= ledge_mask;
1160                    }
1161                    if (ledge_dir == DIR_D || ledge_dir == DIR_LD || ledge_dir == DIR_RD) {
1162                        state[y+1][x+0].cost += cost;
1163                        state[y+1][x+0].ledge |= ledge_mask;
1164                    }
1165                    if (ledge_dir == DIR_L || ledge_dir == DIR_LU || ledge_dir == DIR_LD) {
1166                        state[y+0][x-2].cost += cost;
1167                        state[y+0][x-2].ledge |= ledge_mask;
1168                    }
1169                    if (ledge_dir == DIR_R || ledge_dir == DIR_RU || ledge_dir == DIR_RD) {
1170                        state[y+0][x+1].cost += cost;
1171                        state[y+0][x+1].ledge |= ledge_mask;
1172                    }
1173                }
1174                */
1175
1176                if (x < 1 || x >= grid.x - 1 ||
1177                    y < 1 || y >= grid.y - 1) {
1178                    state[y][x].cost = (1 << 20);
1179                } else if (x < 3 || x >= grid.x - 3 ||
1180                           y < 3 || y >= grid.y - 3) {
1181                    state[y][x].cost += 10000;
1182                }
1183            }
1184        }
1185        for (uint8_t i = 0; i < 16; i++) {
1186            if (ap_sprites[i].type == 0) {
1187                continue;
1188            }
1189            if (!XYIN(ap_sprites[i].hitbox_tl, screen->tl, screen->br)) {
1190                continue;
1191            }
1192            if (ap_sprites[i].attrs & SPRITE_ATTR_ENMY && ap_sprites[i].active) {
1193                struct xy center = XYOP1(XYOP2(ap_sprites[i].hitbox_tl, -, screen->tl), / 8);
1194                for (int dx = -4; dx <= 4; dx++) {
1195                    for (int dy = -4; dy <= 4; dy++) {
1196                        struct xy ds = XYOP2(center, +, XY(dx, dy));
1197                        if (!XYUNDER(ds, grid)) {
1198                            continue;
1199                        }
1200                        state[ds.y][ds.x].cost += MAX(0, 7 - (ABS(dx) + ABS(dy))) * 100;
1201                    }
1202                }
1203            }
1204            if ((ap_sprites[i].attrs & (SPRITE_ATTR_BLKF | SPRITE_ATTR_BLKS)) &&
1205                ap_sprites[i].state != 0x0A && // carried
1206                ap_sprites[i].state != 0x06) { // thrown
1207                assert_bp(ap_sprites[i].state == 0x08 || 
1208                        ap_sprites[i].state == 0x09 || 
1209                        ap_sprites[i].state == 0x00);
1210                //volatile struct xy center = XYOP1(XYOP2(ap_sprites[i].hitbox_tl, -, screen->tl), / 8);
1211                //assert_bp(i != 11);
1212                struct xy hb_tl = XYOP2(ap_sprites[i].hitbox_tl, -, screen->tl);
1213                struct xy hb_br= XYOP2(ap_sprites[i].hitbox_br, -, screen->tl);
1214                hb_tl = XYOP1(hb_tl, / 8);
1215                hb_br = XYOP1(hb_br, / 8);
1216                hb_tl = XYOP1(hb_tl, -1);
1217                hb_br = XYOP1(hb_br, +1);
1218                /*
1219                if (i == 11) {
1220                    LOG(TERM_BOLD("11:") " " PRIXYV, PRIXYVF(center));
1221                }
1222                */
1223                struct xy ds;
1224                for (ds.x = hb_tl.x; ds.x <= hb_br.x; ds.x++) {
1225                    for (ds.y = hb_tl.y; ds.y <= hb_br.y; ds.y++) {
1226                        if (!XYUNDER(ds, grid)) {
1227                            continue;
1228                        }
1229                        state[ds.y][ds.x].cost += (1 << 20);
1230                    }
1231                }
1232            }
1233        }
1234    }
1235
1236    // Hack to round when there is an obstacle nearby
1237    // Doesn't handle diagonals perfectly, but should be fine?
1238    if ((start_xy.x & 0x7) != 0 &&
1239        state[src.y][src.x].cost > state[src.y][src.x+1].cost) {
1240        src.x += 1;
1241        if (commit) LOG("Nudged X start");
1242    }
1243    if ((start_xy.y & 0x7) != 0 &&
1244        state[src.y][src.x].cost > state[src.y+1][src.x].cost) {
1245        src.y += 1;
1246        if (commit) LOG("Nudged Y start");
1247    }
1248    if (save_local_state || commit) {
1249        ap_print_pathfind_local_state(buf, grid);
1250    }
1251
1252    // Hack to nudge starting poing if we are "trapped" (probably inside a sprite)
1253    if (state[src.y][src.x].cost >= (1 << 20)) {
1254        const uint32_t min_cost = state[src.y][src.x].cost;
1255        if (state[src.y-2][src.x-2].cost < min_cost) {
1256            src.x += -2;
1257            src.y += -2;
1258        } else if (state[src.y+2][src.x-2].cost < min_cost) {
1259            src.x += -2;
1260            src.y += +2;
1261        } else if (state[src.y-2][src.x+2].cost < min_cost) {
1262            src.x += +2;
1263            src.y += -2;
1264        } else if (state[src.y+2][src.x+2].cost < min_cost) {
1265            src.x += +2;
1266            src.y += +2;
1267        }
1268    }
1269
1270    static struct pq * pq = NULL;
1271    if (pq == NULL) {
1272        pq = pq_create(sizeof(struct xy));
1273        if (pq == NULL) exit(1);
1274    }
1275    pq_clear(pq);
1276    pq_push(pq, 0, &src);
1277
1278    struct point_state start_ps = { .xy = start_xy };
1279    state[src.y][src.x].from = &start_ps;
1280    state[src.y][src.x].fscore = 0;
1281    state[src.y][src.x].gscore = 0;
1282    
1283    size_t r;
1284    struct xy final_xy;
1285    uint64_t min_heuristic = -1;
1286    for (r = 0; r < 0x80 * 0x80 + 100; r++) {
1287        struct xy node;
1288        uint64_t cost;
1289        int rc = pq_pop(pq, &cost, &node);
1290        if (rc != 0)
1291            goto search_failed;
1292        if (cost != state[node.y][node.x].fscore)
1293            continue;
1294        assert_bp(state[node.y][node.x].ledge != 0xFF);
1295        const struct point_state *const node_state = &state[node.y][node.x];
1296        for (uint8_t i = 1; i < 9; i++) {
1297            struct xy neighbor = XYOP2(node, +, dir_dxy[i]);
1298            const struct point_state *const neighbor_state = &state[neighbor.y][neighbor.x];
1299            if (neighbor.x >= grid.x || neighbor.y >= grid.y)
1300                continue;
1301            assert_bp(state[neighbor.y][neighbor.x].ledge != 0xFF);
1302            if (state[neighbor.y][neighbor.x].raw_tile == 0x1C)
1303                continue;
1304
1305            // Only pick up objects if you are square with them
1306            if (i == DIR_U || i == DIR_D || i >= 5) {
1307                if ((state[neighbor.y][node.x].tile_attrs & lift_mask) !=
1308                    (state[neighbor.y][node.x+1].tile_attrs & lift_mask))
1309                    continue;
1310            }
1311            if (i == DIR_L || i == DIR_R || i >= 5) {
1312                if ((state[node.y][neighbor.x].tile_attrs & lift_mask) !=
1313                    (state[node.y+1][neighbor.x].tile_attrs & lift_mask))
1314                    continue;
1315            }
1316            enum corner corner = state[neighbor.y][neighbor.x].corner;
1317            if (corner != CORNER_NONE) {
1318                if (i >= 5) continue;
1319                //if ((neighbor_state - node_state) != (node_state - node_state->from)) continue;
1320                if ((i == DIR_D || i == DIR_U) && !(corner == CORNER_AL || corner == CORNER_TL || corner == CORNER_BL))
1321                    continue;
1322                if ((i == DIR_L || i == DIR_R) && !(corner == CORNER_TA || corner == CORNER_TL || corner == CORNER_TR))
1323                    continue;
1324            }
1325            /*
1326            // Handle stairs
1327            if (state[node.y][node.x].raw_tile == 0x3E ||
1328                state[node.y][node.x].raw_tile == 0x1E) {
1329                // FIXME: This needs to change now that we've refactored
1330                // BG1/BG2  to be different screens
1331                neighbor.x ^= 0x200 / 8;
1332                //neighbor = XYOP2(neighbor, +, dir_dxy[i]);
1333                //neighbor = XYOP2(neighbor, +, dir_dxy[i]);
1334                assert_bp(XYUNDER(neighbor, grid));
1335            }
1336            */
1337            if ((state[neighbor.y][neighbor.x].ledge) && i >= 5) {
1338                // Stick to cardinal directions near ledges of any kind
1339                continue;
1340            }
1341            // Jump down ledges (disabled)
1342            uint8_t ledge_mask = 1ul << (i - 1);
1343            assert(ledge_mask != 0);
1344            if (false && (state[node.y][node.x].ledge & ledge_mask)) {
1345                bool fail_ledge = false;
1346                int iters = 0;
1347                while (true) {
1348                    if (!XYUNDER(neighbor, grid)) {
1349                        fail_ledge = true;
1350                        break;
1351                    }
1352                    if (state[neighbor.y][neighbor.x].raw_tile == 0x1C) {
1353                        // XXX this is broken now that screens are split by layer
1354                        assert_bp(neighbor.x & (0x200 / 8));
1355                        neighbor.x -= 0x200 / 8;
1356                        neighbor = XYOP2(neighbor, +, dir_dxy[i]);
1357                        assert_bp(XYUNDER(neighbor, grid));
1358                    } else if (state[neighbor.y][neighbor.x].cost < (1 << 20) &&
1359                        state[neighbor.y][neighbor.x].ledge == 0) {
1360                        break;
1361                    }
1362                    if (state[neighbor.y][neighbor.x].tile_attrs & TILE_ATTR_SWIM) {
1363                        fail_ledge = true;
1364                        break;
1365                    }
1366                    //assert_bp(state[neighbor.y][neighbor.x].ledge != 0xFF);
1367                    iters++;
1368                    struct xy next_neighbor = XYOP2(neighbor, +, dir_dxy[i]);
1369                    if (!XYUNDER(next_neighbor, grid))
1370                        break;
1371                    neighbor = next_neighbor;
1372                }
1373                if (fail_ledge)
1374                    continue;
1375                if (commit) LOG("Ledge %u in %d iters from " PRIXYV " to " PRIXYV, i, iters, PRIXYVF(node), PRIXYVF(neighbor));
1376                assert_bp(XYUNDER(neighbor, grid));
1377            } else if (state[node.y][node.x].ledge != 0) {
1378                continue;
1379            } else if (state[neighbor.y][neighbor.x].ledge != 0) {
1380                continue;
1381            }
1382            assert_bp(XYUNDER(neighbor, grid));
1383
1384            uint32_t gscore = state[node.y][node.x].gscore;
1385            gscore += dir_cost[i];
1386            if (true || !(state[node.y][node.x].ledge & ledge_mask)) {
1387                uint32_t step_cost = state[neighbor.y][neighbor.x].cost;
1388                if (i >= 5) {
1389                    step_cost = MAX(step_cost, state[neighbor.y][node.x].cost);
1390                    step_cost = MAX(step_cost, state[node.y][neighbor.x].cost);
1391                }
1392                gscore += step_cost;
1393                //if (!XYIN(neighbor, dst_tl, XYOP1(dst_br, -1))
1394                //    && XYL1DIST(neighbor, src) > 2) {
1395                //    gscore += step_cost;
1396                //}
1397            }
1398            if (gscore >= (1 << 20))
1399                continue;
1400            if (gscore >= state[neighbor.y][neighbor.x].gscore)
1401                continue;
1402
1403            uint32_t heuristic = ap_path_heuristic(neighbor, dst_tl, dst_br);
1404            if (heuristic == 0) {
1405                assert_bp(XYIN(neighbor, dst_tl, dst_br));
1406                final_xy = neighbor;
1407                state[neighbor.y][neighbor.x].from = &state[node.y][node.x];
1408                state[neighbor.y][neighbor.x].gscore = gscore;
1409                goto search_done;
1410            }
1411
1412            min_heuristic = MIN(heuristic, min_heuristic);
1413            uint32_t fscore = gscore + heuristic;
1414            state[neighbor.y][neighbor.x].from = &state[node.y][node.x];
1415            state[neighbor.y][neighbor.x].gscore = gscore;
1416            state[neighbor.y][neighbor.x].fscore = fscore;
1417            
1418            // Issue: neighbor.y == grid.y
1419            assert_bp(XYUNDER(neighbor, grid));
1420            assert_bp(state[neighbor.y][neighbor.x].ledge != 0xFF);
1421            pq_push(pq, fscore, &neighbor);
1422        }
1423    }
1424    if (commit) LOG("A* timed out, min heuristic: %lu", min_heuristic);
1425    return -1;
1426search_failed:
1427    if (commit) LOG("A* failed, min heuristic: %lu", min_heuristic);
1428    return -1;
1429search_done:;
1430    //if (commit) LOG("A* done in %zu steps, pq size = %zu", r, pq_size(pq));
1431
1432    struct point_state * next = &state[final_xy.y][final_xy.x];
1433    int final_gscore = next->gscore;
1434    if (!commit) {
1435        return final_gscore;
1436    }
1437
1438    size_t count = 0;
1439    ap_targets[count++] = (struct ap_target) { .tl = XY(
1440            MIN(next->xy.x, destination_br.x - 15),
1441            MIN(next->xy.y, destination_br.y - 15)),
1442        .joypad = 0, .joypad_mask = 0,
1443    };
1444
1445    struct xy last_dir = XY(1, 2);
1446    struct xy last_xy = next->xy;
1447    while (next != NULL && next->from != NULL) {
1448        struct xy dir = XYOP2(next->xy, -, last_xy);
1449        assert_bp(next->from == NULL || !XYEQ(next->from->xy, XY(0,0)));
1450        assert_bp(next->raw_tile != 0x10);
1451
1452        if (!next->ledge) {
1453            if (true || !XYEQ(dir, last_dir)) {
1454                //XXX This is normally bad, but for now assume recoil
1455                //assert_bp(!((next->xy.x <= screen->tl.x + 15) || (next->xy.y <= screen->tl.y + 15)));
1456                //assert_bp(!((next->xy.x >= screen->br.x - 15) || (next->xy.y >= screen->br.y - 15)));
1457                ap_targets[count++] = (struct ap_target) { .tl = next->xy };
1458                //printf("%zu: "PRIXY "\n", count, PRIXYF(ap_targets[count-1]));
1459            }
1460        }
1461        last_dir = dir;
1462        last_xy = next->xy;
1463        next = next->from;
1464    }
1465
1466    int timeout = ap_set_targets(count);
1467    ap_target_dst_tl = destination_tl;
1468    ap_target_dst_br = destination_br;
1469    ap_target_screen = screen;
1470    ap_target_scripted = false;
1471    ap_print_map_screen(screen);
1472    return final_gscore;
1473}
1474
1475static int
1476ap_pathfind_node_distance(const struct ap_node * src, const struct ap_node * dst) 
1477{
1478    assert(src->screen == dst->screen);
1479    if (src->type == NODE_NONE) {
1480        // Fake node; have to use ap_pathfind_local
1481        return ap_pathfind_local(src->screen, src->tl, dst->tl, dst->br, false);
1482    }
1483    assert(dst->type != NODE_NONE);
1484    //assert_bp(src->screen->distances_length > 0);
1485    const struct ap_node_distance *dists = src->screen->distances;
1486    for (size_t i = 0; i < src->screen->distances_length; i++) {
1487        if (dists[i].src == src && dists[i].dst == dst) {
1488            return dists[i].distance;
1489        }
1490    }
1491    return -1;
1492}
1493
1494static int
1495ap_pathfind_global(struct xy start_xy, struct ap_node * destination, bool commit, int _max_distance)
1496{
1497    uint64_t max_distance = UINT64_MAX;
1498    if (_max_distance > 0) {
1499        max_distance = (uint64_t) _max_distance;
1500    }
1501
1502    struct ap_screen * start_screen = map_screens[XYMAPSCREEN(start_xy)];
1503    if (start_screen == NULL) {
1504        LOG("start screen == NULL");
1505        return -1;
1506    }
1507    if (destination->_debug_blocked) {
1508        return -1;
1509    }
1510
1511    static uint64_t iter = 0;
1512    iter += 2;
1513    static struct ap_node _start_node;
1514    struct ap_node * start_node = &_start_node;
1515    *start_node = (struct ap_node) {
1516        .screen = start_screen,
1517        .tl = start_xy,
1518        .type = NODE_NONE,
1519        .pgsearch = {
1520            .iter = iter,
1521            //.xy = start_xy,
1522            .from = NULL,
1523            .distance = 0,
1524        }
1525    };
1526
1527    if (start_screen == destination->screen) {
1528        int local_dist = ap_pathfind_local(start_screen, start_xy, destination->tl, destination->br, commit);
1529        if (local_dist >= 0) {
1530            destination->pgsearch.iter = iter;
1531            destination->pgsearch.from = start_node;
1532            return local_dist;
1533        }
1534    }
1535
1536    static struct pq * pq = NULL;
1537    if (pq == NULL) {
1538        pq = pq_create(sizeof(struct ap_node *));
1539        if (pq == NULL) exit(1);
1540    }
1541    pq_clear(pq);
1542    pq_push(pq, 0, &start_node);
1543
1544    size_t r = 0;
1545    while (true) {
1546        r++;
1547        struct ap_node * node;
1548        uint64_t distance;
1549        if (pq_pop(pq, &distance, &node) < 0) 
1550            goto search_failed;
1551        if (distance > max_distance)
1552            goto search_too_far;
1553        if (node->pgsearch.iter == iter + 1)
1554            continue;
1555        bool unlockable = false;
1556        const struct ap_room_tag * unlock_tag = NULL;
1557        if (ap_node_islocked(node, &unlockable, &unlock_tag) && !unlockable)
1558            continue;
1559        assert(node->pgsearch.iter == iter);
1560        node->pgsearch.iter++;
1561        if (node == destination)
1562            goto search_done;
1563        assert(node->type == NODE_TRANSITION || node->type == NODE_KEYBLOCK || node == start_node);
1564
1565        // Try going to an adjacent screen
1566        if (node->adjacent_node != NULL && node->adjacent_direction != 0) {
1567            uint64_t d = node->pgsearch.distance + 1000;
1568            if ((node->adjacent_node->pgsearch.iter == iter &&
1569                 node->adjacent_node->pgsearch.distance < d) ||
1570                 node->adjacent_node->pgsearch.iter < iter) {
1571                node->adjacent_node->pgsearch = (struct ap_node_pgsearch) {
1572                    .iter = iter,
1573                    //.xy = XYMID(node->adjacent_node->tl, node->adjacent_node->br),
1574                    .from = node,
1575                    .distance = d,
1576                };
1577                pq_push(pq, d, &node->adjacent_node);
1578            }
1579        }
1580        
1581        // Try going to nodes on the same screen
1582        for (struct ap_node * adj_node = node->screen->node_list->next; adj_node != node->screen->node_list; adj_node = adj_node->next) {
1583            if (adj_node == node)
1584                continue;
1585            if (adj_node->_debug_blocked)
1586                continue;
1587            if (!(adj_node == destination || adj_node->type == NODE_TRANSITION || adj_node->type == NODE_KEYBLOCK))
1588                continue;
1589            if (adj_node->pgsearch.iter == iter + 1)
1590                continue;
1591            // Don't 'hop through' transition nodes on the same screen
1592            if (adj_node->type == NODE_TRANSITION && node->type == NODE_TRANSITION &&
1593                node->pgsearch.from != NULL && node->pgsearch.from->screen == node->screen && node->pgsearch.from->type == NODE_TRANSITION)
1594                continue;
1595            //int delta_distance = ap_pathfind_local(node->screen, node->pgsearch.xy, adj_node->tl, adj_node->br, false);
1596            int delta_distance = ap_pathfind_node_distance(node, adj_node);
1597            if (delta_distance < 0)
1598                continue;
1599
1600            uint64_t d = node->pgsearch.distance + delta_distance;
1601            if ((adj_node->pgsearch.iter == iter &&
1602                 adj_node->pgsearch.distance > d) ||
1603                 adj_node->pgsearch.iter < iter) {
1604                 adj_node->pgsearch = (struct ap_node_pgsearch) {
1605                    .iter = iter,
1606                    //.xy = XYMID(adj_node->tl, adj_node->br),
1607                    .from = node,
1608                    .distance = d,
1609                };
1610                pq_push(pq, d, &adj_node);
1611            }
1612        }
1613    }
1614search_too_far:
1615    if (commit) LOG("Search exceeded limit after %zu steps (max_distance=%d)", r, _max_distance);
1616    return _max_distance + 1;
1617search_failed:
1618    if (commit) LOG("Search failed after %zu steps (max_distance=%d)", r, _max_distance);
1619    return -1;
1620search_done:
1621    if (commit) LOG("Search done in %zu steps, pq size = %zu", r, pq_size(pq));
1622    else return destination->pgsearch.distance;
1623
1624    struct ap_node * next = destination;
1625    size_t count = 0;
1626    while (next != NULL) {
1627        assert(next->pgsearch.iter == iter + 1);
1628        LOG("    %zu: %s %ld", count, next->name, next->pgsearch.distance);
1629        next = next->pgsearch.from;
1630        count++;
1631    }
1632
1633    return destination->pgsearch.distance;
1634}
1635
1636void
1637ap_print_map_graph()
1638{
1639    FILE *graphf = NONNULL(fopen("full_map.dot", "w"));
1640    fprintf(graphf, "digraph map {\nconcatenate=true\n");
1641    struct xy link = ap_link_xy();
1642    int32_t link_min_dist = INT32_MAX;
1643    struct ap_node * link_nearest_node = NULL;
1644
1645    FILE *screenf = NONNULL(fopen("screen_map.dot", "w"));
1646    fprintf(screenf, "strict digraph map {\ngraph [concentrate=true];\n");
1647
1648    for (struct xy xy = XY(0, 0); xy.y < 0x8000; xy.y += 0x100) {
1649        if (!map_screen_mask_y[xy.y / 0x100]) continue;
1650        for (xy.x = 0; xy.x < 0xC000; xy.x += 0x100) {
1651            if (!map_screen_mask_x[xy.x / 0x100]) continue;
1652            struct ap_screen * screen = map_screens[XYMAPSCREEN(xy)];
1653            if (screen == NULL || !XYEQ(screen->tl, xy)) continue;
1654
1655            fprintf(graphf, "subgraph cluster_%p { color=black; label=\"%s\"\n", screen, screen->name);
1656            fprintf(screenf, "s%#x [label=\"%s\" shape=\"rect\"];\n", screen->id, screen->name);
1657            if (XYIN(link, screen->tl, screen->br)) {
1658                fprintf(screenf, "s%#x [color=\"green\"];\n", screen->id);
1659            }
1660
1661            for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
1662                const char *shape = "oval";
1663                switch (node->type) {
1664                case NODE_TRANSITION: shape = "box"; break;
1665                case NODE_CHEST: shape = "house"; break;
1666                case NODE_ITEM: shape = "house"; break;
1667                case NODE_SWITCH: shape = "parallelogram"; break;
1668                default: break;
1669                }
1670                const char *special = "";
1671                if (node->_debug_blocked) {
1672                    special = "fillcolor=grey";
1673                }
1674                fprintf(graphf, " n%p [label=\"%s\" shape=%s %s]\n", node, node->name, shape, special);
1675            }
1676            fprintf(graphf, "}\n");
1677
1678            for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
1679                struct xy node_mid = XYMID(node->tl, node->br);
1680                if (XYL1DIST(link, node_mid) < link_min_dist) {
1681                    link_min_dist = XYL1DIST(link, node_mid);
1682                    link_nearest_node = node;
1683                }
1684
1685                if (node->adjacent_node != NULL) {
1686                    fprintf(graphf, " n%p -> n%p [label=%s color=purple]\n", node, node->adjacent_node, dir_names[node->adjacent_direction]);
1687                    fprintf(screenf, "s%#x -> s%#x;\n", screen->id, node->adjacent_node->screen->id);
1688                }
1689                if (node->type == NODE_CHEST) {
1690                    if (ap_pathfind_node(node, false, -1) < 0) {
1691                        fprintf(screenf, "s%#x [color=red];\n", screen->id);
1692                    } else {
1693                        fprintf(screenf, "s%#x [color=blue];\n", screen->id);
1694                    }
1695                }
1696
1697                for (struct ap_node * node2 = node->next; node2 != screen->node_list; node2 = node2->next) {
1698                    if (node->type != NODE_TRANSITION && node2->type != NODE_TRANSITION) continue;
1699                    int dist1 = ap_pathfind_local(screen, XYMID(node->tl, node->br), node2->tl, node2->br, false);
1700                    int dist2 = ap_pathfind_node_distance(node, node2);
1701                    if (dist2 >= 0) {
1702                        fprintf(graphf, " n%p -> n%p [color=blue dir=both]\n", node, node2);
1703                        //assert(node->type != NODE_TRANSITION || dist1 >= 0);
1704                    } else {
1705                        //assert(node->type != NODE_TRANSITION || dist1 < 0);
1706                    }
1707                }
1708            }
1709        }
1710    }
1711
1712    for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
1713        if (goal->node == NULL) continue;
1714        fprintf(graphf, " n%p [color=red];\n", goal->node);
1715    }
1716
1717    fprintf(graphf, " n%p [color=green];\n", link_nearest_node);
1718    fprintf(graphf, "}\n");
1719    fclose(graphf);
1720
1721    fprintf(screenf, "}\n");
1722    fclose(screenf);
1723    LOG("Exported map graph to full_map.dot and screens to screen_map.dot");
1724}
1725
1726static void
1727ap_print_pathfind_local_state(struct point_state *buf, struct xy grid) {
1728    struct point_state (*state)[grid.x + 2] = (void *) &buf[0x83];
1729    struct xy link = ap_link_xy();
1730    link = XYOP1(link, & 0xFFF8);
1731
1732    FILE * mapf = fopen("local_cost.pgm.tmp", "w");
1733    fprintf(mapf, "P6 %u %u %u\n", grid.x, grid.y, 255);
1734    for (uint16_t y = 0; y < grid.y; y++) {
1735        for (uint16_t x = 0; x < grid.x; x++) {
1736            uint32_t cost = state[y][x].cost;
1737            uint8_t c = 0;
1738            if (cost >= (1 << 20)) {
1739                c = 0;
1740            } else if (cost >= 10000) {
1741                c = 25;
1742            } else if (cost >= 1000) {
1743                c = 40;
1744            } else if (cost > 200) {
1745                c = 50;
1746            } else {
1747                c = 255 - cost;
1748            }
1749            if (XYEQ(state[y][x].xy, link)) {
1750                fputc(0, mapf);
1751                fputc(255, mapf);
1752                fputc(c, mapf);
1753            } else {
1754                fputc(c, mapf);
1755                fputc(c, mapf);
1756                fputc(c, mapf);
1757            }
1758        }
1759    }
1760    fclose(mapf);
1761    rename("local_cost.pgm.tmp", "local_cost.pgm");
1762}
1763
1764void
1765ap_print_map_full()
1766{
1767    ap_print_state();
1768    ap_print_map_graph();
1769    FILE * mapf = fopen("full_map.pgm.tmp", "w");
1770    uint16_t size_x = 0, size_y = 0;
1771    for (size_t i = 0; i < 0xC0; i++) {
1772        if (map_screen_mask_x[i]) size_x += 0x100 / 8;
1773        if (map_screen_mask_y[i]) size_y += 0x100 / 8;
1774    }
1775    fprintf(mapf, "P6 %u %u %u\n", size_x, size_y, 255);
1776    //fprintf(mapf, "P6 %u %u %u\n", 0x800, 0x800, 255);
1777
1778    struct xy link = ap_link_xy();
1779    struct xy link_tl = link;
1780    struct xy link_br = XYOP1(link, + 15);
1781
1782    // Overworld: (0, 0)
1783    // Dark World: (0, 1)?
1784    // Underworld: (4, 0) x (7, 2)?
1785    // Zora's Domain: ?
1786    // Pedistal: ?
1787    //for (struct xy xy = XY(0, 0); xy.y < 0x8000; xy.y += 8) {
1788    for (struct xy xy = XY(0, 0); xy.y < 0x8000; xy.y += 8) {
1789        if (!map_screen_mask_y[xy.y / 0x100]) continue;
1790        //for (xy.x = 0; xy.x < 0x8000; xy.x += 8) {
1791        for (xy.x = 0; xy.x < 0xC000; xy.x += 8) {
1792            if (!map_screen_mask_x[xy.x / 0x100]) continue;
1793            //if (xy.x >= 0x1000 && xy.x < 0x4000) continue;
1794            //if ((xy.y & 0x2000) || (xy.x & 0x2000)) continue;
1795            if ((xy.y % 0x1000) == 0 || (xy.x % 0x1000) == 0) {
1796                fputc(200, mapf);
1797                fputc(0, mapf);
1798                fputc(200, mapf);
1799                goto next_point;
1800            }
1801            uint8_t tile_attr = 0x80; // default: grey
1802            struct ap_screen * screen = map_screens[XYMAPSCREEN(xy)];
1803            if (screen != NULL) {
1804                tile_attr = ap_map_attr(xy);
1805            }
1806            if (screen != NULL && (xy.y % 0x100) == 8 && (xy.x % 0x100) == 8) {
1807                if (screen->dungeon_room != (uint16_t) -1) {
1808                    uint16_t state = ap_ram.sram_room_state[screen->dungeon_room];
1809                    //LOG("Room: %#6x State: %#6x", screen->dungeon_room, state);
1810                    if ((xy.y % 0x200) == 8 && (xy.x % 0x200) == 8) {
1811                        fputc(100, mapf); fputc(50, mapf); fputc((state & 0x8) ? 255 : 20, mapf);
1812                    } else if ((xy.y % 0x200) == 8) {
1813                        fputc(100, mapf); fputc(50, mapf); fputc((state & 0x4) ? 255 : 20, mapf);
1814                    } else if ((xy.x % 0x200) == 8) {
1815                        fputc(100, mapf); fputc(50, mapf); fputc((state & 0x2) ? 255 : 20, mapf);
1816                    } else {
1817                        fputc(100, mapf); fputc(50, mapf); fputc((state & 0x1) ? 255 : 20, mapf);
1818                    }
1819                    goto next_point;
1820                }
1821            }
1822            if (screen != NULL && (xy.y % 0x200) == 16 && (xy.x % 0x200) < (8 * 13)) {
1823                uint16_t state = ap_ram.sram_room_state[screen->dungeon_room];
1824                uint8_t bit = (xy.x % 0x200) / 8;
1825                if (bit > 0 && screen->dungeon_room != (uint16_t) -1) {
1826                    fputc(100, mapf);
1827                    fputc((state & (1 << (15 - bit))) ? 255 : 20, mapf);
1828                    fputc(20, mapf);
1829                    goto next_point;
1830                }
1831            }
1832            if (XYIN(xy, link_tl, link_br)) {
1833                fputc(0, mapf);
1834                fputc(128, mapf);
1835                fputc(0, mapf);
1836                goto next_point;
1837            }
1838            for (size_t i = 0; i < ap_target_count; i++) {
1839                if (XYIN(xy, ap_targets[i].tl, XYOP1(ap_targets[i].tl, +8))) {
1840                    fputc(0, mapf);
1841                    fputc(255 - tile_attr, mapf);
1842                    fputc(255, mapf);
1843                    goto next_point;
1844                }
1845            }
1846            for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
1847                if (goal->node == NULL) {
1848                    continue;
1849                }
1850                if (XYIN(xy, goal->node->tl, goal->node->br)) {
1851                    //if (ap_graph_is_blocked(&goal->graph)) {
1852                    if (!ap_req_is_satisfied(&goal->req)) {
1853                        fprintf(mapf, "%c%c%c", 200, 0, 100);
1854                    } else {
1855                        fprintf(mapf, "%c%c%c", 255, 255 - goal->attempts * 64, 0);
1856                    }
1857                    goto next_point;
1858                }
1859            }
1860            if (screen != NULL) {
1861                for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
1862                    if (node->adjacent_node == NULL)
1863                        continue;
1864                    if (!XYIN(xy, node->tl, node->br))
1865                        continue;
1866                    fprintf(mapf, "%c%c%c", 128, 255, 0);
1867                    goto next_point;
1868                }
1869            }
1870            fputc(255 - tile_attr, mapf);
1871            fputc(255 - tile_attr, mapf);
1872            fputc(255 - tile_attr, mapf);
1873next_point:;
1874        }
1875    }
1876    fclose(mapf);
1877    rename("full_map.pgm.tmp", "full_map.pgm");
1878    LOG("Exported full map");
1879}
1880
1881void
1882ap_print_state()
1883{
1884    FILE * mapf = fopen("map_attrs.pgm", "w");
1885    fprintf(mapf, "P5 %u %u %u\n", 0x1000, 0x1000, 255);
1886    for (struct xy xy = XY(0, 0); xy.y < 0x8000; xy.y += 8) {
1887        for (xy.x = 0; xy.x < 0x8000; xy.x += 8) {
1888            uint8_t tile_attr = 0xFF; // default
1889            struct ap_screen * screen = map_screens[XYMAPSCREEN(xy)];
1890            if (screen != NULL) {
1891                tile_attr = ap_map_attr(xy);
1892            }
1893            fputc(tile_attr, mapf);
1894        }
1895    }
1896    fclose(mapf);
1897    LOG("Exported map_attrs.pgm");
1898
1899    FILE * screenf = fopen("screens.txt", "w");
1900    for (size_t i = 0; i < sizeof(map_screens) / sizeof(*map_screens); i++) {
1901        struct ap_screen * screen = map_screens[i];
1902        if (screen == NULL)
1903            continue;
1904        if (XYMAPSCREEN(screen->tl) != i)
1905            continue;
1906        fprintf(screenf, "[screen %s]\n", screen->name);
1907        for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
1908            struct ap_node n;
1909            n.tl = XYOP2(node->tl, -, screen->tl);
1910            n.br = XYOP2(node->br, -, screen->tl);
1911            fprintf(screenf, "    node: %s " PRIBBWH " ", node->name, PRIBBWHF(n));
1912            if (node->type == NODE_TRANSITION) {
1913                if (node->adjacent_node) {
1914                    fprintf(screenf, "to screen %s node %s", node->adjacent_node->screen->name, node->adjacent_node->name);
1915                } else {
1916                    fprintf(screenf, "unmapped");
1917                }
1918            }
1919            fprintf(screenf, "\n");
1920        }
1921    }
1922    fclose(screenf);
1923    LOG("Exported screens.txt");
1924}
1925
1926int
1927ap_pathfind_node(struct ap_node * node, bool commit, int max_distance)
1928{
1929    /*
1930    if (node->adjacent_direction) {
1931        struct xy offset = XYOP1(dir_dxy[node->adjacent_direction], * -7);
1932        tl = XYOP2(tl, +, offset);
1933        br = XYOP2(br, +, offset);
1934    }
1935    */
1936    bool unlockable = false;
1937    const struct ap_room_tag * unlock_tag = NULL;
1938    if (ap_node_islocked(node, &unlockable, &unlock_tag) && !unlockable) {
1939        return -1;
1940    }
1941    struct xy link = ap_link_xy();
1942    ap_target_sprite_index = 0;
1943    return ap_pathfind_global(link, node, commit, max_distance);
1944}
1945
1946int
1947ap_pathfind_sprite(size_t sprite_idx)
1948{
1949    struct xy link = ap_link_xy();
1950    struct ap_screen * screen = map_screens[XYMAPSCREEN(link)];
1951    assert(screen != NULL);
1952    assert_bp(XYIN(ap_sprites[sprite_idx].tl, screen->tl, screen->br));
1953    ap_target_sprite_index = sprite_idx;
1954    return ap_pathfind_local(screen, link, ap_sprites[sprite_idx].tl, ap_sprites[sprite_idx].br, true);
1955}
1956
1957static void
1958array_reverse(void *array_base, size_t item_size, size_t array_len) {
1959    uint8_t buf[item_size];
1960    uint8_t (*array)[item_size] = array_base;
1961    for (size_t i = 0; i < array_len / 2; i++) {
1962        size_t j = array_len - 1 - i;
1963        assert(j > i);
1964        memcpy(buf, array[i], item_size);
1965        memcpy(array[i], array[j], item_size);
1966        memcpy(array[j], buf, item_size);
1967    }
1968}
1969static void
1970array_reverse_test() {
1971    char buf[32] = "123 456 789 abc def ";
1972    array_reverse(buf, 4, 5);
1973    assert_bp(strcmp(buf, "def abc 789 456 123 ") == 0);
1974    array_reverse(buf, 4, 4);
1975    assert_bp(strcmp(buf, "456 789 abc def 123 ") == 0);
1976    array_reverse(buf, 1, 3);
1977    assert_bp(strcmp(buf, "654 789 abc def 123 ") == 0);
1978}
1979
1980int
1981ap_set_script(const struct ap_script * script) {
1982    LOG("Setting script: %s", script->name);
1983    assert(script->type == SCRIPT_SEQUENCE);
1984    assert(script->sequence != NULL);
1985    struct xy xy = script->start_tl;
1986    size_t i = 0;
1987    uint16_t dir_mask = SNES_MASK(UP) | SNES_MASK(DOWN) | SNES_MASK(LEFT) | SNES_MASK(RIGHT);
1988    ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad = 0, .joypad_mask = 0 };
1989    for (const char *s = script->sequence; *s != '\0'; s++) {
1990        switch (*s) {
1991        case '<':
1992            xy.x -= 16;
1993            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad = 0, .joypad_mask = 0 };
1994            break;
1995        case '>':
1996            xy.x += 16;
1997            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad = 0, .joypad_mask = 0 };
1998            break;
1999        case '^':
2000            xy.y -= 16;
2001            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad = 0, .joypad_mask = 0 };
2002            break;
2003        case 'v':
2004            xy.y += 16;
2005            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad = 0, .joypad_mask = 0 };
2006            break;
2007        case 'A':
2008            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(A), .joypad = 0, };
2009            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(A), .joypad = SNES_MASK(A), };
2010            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(A), .joypad = 0, };
2011            break;
2012        case 'B':
2013            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(B), .joypad = 0, };
2014            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(B), .joypad = SNES_MASK(B), };
2015            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(B), .joypad = 0, };
2016            break;
2017        case 'Y':
2018            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(Y), .joypad = 0, };
2019            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(Y), .joypad = SNES_MASK(Y), };
2020            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = SNES_MASK(Y), .joypad = 0, };
2021            break;
2022        case 'U':
2023            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = dir_mask, .joypad = 0, };
2024            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = dir_mask, .joypad = SNES_MASK(UP), };
2025            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = dir_mask, .joypad = 0, };
2026            break;
2027        case 'D':
2028            ap_targets[i++] = (struct ap_target) { .tl = xy, .joypad_mask = dir_mask, .joypad = SNES_MASK(DOWN), };
2029            break;
2030        default:
2031            LOG("Unhandled character in sequence: '%c'", *s);
2032            assert_bp(false);
2033            break;
2034        }
2035    }
2036    if (ap_targets[i].joypad != 0) {
2037        ap_targets[i+1] = ap_targets[i];
2038        ap_targets[i+1].joypad = 0;
2039        ap_targets[i+1].joypad_mask = 0;
2040        i++;
2041    }
2042    array_reverse(ap_targets, sizeof(*ap_targets), i);
2043    int timeout = ap_set_targets(i);
2044    ap_target_dst_tl = xy;
2045    ap_target_dst_br = XYOP1(xy, + 15);
2046    ap_target_screen = map_screens[XYMAPSCREEN(xy)];
2047    ap_target_scripted = true;
2048
2049    LOG("Script timeout: %d", timeout);
2050    return timeout;
2051}
2052
2053static void
2054ap_screen_add_raw_node(struct ap_screen * screen, struct ap_node * new_node)
2055{
2056    // Pair overlays
2057    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2058        if (XYEQ(node->tl, new_node->tl) && XYEQ(node->br, new_node->br)) {
2059            if (node->type == NODE_TRANSITION && new_node->type == NODE_OVERLAY) {
2060                node->lock_node = new_node;
2061            } else if (node->type == NODE_OVERLAY && new_node->type == NODE_TRANSITION) {
2062                new_node->lock_node = node;
2063            }
2064        }
2065    }
2066
2067    // Attach to screen
2068    new_node->screen = screen;
2069    LL_INIT(new_node);
2070    LL_PUSH(screen->node_list, new_node);
2071    LOG("attached node %s", new_node->name);
2072
2073    // XXX This needs to be refactored to pair switches w/ doors
2074    // XXX Moved to ap_node_islocked
2075    /*
2076    if (new_node->type == NODE_SWITCH) {
2077        // Assign all doors to this switch
2078        for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2079            if (node->lock_node == NULL && !XYEQ(node->locked_xy, XY(0, 0))) {
2080                LOGB("Assigning switch %s to unlock door %s", new_node->name, node->name);
2081                node->lock_node = new_node;
2082                break;
2083            }
2084        }
2085    }
2086    if (!XYEQ(new_node->locked_xy, XY(0, 0))) {
2087        // Assign the first switch we find to this door
2088        for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2089            if (node->type == NODE_SWITCH) {
2090                LOGB("Assigning switch %s to unlock door %s", node->name, new_node->name);
2091                new_node->lock_node = node;
2092                break;
2093            }
2094        }
2095        LOGB("Unlock door %s = %p", new_node->name, new_node->lock_node);
2096    }
2097    */
2098
2099    // Add goals & locations
2100    switch (new_node->type) {
2101    case NODE_TRANSITION:
2102        if (add_explore_goals_global || (screen->info != NULL && screen->info->add_explore_goals)) {
2103            if (new_node->adjacent_node == NULL && new_node->adjacent_direction != 0)
2104                new_node->goal = ap_goal_add(GOAL_EXPLORE, new_node);
2105        }
2106        break;
2107    case NODE_ITEM:
2108        //ap_item_loc_add(new_node);
2109        new_node->goal = ap_goal_add(GOAL_PICKUP, new_node);
2110        break;
2111    case NODE_CHEST:
2112        new_node->locked_xy = XYOP2(new_node->tl, -, XY(0, 16));
2113        ap_item_loc_add(new_node);
2114        new_node->goal = ap_goal_add(GOAL_CHEST, new_node);
2115        break;
2116    case NODE_KEYBLOCK:
2117        //new_node->goal = ap_goal_add(GOAL_XXX new_node);
2118        break;
2119    case NODE_SWITCH:
2120        //ap_goal_add(GOAL_EXPLORE, new_node);
2121        break;
2122    case NODE_SPRITE:;
2123        uint16_t attrs = ap_sprite_attrs_for_type(new_node->sprite_type, new_node->sprite_subtype, screen->dungeon_room);
2124        ap_item_loc_add(new_node);
2125        if (attrs & SPRITE_ATTR_TALK) {
2126            new_node->goal = ap_goal_add(GOAL_NPC, new_node);
2127        } else if (attrs & SPRITE_ATTR_ITEM)  {
2128            ap_goal_add(GOAL_PICKUP, new_node);
2129        }
2130        break;
2131    case NODE_SCRIPT:
2132        new_node->goal = ap_goal_add(GOAL_SCRIPT, new_node);
2133        break;
2134    case NODE_OVERLAY:
2135        break;
2136    case NODE_NONE:
2137    default:
2138        assert_bp(false);
2139        ;
2140    }
2141
2142    // Update Graph
2143    /*
2144    if (new_node->goal != NULL) {
2145        ap_graph_add_prereq(&new_node->goal->graph, &screen->graph);
2146
2147        for (struct ap_node * node2 = screen->node_list->next; node2 != screen->node_list; node2 = node2->next) {
2148            if (node2 == new_node) continue;
2149            if (new_node->goal == NULL || node2->goal == NULL) continue;
2150            if (new_node->type != NODE_TRANSITION && node2->type != NODE_TRANSITION) continue;
2151            int dist1 = ap_pathfind_local(screen, XYMID(new_node->tl, new_node->br), node2->tl, node2->br, false);
2152            int dist2 = ap_pathfind_local(screen, XYMID(node2->tl, node2->br), new_node->tl, new_node->br, false);
2153            if (dist1 >= 0 && dist2 >= 0) {
2154                if (new_node->type == NODE_TRANSITION && node2->type == NODE_TRANSITION) {
2155                    // XXX only the first new equiv is actually added; transitivity is assumed
2156                    ap_graph_add_equiv(&node2->goal->graph, &new_node->goal->graph);
2157                } else if (new_node->type == NODE_TRANSITION) {
2158                    ap_graph_add_prereq(&node2->goal->graph, &new_node->goal->graph);
2159                } else {
2160                    ap_graph_add_prereq(&new_node->goal->graph, &node2->goal->graph);
2161                }
2162            }
2163        }
2164    }
2165    */
2166    if (new_node->screen->id == 0x1481 && new_node->adjacent_direction == DIR_L) {
2167        // Door into EP stalfos room
2168        //new_node->_debug_blocked = true;
2169    }
2170    if (strcmp(new_node->name, "door 0x5e") == 0 || strcmp(new_node->name, "door 0x65") == 0) {
2171        // Stateful Fairy Fountain room
2172        new_node->_debug_blocked = true;
2173    }
2174    if (strcmp(new_node->name, "door 0x11") == 0 || strcmp(new_node->name, "door 0x38") == 0) {
2175        // One-way overworld doors
2176        new_node->_debug_blocked = true;
2177    }
2178
2179    /*
2180    if (strcmp(new_node->name, "0x198A D 4") == 0) {
2181        new_node->_debug_blocked = false;
2182    } else if (strcmp(new_node->name, "0x1483 L 3") == 0) {
2183        new_node->_debug_blocked = true;
2184    } else if (strcmp(new_node->name, "0x148A L 4 l") == 0) {
2185        new_node->_debug_blocked = true;
2186    //} else if (new_node->screen->id == 0x0e50 && strcmp(new_node->name, "stairs U 0x5e") == 0) {
2187    //    new_node->_debug_blocked = true;
2188    } else if (new_node->screen->id == 0x0c0a && strncmp(new_node->name, "door", 4) == 0) {
2189        new_node->_debug_blocked = true;
2190    } else if (new_node->screen->id == 0x0402 && strncmp(new_node->name, "door", 4) == 0) {
2191        new_node->_debug_blocked = true;
2192    }
2193    */
2194
2195}
2196
2197static struct ap_node *
2198ap_screen_commit_node(struct ap_screen * screen, struct ap_node ** new_node_p)
2199{
2200    struct ap_node * new_node = *new_node_p;
2201    NONNULL(new_node);
2202
2203    if (new_node->tl.x == 0 && new_node->tl.y == 0)
2204        return NULL;
2205    assert(new_node->type != NODE_NONE);
2206    assert(XYUNDER(new_node->tl, new_node->br));
2207
2208    // Check if the node already exists
2209    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2210        if (node->type != new_node->type)
2211            continue;
2212        if (node->adjacent_direction != new_node->adjacent_direction)
2213            continue;
2214        if (node->type != NODE_SPRITE) {
2215            if (XYEQ(node->tl, new_node->tl) && XYEQ(node->br, new_node->br)) {
2216                //assert_bp(node->tile_attr == new_node->tile_attr);
2217            } else if (XYIN(new_node->tl, node->tl, node->br) && XYIN(new_node->br, node->tl, node->br)) {
2218                //assert(node->adjacent_screen == new_node->adjacent_screen);
2219                // XXX This has a lot of false positives
2220                //assert_bp(node->tile_attr == new_node->tile_attr);
2221
2222                //LOG("duplicate node %s == %s", node->name, new_node->name);
2223
2224                // Merge in new information???
2225                LOGB("node tl/br would shrink: before: " PRIBBV " after: " PRIBBV, PRIBBVF(*node), PRIBBVF(*new_node));
2226                new_node->tl = node->tl;
2227                new_node->br = node->br;
2228            } else if (node->type == NODE_TRANSITION && XYIN(node->tl, new_node->tl, new_node->br) && XYIN(node->br, new_node->tl, new_node->br)) {
2229                //assert_bp(node->tile_attr == new_node->tile_attr);
2230                LOGB("node tl/br grew: before: " PRIBBV " after: " PRIBBV, PRIBBVF(*node), PRIBBVF(*new_node));
2231            } else {
2232                continue;
2233            }
2234        }
2235
2236        node->tl = new_node->tl;
2237        node->br = new_node->br;
2238        node->overlay_index = new_node->overlay_index;
2239        if (!XYEQ(new_node->locked_xy, XY(0, 0))) {
2240            node->locked_xy = new_node->locked_xy; 
2241        }
2242
2243        memset(new_node, 0, sizeof *new_node);
2244        return node;
2245    }
2246
2247    // Merge paired stairs from upper to lower levels
2248    if (new_node->type == NODE_TRANSITION && (ap_tile_attrs[new_node->tile_attr] & TILE_ATTR_STRS) && new_node->adjacent_node == NULL && XYINDOORS(new_node->tl)) {
2249        struct xy alt_xy = XYOP2(XYFLIPBG(new_node->tl), + 48 *, dir_dxy[new_node->adjacent_direction]);
2250        struct ap_screen * alt_screen = map_screens[XYMAPSCREEN(alt_xy)];
2251        if (alt_screen != NULL) {
2252            for (struct ap_node * node = alt_screen->node_list->next; node != alt_screen->node_list; node = node->next) {
2253                if (node->type != NODE_TRANSITION)
2254                    continue;
2255                if (node->adjacent_node != NULL)
2256                    continue;
2257                if (node->adjacent_direction != dir_opp[new_node->adjacent_direction])
2258                    continue;
2259                if (node->tile_attr != new_node->tile_attr)
2260                    continue;
2261                if (!XYIN(alt_xy, node->tl, node->br))
2262                    continue;
2263                //LOG("Paring nodes: %s <-> %s", node->name, new_node->name);
2264                node->adjacent_node = new_node;
2265                new_node->adjacent_node = node;
2266                break;
2267            }
2268        }
2269    }
2270
2271    ap_screen_add_raw_node(screen, new_node);
2272
2273    *new_node_p = NONNULL(calloc(1, sizeof **new_node_p));
2274    return new_node;
2275}
2276
2277static void
2278ap_update_map_screen_nodes()
2279{
2280    // Update the nodes that already exist on the screen
2281    struct xy tl, br;
2282    ap_map_bounds(&tl, &br);
2283    size_t index = XYMAPSCREEN(tl);
2284    struct ap_screen * screen = NONNULL(map_screens[index]);
2285
2286    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2287        switch (node->type) {
2288        case NODE_SWITCH:;
2289            uint16_t mask = 0x0;
2290            switch(node->tile_attr) {
2291                case 0x23: mask = 0x8000; break;
2292                case 0x24: mask = 0x4000; break;
2293                case 0x25: mask = 0x2000; break; // XXX Wild guess
2294                case 0x26: mask = 0x1000; break; // XXX Wild guess
2295                default: assert(false);
2296            }
2297            if (*ap_ram.room_state & mask) {
2298                // what to update?
2299            }
2300            break;
2301        case NODE_SPRITE:
2302            break;
2303        case NODE_CHEST:
2304        case NODE_KEYBLOCK:
2305        case NODE_ITEM:
2306        case NODE_TRANSITION:
2307        case NODE_NONE:
2308        default:;
2309        }
2310    }
2311}
2312
2313static void ap_map_add_nodes_to_screen(struct ap_screen * screen);
2314static void ap_map_add_scripts_to_screen(struct ap_screen * screen);
2315static void ap_map_add_constants_to_screen(struct ap_screen * screen);
2316
2317static int ap_node_distance_cmp(const void *a, const void *b) {
2318    return memcmp(a, b, sizeof(struct ap_node_distance));
2319}
2320
2321static void ap_map_screen_update_distances(struct ap_screen * screen) {
2322    size_t n_transition_nodes = 0;
2323    size_t n_other_nodes = 0;
2324    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2325        if (node->type == NODE_TRANSITION || node->type == NODE_KEYBLOCK) {
2326            n_transition_nodes++;
2327        } else {
2328            n_other_nodes++;
2329        }
2330    }
2331
2332    size_t req_capcacity = n_transition_nodes * (n_transition_nodes + n_other_nodes);
2333    if (req_capcacity > screen->distances_capacity) {
2334        screen->distances = NONNULL(realloc(screen->distances, req_capcacity * sizeof(*screen->distances)));
2335        screen->distances_capacity = req_capcacity;
2336    }
2337    memset(screen->distances, 0, req_capcacity * sizeof(*screen->distances));
2338
2339    size_t d = 0;
2340    for (struct ap_node * src = screen->node_list->next; src != screen->node_list; src = src->next) {
2341        if (src->type != NODE_TRANSITION && src->type != NODE_KEYBLOCK) {
2342            continue;
2343        }
2344        for (struct ap_node * dst = screen->node_list->next; dst != screen->node_list; dst = dst->next) {
2345            int distance = ap_pathfind_local(screen, XYMID(src->tl, src->br), dst->tl, dst->br, false);
2346            if (distance < 0) {
2347                continue;
2348            }
2349            screen->distances[d++] = (struct ap_node_distance) {
2350                .src = src,
2351                .dst = dst,
2352                .distance = (uint64_t) distance,
2353            };
2354        }
2355    }
2356    assert(d <= req_capcacity);
2357    screen->distances_length = d;
2358    qsort(screen->distances, d, sizeof(*screen->distances), &ap_node_distance_cmp);
2359}
2360
2361struct ap_screen *
2362ap_update_map_screen(bool force)
2363{
2364    struct xy tl, br;
2365    ap_map_bounds(&tl, &br);
2366    size_t index = XYMAPSCREEN(tl);
2367    static size_t last_map_index = -1;
2368    //static uint8_t last_trap_doors = 0;
2369    //last_trap_doors = *ap_ram.room_trap_doors;
2370    last_screen = map_screens[index];
2371
2372    struct ap_screen * screen = map_screens[index];
2373    if (screen == NULL) {
2374        struct xy cells[8] = {
2375            XY(0x000, 0x000), XY(0x100, 0x000),
2376            XY(0x000, 0x100), XY(0x100, 0x100),
2377            XY(0x200, 0x000), XY(0x300, 0x000),
2378            XY(0x200, 0x100), XY(0x300, 0x100),
2379        };
2380        bool indoors = XYINDOORS(tl);
2381        for (size_t i = 0; i < (indoors ? 8 : 1); i++) {
2382            struct xy cell_tl, cell_br;
2383            if (indoors) {
2384                cell_tl = XYOP2(XYOP2(tl, &~, cells[7]), |, cells[i]);
2385                ap_map_room_bounds(cell_tl, &cell_tl, &cell_br);
2386            } else {
2387                cell_tl = tl;
2388                cell_br = br;
2389            }
2390            if (map_screens[XYMAPSCREEN(cell_tl)] != NULL) continue;
2391
2392            screen = calloc(1, sizeof *screen);
2393            screen->tl = cell_tl;
2394            screen->br = cell_br;
2395            screen->id = (screen->tl.x >> 8) | (screen->tl.y & 0xFF00);
2396            if (indoors) {
2397                screen->dungeon_id = *ap_ram.dungeon_id / 2;
2398                screen->dungeon_room = *ap_ram.dungeon_room;
2399                screen->dungeon_tags = *ap_ram.dungeon_tags;
2400            } else {
2401                screen->dungeon_id = -1;
2402                screen->dungeon_room = -1;
2403                screen->dungeon_tags = 0;
2404            }
2405            const char * suffix = "";
2406            if (XYINDOORS(screen->tl)) {
2407                if (XYONUPPER(screen->tl)) {
2408                    screen->id &= ~0x2;
2409                    suffix = " ^";
2410                } else {
2411                    suffix = " v";
2412                }
2413            }
2414            for (const struct ap_screen_info * info = ap_screen_infos; info->id != (uint16_t) -1; info++) {
2415                if (info->id == screen->id) {
2416                    screen->info = info;
2417                    break;
2418                }
2419            }
2420            //ap_graph_init(&screen->graph, screen->name);
2421            LL_INIT(screen->node_list);
2422            if (screen->info) {
2423                snprintf(screen->name, sizeof screen->name, "%s%s %#x " PRIXY " x " PRIXY, screen->info->name, suffix, screen->dungeon_tags, PRIXYF(cell_tl), PRIXYF(cell_br));
2424            } else {
2425                snprintf(screen->name, sizeof screen->name, "%#06x%s %#x " PRIXYV " x " PRIXYV, screen->id, suffix, screen->dungeon_tags, PRIXYVF(cell_tl), PRIXYVF(cell_br));
2426
2427            }
2428
2429            struct xy xy;
2430            for (xy.y = cell_tl.y; xy.y < cell_br.y; xy.y += 0x100) {
2431                for (xy.x = cell_tl.x; xy.x < cell_br.x; xy.x += 0x100) {
2432                    map_screens[XYMAPSCREEN(xy)] = screen;
2433                    map_screen_mask_x[xy.x / 0x100] = true;
2434                    map_screen_mask_y[xy.y / 0x100] = true;
2435                }
2436            }
2437
2438            ap_screen_refresh_cache(screen);
2439            ap_map_add_nodes_to_screen(screen);
2440            ap_map_add_scripts_to_screen(screen);
2441            ap_map_add_constants_to_screen(screen);
2442            ap_map_add_sprite_nodes_to_screen(screen);
2443            ap_map_screen_update_distances(screen);
2444        }
2445        screen = map_screens[index];
2446    } else {
2447        bool needs_refresh = !(index == last_map_index && !force);
2448        if (!needs_refresh && !XYINDOORS(screen->tl)) {
2449            ap_update_map_screen_nodes();
2450            return NONNULL(map_screens[index]);
2451        }
2452
2453        bool cache_changed = ap_screen_refresh_cache(screen);
2454        if (!cache_changed && index == last_map_index && !force) {
2455            ap_update_map_screen_nodes();
2456            return NONNULL(map_screens[index]);
2457        }
2458        ap_map_add_nodes_to_screen(screen);
2459        ap_map_add_sprite_nodes_to_screen(screen);
2460    }
2461    last_map_index = index;
2462
2463    ap_map_screen_update_distances(screen);
2464
2465    //ap_graph_mark_done(&screen->graph);
2466    ap_sprites_print();
2467    ap_ancillia_print();
2468    ap_print_map_screen(screen);
2469    // TODO: Skip this if link is not yet on the screen
2470    printf("Nodes: [(un)Reachable? (un)Adjacent?] (tl x br) type \"name\"\n");
2471
2472    int i = 0; 
2473    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2474        bool reachable = ap_pathfind_local(screen, ap_link_xy(), node->tl, node->br, false) >= 0;
2475        node->_reachable = reachable;
2476        printf("   %d. %p [%c%c] (" PRIXY " x " PRIXY ") %s \"%s\"\n",
2477                i++, node, "uR"[reachable], "uA"[node->adjacent_node != NULL], PRIXYF(node->tl), PRIXYF(node->br), ap_node_type_names[node->type], node->name);
2478
2479    }
2480
2481    return screen;
2482}
2483
2484static void ap_map_add_scripts_to_screen(struct ap_screen * screen) {
2485    for (size_t i = 0; i < ARRAYLEN(ap_scripts); i++) {
2486        const struct ap_script * script = &ap_scripts[i];
2487        if (!XYIN(script->start_tl, screen->tl, screen->br)) {
2488            continue;
2489        }
2490
2491        struct ap_node * new_node = NONNULL(calloc(1, sizeof *new_node));
2492        new_node->type = NODE_SCRIPT;
2493        new_node->tl = script->start_tl;
2494        new_node->br = XYOP1(script->start_tl, +15);
2495        new_node->script = script;
2496        snprintf(new_node->name, sizeof new_node->name, "Script: %s", script->name);
2497
2498        ap_screen_add_raw_node(screen, new_node);
2499    }
2500}
2501
2502static void ap_map_add_constants_to_screen(struct ap_screen * screen) {
2503    if (!XYINDOORS(screen->tl)) {
2504        screen->quadmask = QUAD_ALL;
2505        screen->room_tags[0] = NULL;
2506        screen->room_tags[1] = NULL;
2507    } else {
2508        // Quadrants are 0x100 x 0x100 big
2509        screen->quadmask = 0;
2510        struct xy tl = XYOP1(screen->tl, & 0x1FF);
2511        struct xy br = XYOP1(screen->br, & 0x1FF);
2512        struct {
2513            uint8_t q;
2514            struct xy xy;
2515        } points[4] = {
2516            { .q = QUAD_A, .xy = XY(0x080, 0x080), },
2517            { .q = QUAD_B, .xy = XY(0x180, 0x080), },
2518            { .q = QUAD_C, .xy = XY(0x080, 0x180), },
2519            { .q = QUAD_D, .xy = XY(0x180, 0x180), },
2520        };
2521        for (size_t i = 0; i < 4; i++) {
2522            if (XYIN(points[i].xy, tl, br)) {
2523                screen->quadmask |= points[i].q;
2524            }
2525        }
2526
2527        // Room Tags
2528        uint8_t index_1 = screen->dungeon_tags & 0xFF;
2529        uint8_t index_2 = (screen->dungeon_tags >> 8) & 0xFF;
2530        assert(index_1 < 0x40 && index_2 < 0x40);
2531        const struct ap_room_tag * tag_1 = &ap_room_tags[index_1];
2532        const struct ap_room_tag * tag_2 = &ap_room_tags[index_2];
2533
2534        size_t t = 0;
2535        screen->room_tags[0] = NULL;
2536        screen->room_tags[0] = NULL;
2537        if (tag_1->quadmask & screen->quadmask) {
2538            screen->room_tags[t++] = tag_1;
2539            assert_bp(!tag_1->unsure);
2540        }
2541        if (tag_2->quadmask & screen->quadmask) {
2542            screen->room_tags[t++] = tag_2;
2543            assert_bp(!tag_2->unsure);
2544        }
2545
2546        LOGB("Screen tags: %s %s; %s",
2547            ap_room_tag_print(screen->room_tags[0]),
2548            ap_room_tag_print(screen->room_tags[1]),
2549            screen->name);
2550    }
2551}
2552
2553static void
2554ap_map_add_sprite_nodes_to_screen(struct ap_screen * screen) {
2555    static struct ap_node * new_node = NULL;
2556    if (new_node == NULL)
2557        new_node = NONNULL(calloc(1, sizeof *new_node));
2558
2559    for (size_t i = 0; i < N_SPRITES; i++) {
2560        if (ap_sprites[i].type == 0)
2561            continue;
2562        if (ap_sprites[i].attrs & SPRITE_ATTR_NODE) {
2563            new_node->tl = ap_sprites[i].tl;
2564            if (!XYIN(new_node->tl, screen->tl, screen->br)) {
2565                LOG("Sprite %zu out of screen", i);
2566            } else {
2567                new_node->type = NODE_SPRITE;
2568                new_node->br = ap_sprites[i].br;
2569                new_node->sprite_type = ap_sprites[i].type;
2570                new_node->sprite_subtype = ap_sprites[i].subtype;
2571                /*
2572                if (ap_sprites[i].attrs & SPRITE_ATTR_TALK) {
2573                    new_node->tl = XYOP2(ap_sprites[i].hitbox_tl, +, XY(0, 16));
2574                    new_node->br = XYOP2(ap_sprites[i].hitbox_tl, +, XY(15, 31));
2575                }
2576                */
2577                snprintf(new_node->name, sizeof new_node->name, "sprite %#x.%#x %s", new_node->sprite_type, new_node->sprite_subtype, ap_sprite_attr_name(ap_sprites[i].attrs));
2578                ap_screen_commit_node(screen, &new_node);
2579            }
2580        }
2581    }
2582
2583    for (size_t i = 0; i < N_ANCILLIA; i++) {
2584        if (ap_ancillia[i].type == 0)
2585            continue;
2586        if (ap_ancillia[i].attrs & SPRITE_ATTR_NODE) {
2587            new_node->tl = ap_ancillia[i].tl;
2588            if (!XYIN(new_node->tl, screen->tl, screen->br)) {
2589                LOG("Ancillia %zu out of screen", i);
2590            } else {
2591                new_node->type = NODE_SPRITE;
2592                new_node->br = ap_ancillia[i].br;
2593                new_node->sprite_type = ap_ancillia[i].type;
2594                new_node->sprite_subtype = 0;
2595                /*
2596                if (ap_ancillia[i].attrs & SPRITE_ATTR_TALK) {
2597                    new_node->tl = XYOP2(ap_ancillia[i].hitbox_tl, +, XY(0, 16));
2598                    new_node->br = XYOP2(ap_ancillia[i].hitbox_tl, +, XY(15, 31));
2599                }
2600                */
2601                snprintf(new_node->name, sizeof new_node->name, "ancillia %#x %s", new_node->sprite_type, ap_sprite_attr_name(ap_ancillia[i].attrs));
2602                ap_screen_commit_node(screen, &new_node);
2603            }
2604        }
2605    }
2606}
2607
2608static void
2609ap_map_add_nodes_to_screen(struct ap_screen * screen) {
2610    struct xy tl = screen->tl;
2611    struct xy br = screen->br;
2612    size_t index = XYMAPSCREEN(tl);
2613    struct xy link = ap_link_xy();
2614
2615    int new_node_count = 0;
2616    static struct ap_node * new_node = NULL;
2617    if (new_node == NULL)
2618        new_node = NONNULL(calloc(1, sizeof *new_node));
2619
2620    uint16_t lift_mask = TILE_ATTR_LFT0;
2621    if (*ap_ram.inventory_gloves >= 1) lift_mask |= TILE_ATTR_LFT1;
2622    if (*ap_ram.inventory_gloves >= 2) lift_mask |= TILE_ATTR_LFT2;
2623
2624    //uint16_t walk_mask = TILE_ATTR_WALK | TILE_ATTR_SWIM;
2625    uint16_t walk_mask = TILE_ATTR_WALK;
2626    const uint8_t dirs[4] = {
2627        // Top, Botton, Left, Right edges
2628        DIR_U, DIR_D, DIR_L, DIR_R,
2629    };
2630    const struct xy xy_init[4] = {
2631        tl, XY(tl.x, br.y & ~7), tl, XY(br.x & ~7, tl.y),
2632    };
2633    const struct xy xy_step[4] = {
2634        dir_dxy[DIR_R], dir_dxy[DIR_R], dir_dxy[DIR_D], dir_dxy[DIR_D],
2635    };
2636    uint16_t masks[4] = {
2637        walk_mask, walk_mask, walk_mask, walk_mask,
2638    };
2639    uint8_t f = 2;
2640
2641    for (uint8_t k = 0; k < 4; k++) {
2642        uint8_t i = dirs[k];
2643        struct xy new_start = XY(0, 0);
2644        struct xy new_end = XY(0, 0);
2645        size_t size = 0;
2646
2647        for (struct xy xy = xy_init[k]; XYIN(xy, tl, br); xy = XYOP2(xy, +, XYOP1(xy_step[k], * 8))) {
2648            for (; XYIN(xy, tl, br); xy = XYOP2(xy, +, XYOP1(xy_step[k], * 8))) {
2649                bool can_walk = true;
2650                //if (XYINDOORS(xy) && (ap_map_attr(xy) == 0x00 || ap_map_attr(xy) == 0x1c)) {
2651                if (XYINDOORS(xy) && (ap_map_attr_from_ram(xy) == 0x1c)) {
2652                    // NOTE: sometimes 0's are actually walkable
2653                    // inside, border 0 or 1c is just empty
2654                    // you could walk there, but you definitely can't get there
2655                    can_walk = false;
2656                }
2657                for (int8_t j = 0; j < f + 2; j++) {
2658                    struct xy lxy = XYOP2(xy, -, XYOP1(dir_dxy[i], * 8 * j));
2659                    if (!(ap_tile_attrs[ap_map_attr_from_ram(lxy)] & masks[k])) {
2660                        can_walk = false;
2661                        break;
2662                    }
2663                }
2664                if (can_walk) {
2665                    new_end = XYOP2(xy, -, XYOP1(dir_dxy[i], * 8 * (f + 1)));
2666                    for (int8_t j = f + 1; j < f + 8; j++) {
2667                        struct xy lxy = XYOP2(new_end, -, XYOP1(dir_dxy[i], * 8));
2668                        uint8_t attr = ap_map_attr_from_ram(lxy);
2669                        // 0x80 through 0x9F are walkways between rooms
2670                        // 0xF0 through 0xF8 are locked doors?
2671                        if (attr < 0x80)
2672                            break;
2673                        else if (attr > 0x9F && attr < 0xF0)
2674                            break;
2675                        else if (attr > 0xF8)
2676                            break;
2677                        new_end = lxy;
2678                    }
2679                    if (size == 0) {
2680                        //new_start = XYOP2(xy, -, XYOP1(dir_dxy[i], * 8 * f));
2681                        new_start = xy;
2682                    }
2683                    size++;
2684                } else if (size < 2) {
2685                    new_start = new_end = XY(0, 0);
2686                    size = 0;
2687                } else {
2688                    break;
2689                }
2690            }
2691            if (XYEQ(new_start, XY(0, 0)) && XYEQ(new_end, XY(0, 0))) {
2692                size = 0;
2693                continue;
2694            }
2695            new_node->tl = XYFN2(MIN, new_start, new_end);
2696            new_node->br = XYFN2(MAX, new_start, new_end);
2697            new_node->br = XYOP1(new_node->br, + 7);
2698            new_node->type = NODE_TRANSITION;
2699            new_node->adjacent_direction = i;
2700            new_node->tile_attr = ap_map_attr_from_ram(ap_box_edge(new_node->tl, new_node->br, i));
2701            new_node->locked_xy = XY(0, 0);
2702            if ((new_node->tile_attr & 0xF8) == 0x80) { // locked door
2703                new_node->locked_xy = ap_box_edge(new_node->tl, new_node->br, dir_opp[i]);
2704                new_node->locked_xy = XYOP1(new_node->locked_xy, &~7);
2705                uint8_t attr = ap_map_attr_from_ram(new_node->locked_xy);
2706            }
2707            // Skip unreachable border nodes
2708            if (new_node->tile_attr == 0x00 || (!XYINDOORS(link) && new_node->tile_attr == 0x48)) {
2709                bool is_reachable = false;
2710                if (screen->info != NULL && screen->info->include_borders) {
2711                    is_reachable = true;
2712                } else if (XYIN(link, tl, br)) {
2713                    is_reachable = ap_pathfind_local(screen, link, new_node->tl, new_node->br, false) > 0;
2714                } else {
2715                    // XXX This is such a hack
2716                    struct xy test_xy = XYMID(tl, br);
2717                    is_reachable = ap_pathfind_local(screen, test_xy, new_node->tl, new_node->br, false) > 0;
2718                    /*
2719                    for (const struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
2720                        if (ap_pathfind_local(screen, XYMID(node->tl, node->br), new_node->tl, new_node->br, false) > 0) {
2721                            is_reachable = true;
2722                            break;
2723                        }
2724                    }
2725                    */
2726                }
2727                if (!is_reachable) {
2728                //if (!XYIN(link, tl, br) || ap_pathfind_local(screen, link, new_node->tl, new_node->br, false) < 0) {
2729                    //if(!XYINDOORS(link)) assert_bp(false); // unreachable
2730                    new_start = new_end = XY(0, 0);
2731                    size = 0;
2732                    continue;
2733                }
2734            }
2735
2736            //new_node->adjacent_screen = &map_screens[XYMAPSCREEN(XYOP2(new_node->tl, +, XYOP1(dir_dxy[i], * 0x100)))];
2737            snprintf(new_node->name, sizeof new_node->name, "0x%02zX %s %d%s", index, dir_names[i], ++new_node_count, XYEQ(new_node->locked_xy, XY(0, 0)) ? "" : " l");
2738            ap_screen_commit_node(screen, &new_node);
2739            new_start = new_end = XY(0, 0);
2740            size = 0;
2741        }
2742    }
2743    if (!*ap_ram.in_building) {
2744        //LOG("xo: %04x, xm: %04x, yo: %04x, ym: %04x", *ap_ram.map_x_offset, *ap_ram.map_x_mask, *ap_ram.map_y_offset, *ap_ram.map_y_mask);
2745        for (size_t i = 0; i < 0x81; i++) {
2746            if (ap_ram.over_ent_areas[i] != *ap_ram.map_area)
2747                continue;
2748
2749            uint16_t id = ap_ram.over_ent_ids[i];
2750            if (id > 0x85) {
2751                LOG("weird id: %u %zu", id, i);
2752                continue;
2753            }
2754            if (id == 0x5e || id == 0x65) {
2755                // Skip fairy fountain/Fortune teller; all doors lead to the same location and have state
2756                continue;
2757            }
2758
2759            struct xy original_xy = ap_map16_to_xy(tl, ap_ram.over_ent_map16s[i]);
2760            new_node->tl = ap_map16_to_xy(tl, ap_ram.over_ent_map16s[i]);
2761            if (id == 0x3c || id == 0x26 || id == 0x68) {
2762                // Door in a log is oddly placed
2763                new_node->tl.x -= 0x08;
2764            }
2765            new_node->tl.y += 16;
2766            new_node->br = XYOP1(new_node->tl, + 15);
2767            new_node->type = NODE_TRANSITION;
2768            //new_node->tile_attr = 0x80; // not quite true
2769            new_node->tile_attr = ap_map_attr_from_ram(original_xy);
2770            struct xy entrance = XY(ap_ram.entrance_xs[id], ap_ram.entrance_ys[id]);
2771            //new_node->adjacent_screen = &map_screens[XYMAPSCREEN(entrance)];
2772            new_node->adjacent_direction = DIR_U;
2773            snprintf(new_node->name, sizeof new_node->name, "door 0x%02x", ap_ram.over_ent_ids[i]);
2774            //LOG("tl: " PRIXYV ", out: " PRIXYV ", map16: %04x", PRIXYVF(tl), PRIXYVF(new_node->tl), ap_ram.over_ent_map16s[i]);
2775            ap_screen_commit_node(screen, &new_node);
2776            new_node_count++;
2777        }
2778
2779        // Overlays (Bombable Walls)
2780        uint16_t overlay_map16 = ap_ram.over_overlay_map16s[*ap_ram.overworld_index];
2781        if (overlay_map16 != 0) {
2782            new_node->overlay_index = *ap_ram.overworld_index;
2783            new_node->tl = ap_map16_to_xy(tl, overlay_map16);
2784            new_node->br = XYOP1(new_node->tl, + 15);
2785            new_node->type = NODE_OVERLAY;
2786            new_node->tile_attr = 0;
2787            snprintf(new_node->name, sizeof new_node->name, "overlay %#x", *ap_ram.overworld_index);
2788            ap_screen_commit_node(screen, &new_node);
2789            new_node_count++;
2790        }
2791
2792        /* TODO: work out map16 decoding
2793        for (size_t i = 1; i < 0x1C; i++) {
2794            if (ap_ram.over_hle_areas[i] != *ap_ram.map_area)
2795                continue;
2796            new_node = ap_node_append(map_node);
2797            node_count++;
2798            new_node->tl = ap_map16_to_xy(tl, ap_ram.over_hle_map16s[i]);
2799            new_node->br = XYOP1(new_node->tl, + 15);
2800            snprintf(new_node->name, sizeof new_node->name, "hole 0x%02x", ap_ram.over_hle_ids[i]);
2801        }
2802        */
2803    }
2804    if (XYONUPPER(tl)) {
2805        // Inside on upper level, look for non-diagonal ledges
2806        // and add them as nodes
2807        for (struct xy xy = tl; xy.y < br.y; xy.y += 0x8) {
2808            for (xy.x = tl.x; xy.x < br.x; xy.x += 0x8) {
2809                uint8_t attr = ap_map_attr_from_ram(xy);
2810                int d = attr - 0x27;
2811                if (d <= 0 || d >= 5)
2812                    continue;
2813                // In dungeons 0x28 is used for both up & down; 0x2A for both left & right
2814                // Look for the 0x1C "hole" to fall into
2815                if (ap_map_attr_from_ram(XYOP2(xy, + 8*, dir_dxy[d])) != 0x1C)
2816                    d++;
2817                if (ap_map_attr_from_ram(XYOP2(xy, + 8*, dir_dxy[d])) != 0x1C)
2818                    continue;
2819                static const int perp_dirs[5] = {0, DIR_R, DIR_R, DIR_D, DIR_D};
2820                uint8_t adj_attr = ap_map_attr_from_ram(XYOP2(xy, - 8*, dir_dxy[perp_dirs[d]]));
2821                if (adj_attr == attr)
2822                    continue; // already made a node for this
2823                struct xy l_tl = xy;
2824                struct xy l_br = xy;
2825                bool valid_ledge = true;
2826                int ledge_size = 0;
2827                bool found_start = false;
2828                for (struct xy t = xy; XYUNDER(t, br); t = XYOP2(t, + 8*, dir_dxy[perp_dirs[d]])) {
2829                    uint8_t t_attr = ap_map_attr_from_ram(t);
2830                    if (ap_tile_attrs[t_attr] & TILE_ATTR_STRS) {
2831                        // If there are stairs, omit the ledge (because you can just take the stairs)
2832                        valid_ledge = false;
2833                    } else if (t_attr != attr) {
2834                        break;
2835                    } else if (ap_tile_attrs[ap_map_attr_from_ram(XYOP2(t, - 8*, dir_dxy[d]))] & TILE_ATTR_WALK) {
2836                        if (!found_start) {
2837                            found_start = true;
2838                            l_tl = t;
2839                        }
2840                        ledge_size++;
2841                    } else if (found_start) {
2842                        break;
2843                    }
2844                    l_br = t;
2845                }
2846                if (found_start && valid_ledge && ledge_size >= 2) {
2847                    struct xy ledge_tl = l_tl;
2848                    struct xy ledge_br = XYOP1(l_br, + 7);
2849                    struct xy offset = XYOP1(dir_dxy[d], * 8);
2850                    if (d == DIR_D || d == DIR_R) {
2851                        ledge_tl = XYOP2(ledge_tl, -, XYOP1(offset, * 2));
2852                    } else {
2853                        ledge_br = XYOP2(ledge_br, -, XYOP1(offset, * 2));
2854                    }
2855                    // landing target
2856                    new_node->type = NODE_TRANSITION;
2857                    new_node->adjacent_direction = 0;
2858                    new_node->tl = XYOP2(ledge_tl, +, XYOP1(offset, * 3));
2859                    new_node->br = XYOP2(ledge_br, +, XYOP1(offset, * 3));
2860                    new_node->tl.x ^= 0x200;
2861                    new_node->br.x ^= 0x200;
2862                    new_node->tile_attr = 0x1C; // a lie
2863                    snprintf(new_node->name, sizeof new_node->name, "ledge %s landing", dir_names[d]);
2864                    struct ap_screen * bottom_screen = NONNULL(map_screens[XYMAPSCREEN(new_node->tl)]);
2865                    struct ap_node * landing = ap_screen_commit_node(bottom_screen, &new_node);
2866                    new_node_count++;
2867
2868                    new_node->tile_attr = attr;
2869                    new_node->type = NODE_TRANSITION;
2870                    new_node->adjacent_direction = d;
2871                    new_node->tl = XYOP2(ledge_tl, -, XYOP1(offset, * 1));
2872                    new_node->br = XYOP2(ledge_br, -, XYOP1(offset, * 1));
2873                    new_node->adjacent_node = landing;
2874                    snprintf(new_node->name, sizeof new_node->name, "ledge %sx%d", dir_names[d], ledge_size);
2875                    ap_screen_commit_node(screen, &new_node);
2876                    new_node_count++;
2877                }
2878
2879                xy.x = l_br.x;
2880            }
2881        }
2882    }
2883    size_t n_doors = 0;
2884    for (struct xy xy = tl; xy.y < br.y; xy.y += 0x10) {
2885        for (xy.x = tl.x; xy.x < br.x; xy.x += 0x10) {
2886            uint8_t attr = ap_map_attr_from_ram(xy);
2887            if (!(ap_tile_attrs[attr] & (TILE_ATTR_NODE | TILE_ATTR_DOOR)))
2888                continue;
2889            struct xy ds[4] = {XY(8, 8), XY(-8, -8), XY(-8, 8), XY(8, -8)};
2890            new_node->tl = new_node->br = XY(0, 0);
2891            for (int i = 0; i < 4; i++) {
2892                struct xy xy2 = XYOP2(xy, +, ds[i]);
2893                uint8_t attr2 = ap_map_attr_from_ram(xy2);
2894                if ((ap_tile_attrs[attr] & TILE_ATTR_MERG) && (ap_tile_attrs[attr2] & TILE_ATTR_MERG)) {
2895                    attr2 = attr;
2896                }
2897                if (XYIN(xy2, tl, br) && attr2 == attr) {
2898                    // || ((ap_tile_attrs[attr2] & TILE_ATTR_MERG) && (ap_tile_attrs[attr] & TILE_ATTR_MERG))) {
2899                    new_node->tl = XYFN2(MIN, xy, xy2);
2900                    new_node->br = XYFN2(MAX, xy, xy2);
2901                    new_node->br = XYOP1(new_node->br, + 7);
2902                    break;
2903                }
2904            }
2905            if (XYEQ(new_node->tl, new_node->br)) {
2906                continue;
2907            }
2908            if (ap_tile_attrs[attr] & TILE_ATTR_DOOR) {
2909                uint8_t attr_mask = 0xFF;
2910                if (attr >= 0x80 && attr <= 0x87) {
2911                    attr_mask = 0xEF;
2912                } else if (attr >= 0x90 && attr <= 0x97) {
2913                    continue;
2914                }
2915                struct xy next_tl = new_node->tl;
2916                while (XYIN(next_tl, tl, br) && (ap_map_attr_from_ram(next_tl) & attr_mask) == attr) {
2917                    new_node->tl = next_tl;
2918                    next_tl.x -= 8;
2919                }
2920                next_tl = new_node->tl;
2921                while (XYIN(next_tl, tl, br) && (ap_map_attr_from_ram(next_tl) & attr_mask) == attr) {
2922                    new_node->tl = next_tl;
2923                    next_tl.y -= 8;
2924                }
2925                if (xy.x - new_node->tl.x >= 0x10) {
2926                    continue;
2927                }
2928                if (xy.y - new_node->tl.y >= 0x10) {
2929                    continue;
2930                }
2931                struct xy next_br = new_node->br;
2932                while (XYIN(next_br, tl, br) && (ap_map_attr_from_ram(next_br) & attr_mask) == attr) {
2933                    new_node->br = next_br;
2934                    next_br.x += 8;
2935                }
2936                next_br = new_node->br;
2937                while (XYIN(next_br, tl, br) && (ap_map_attr_from_ram(next_br) & attr_mask) == attr) {
2938                    new_node->br = next_br;
2939                    next_br.y += 8;
2940                }
2941            }
2942            new_node->tile_attr = attr;
2943            const char * attr_name = ap_tile_attr_name(attr);
2944            if (ap_tile_attrs[attr] & TILE_ATTR_STRS) {
2945                if (attr == 0x5e || attr == 0x5f) {
2946                    //new_node->tl.y += 24;
2947                    //new_node->br.y += 24;
2948                }
2949                new_node->type = NODE_TRANSITION;
2950                if ((attr & 0xF0) == 0x10 || attr == 0x5E) {
2951                    new_node->adjacent_direction = DIR_U;
2952                } else {
2953                    new_node->adjacent_direction = DIR_D;
2954                }
2955                if (attr == 0x3E || attr == 0x1E) { // TODO: Stairs 0x1F, 0x3F, 0x5F
2956                    if (xy.x & 0x200) { // on upper level
2957                        new_node->adjacent_direction = dir_opp[new_node->adjacent_direction];
2958                    }
2959                    struct xy offset = XYOP1(dir_dxy[new_node->adjacent_direction], * -24);
2960                    new_node->tl = XYOP2(new_node->tl, +, offset);
2961                    new_node->br = XYOP2(new_node->br, +, offset);
2962                } else if ((ap_map_attr_from_ram(XYOP2(new_node->tl, -, XY(0, 8))) & 0xF0) == 0x30) {
2963                    // Check for 0x30 family which does a screen transition
2964                    new_node->adjacent_direction = DIR_U;
2965                } else if ((ap_map_attr_from_ram(XYOP2(new_node->tl, +, XY(0, 16))) & 0xF0) == 0x30) {
2966                    new_node->adjacent_direction = DIR_D;
2967                } else {
2968                    if (xy.x & 0x200) { // on upper level
2969                        new_node->adjacent_direction = dir_opp[new_node->adjacent_direction];
2970                    }
2971                    struct xy offset = XYOP1(dir_dxy[new_node->adjacent_direction], * -24);
2972                    new_node->tl = XYOP2(new_node->tl, +, offset);
2973                    new_node->br = XYOP2(new_node->br, +, offset);
2974                }
2975                snprintf(new_node->name, sizeof new_node->name, "stairs %c 0x%02x", new_node->adjacent_direction == DIR_U ? 'U' : 'D', attr);
2976            } else if (ap_tile_attrs[attr] & TILE_ATTR_DOOR) {
2977                new_node->type = NODE_TRANSITION;
2978                assert_bp(XYIN(new_node->tl, screen->tl, screen->br));
2979                assert_bp(XYIN(new_node->br, screen->tl, screen->br));
2980                if (attr == 0x5e || attr == 0x5f) {
2981                    new_node->adjacent_direction = DIR_U;
2982                    new_node->door_type = 0x80 | (attr & 0x1);
2983                } else {
2984                    // Look for adjacent seals
2985                    struct xy adjs[4] = {
2986                        XYOP2(new_node->tl, -, XY(0, 8)), // U
2987                        XYOP2(new_node->br, +, XY(0, 8)), // D
2988                        XYOP2(new_node->tl, -, XY(8, 0)), // L
2989                        XYOP2(new_node->br, +, XY(8, 0)), // R
2990                    };
2991                    uint8_t found_dir = 0;
2992                    for (uint8_t i = 0; i < 4; i++) {
2993                        if (!XYIN(adjs[i], tl, br)) {
2994                            continue;
2995                        }
2996                        uint16_t a = ap_map_attr_from_ram(adjs[i]);
2997                        if (attr >= 0xF0 && attr <= 0xFA) {
2998                            // If we're an 0xF# node; discard ourselves if there is a neighboring 0x8#
2999                            if (ap_tile_attrs[a] & TILE_ATTR_DOOR) {
3000                                if (a == 0x30) {
3001                                    // Stairs; don't expand into it, but still count this node
3002                                    assert_bp(found_dir == 0);
3003                                } else {
3004                                    assert_bp((a & 0xF8) == 0x80);
3005                                    found_dir = 0xFF;
3006                                    break;
3007                                }
3008                            }
3009                        } else {
3010                            // If we're an 0x8# node; expand into the 0xF# node
3011                            if (a >= 0xF0 && a <= 0xFA) {
3012                                assert_bp((attr & 0xF8) == 0x80);
3013                                assert_bp(found_dir == 0);
3014                                found_dir = i + 1;
3015                            }
3016                        }
3017                    }
3018                    if (found_dir == 0xFF) {
3019                        continue;
3020                    }
3021                    if (found_dir != 0 || (attr >= 0xF0 && attr <= 0xFA)) {
3022                        switch(found_dir) {
3023                            case 0: break;
3024                            case 1: new_node->tl.y -= 16; break;
3025                            case 2: new_node->br.y += 16; break;
3026                            case 3: new_node->tl.x -= 16; break;
3027                            case 4: new_node->br.x += 16; break;
3028                        }
3029                        new_node->locked_xy = XYOP1(ap_box_edge(new_node->tl, new_node->br, found_dir), &~0x7);
3030                    }
3031
3032                    uint16_t d_t = new_node->tl.y - screen->tl.y;
3033                    uint16_t d_l = new_node->tl.x - screen->tl.x;
3034                    uint16_t d_b = screen->br.y - new_node->br.y;
3035                    uint16_t d_r = screen->br.x - new_node->br.x;
3036                    assert(d_t < 0x1000 && d_l < 0x1000 && d_b < 0x1000 && d_r < 0x1000);
3037                    uint16_t dx = 0;
3038                    if (d_l < d_r) {
3039                        new_node->adjacent_direction = DIR_L;
3040                        dx = d_l;
3041                    } else {
3042                        new_node->adjacent_direction = DIR_R;
3043                        dx = d_r;
3044                    }
3045                    if (d_t < d_b) {
3046                        if (d_t < dx) {
3047                            new_node->adjacent_direction = DIR_U;
3048                        }
3049                    } else {
3050                        if (d_b < dx) {
3051                            new_node->adjacent_direction = DIR_D;
3052                        }
3053                    }
3054                    assert_bp(found_dir == 0 || dir_opp[found_dir] == new_node->adjacent_direction);
3055
3056                    size_t door_idx;
3057                    for (door_idx = 0; door_idx < 16; door_idx++) {
3058                        uint16_t door_tilemap = ap_ram.dungeon_door_tilemaps[door_idx];
3059                        if (door_tilemap == 0) {
3060                            continue;
3061                        }
3062                        struct xy door_xy = ap_tilemap_to_xy(tl, door_tilemap);
3063                        if (XYIN(door_xy , new_node->tl, new_node->br)) {
3064                            break;
3065                        }
3066                    }
3067                    //assert_bp(door_idx < 16);
3068                    if (door_idx < 16) {
3069                        uint16_t raw_door_type = ap_ram.dungeon_door_types[door_idx];
3070                        uint8_t door_type = (raw_door_type & 0xFE) >> 1;
3071                        //uint8_t door_direction = ((raw_door_type & 0x0600) >> 9) + 1;
3072                        uint8_t door_direction = (ap_ram.dungeon_door_dirs[door_idx] & 0x3) + 1;
3073                        assert_bp(new_node->adjacent_direction == door_direction);
3074                        new_node->door_type = door_type;
3075                        n_doors++;
3076                    } else {
3077                        new_node->door_type = 0xFF;
3078                    }
3079                }
3080
3081                struct xy dxy = XYOP1(dir_dxy[new_node->adjacent_direction], * -24);
3082                new_node->tl = XYOP2(new_node->tl, +, dxy);
3083                new_node->br = XYOP2(new_node->br, +, dxy);
3084
3085                snprintf(new_node->name, sizeof new_node->name, "door %s 0x%02x %s %s", dir_names[new_node->adjacent_direction], attr, attr_name, ap_door_attr_name(new_node->door_type));
3086                /*
3087            } else if (ap_tile_attrs[attr] & TILE_ATTR_CHST) {
3088                assert_bp(attr < (0x58 + (*ap_ram.room_keyblock_index / 2)));
3089                if (attr < (0x58 + (*ap_ram.room_chest_index / 2))) {
3090                    new_node->type = NODE_CHEST;
3091                    // Chests are opened from the bottom
3092                    new_node->tl.y += 16;
3093                    new_node->br.y += 16;
3094                    snprintf(new_node->name, sizeof new_node->name, "chest 0x%02x %s", attr, attr_name);
3095                } else {
3096                    new_node->type = NODE_KEYBLOCK;
3097                    // Extend keyblocks node up 16px because key blocks always (?) face upwards
3098                    // This makes it possible for pathfind to reach everything in the "locked" area
3099                    // before the node is unlocked
3100                    new_node->tl.y -= 16;
3101                    snprintf(new_node->name, sizeof new_node->name, "keyblock 0x%02x %s", attr, attr_name);
3102                }
3103                n_chests++;
3104                */
3105            } else if (ap_tile_attrs[attr] & TILE_ATTR_SWCH) {
3106                new_node->type = NODE_SWITCH;
3107                new_node->adjacent_direction = 0;
3108                snprintf(new_node->name, sizeof new_node->name, "switch 0x%02x %s", attr, attr_name);
3109            } else if (ap_tile_attrs[attr] & lift_mask) {
3110                size_t i;
3111                for (i = 0; i < ARRAYLEN(ap_pushblocks); i++) {
3112                    if (XYEQ(ap_pushblocks[i].tl, new_node->tl)) {
3113                        break;
3114                    }
3115                }
3116                if (i != ARRAYLEN(ap_pushblocks)) {
3117                    continue;
3118                }
3119                new_node->type = NODE_ITEM;
3120                snprintf(new_node->name, sizeof new_node->name, "pot 0x%02x %s", attr, attr_name);
3121            } else {
3122                new_node->type = NODE_ITEM;
3123                snprintf(new_node->name, sizeof new_node->name, "unknown item!? 0x%02x %s", attr, attr_name);
3124            }
3125            ap_screen_commit_node(screen, &new_node);
3126            new_node_count++;
3127        }
3128    }
3129    if (XYINDOORS(tl)) {
3130        size_t n_doors2 = 0;
3131        size_t n_stairs = (
3132                *ap_ram.dngn_stairs_0 + \
3133                *ap_ram.dngn_stairs_1 + \
3134                *ap_ram.dngn_stairs_2 + \
3135                *ap_ram.dngn_stairs_3 + \
3136                *ap_ram.dngn_stairs_4 + \
3137                *ap_ram.dngn_stairs_5 + \
3138                *ap_ram.dngn_stairs_6 + \
3139                *ap_ram.dngn_stairs_7 + \
3140                *ap_ram.dngn_stairs_8 + \
3141                *ap_ram.dngn_stairs_9) / 2;
3142        //ap_ram_print();
3143        /*
3144        assert_bp(n_stairs <= 4);
3145        for (size_t i = 0; i < n_stairs; i++) {
3146            uint16_t stairs_tilemap = ap_ram.special_tilemaps[i];
3147            assert_bp(stairs_tilemap != 0);
3148            struct xy stairs_xy = ap_tilemap_to_xy(tl, stairs_tilemap);
3149            if (XYIN(stairs_xy, tl, br)) {
3150                n_doors2++;
3151            }
3152        }
3153        */
3154        for (size_t i = 0; i < 16; i++) {
3155            uint16_t raw_door_type = ap_ram.dungeon_door_types[i];
3156            uint16_t door_tilemap = ap_ram.dungeon_door_tilemaps[i];
3157            if (raw_door_type == 0 && door_tilemap == 0) {
3158                continue;
3159            }
3160
3161            uint8_t door_type = (raw_door_type & 0xFE) >> 1;
3162            uint8_t door_direction = (ap_ram.dungeon_door_dirs[i] & 3) + 1;//((raw_door_type & 0x0600) >> 9) + 1;
3163            struct xy door_xy = ap_tilemap_to_xy(tl, door_tilemap);
3164            if (XYIN(door_xy, tl, br)) {
3165                n_doors2++;
3166            }
3167            /*
3168            char layer = 'n';
3169            if (ap_tile_attrs[ap_map_attr_from_ram(XYTOLOWER(door_xy))] & TILE_ATTR_DOOR) {
3170                if (ap_tile_attrs[ap_map_attr_from_ram(XYTOUPPER(door_xy))] & TILE_ATTR_DOOR) {
3171                    layer = 'B';
3172                } else {
3173                    layer = 'v';
3174                }
3175            } else if (ap_tile_attrs[ap_map_attr_from_ram(XYTOUPPER(door_xy))] & TILE_ATTR_DOOR) {
3176                layer = '^';
3177            }
3178            assert_bp(layer != 'n');
3179            assert_bp(layer != 'B');
3180            */
3181            char layer = XYONLOWER(door_xy) ? 'v' : '^';
3182            LOG("Door %2zu: %#06x %#x %s " PRIXY " (%#06x) %c %s",
3183                i, raw_door_type, door_type, dir_names[door_direction],
3184                PRIXYF(door_xy), door_tilemap, layer, ap_door_attr_name(door_type));
3185        }
3186        if (n_doors != n_doors2) LOGB("%zu %zu", n_doors, n_doors2);
3187        assert_bp(n_doors <= n_doors2);
3188
3189        size_t n_chests_and_keyblocks = *ap_ram.room_keyblock_index / 2;
3190        size_t n_chests = *ap_ram.room_chest_index / 2;
3191        assert(n_chests <= n_chests_and_keyblocks);
3192
3193        size_t chest_index;
3194        for (chest_index = 0; chest_index < 168; chest_index++) {
3195            uint16_t chest_id = ap_ram.dungeon_chests[chest_index * 3 + 0];
3196            chest_id |= ap_ram.dungeon_chests[chest_index * 3 + 1] << 8;
3197            if ((chest_id & 0x7FFF) == *ap_ram.dungeon_room) {
3198                break;
3199            }
3200        }
3201        assert(n_chests == 0 || chest_index != 168);
3202
3203        for (size_t i = 0; i < n_chests_and_keyblocks; i++) {
3204            uint16_t chest_tilemap = ap_ram.room_chest_tilemaps[i];
3205            if (chest_tilemap == 0) {
3206                LOG("Skipping chest with tilemap 0 (%zu)", i);
3207                continue;
3208            }
3209            struct xy chest_xy = ap_tilemap_to_xy(screen->tl, chest_tilemap & 0x7FFF);
3210            // Undo the tilemap offset?
3211            chest_xy = XYOP2(chest_xy, -, XY(8, 16));
3212            if (!XYIN(chest_xy, screen->tl, screen->br)) {
3213                continue;
3214            }
3215
3216            new_node->tl = chest_xy;
3217            new_node->tile_attr = 0x58 + i;
3218
3219            if (i >= n_chests) {
3220                // Keyblock
3221                new_node->type = NODE_KEYBLOCK;
3222                // Extend keyblocks node up 16px because key blocks always (?) face upwards
3223                // This makes it possible for pathfind to reach everything in the "locked" area
3224                // before the node is unlocked
3225                new_node->tl.y -= 16;
3226                new_node->br = XYOP2(new_node->tl, +, XY(15, 31));
3227                snprintf(new_node->name, sizeof new_node->name, "keyblock %zu", i);
3228            } else {
3229                assert_bp(chest_index != 168);
3230                uint16_t chest_id = ap_ram.dungeon_chests[(chest_index + i) * 3 + 0];
3231                chest_id |= ap_ram.dungeon_chests[(chest_index + i) * 3 + 1] << 8;
3232                assert_bp((chest_id & 0x7FFF) == *ap_ram.dungeon_room);
3233                if (chest_id & 0x8000) {
3234                    // Big Chest
3235                    assert_bp(i <= n_chests);
3236                    new_node->type = NODE_CHEST;
3237                    new_node->chest_type = 1;
3238                    new_node->tl.y += 24;
3239                    new_node->br = XYOP2(new_node->tl, +, XY(31, 15));
3240                    snprintf(new_node->name, sizeof new_node->name, "big chest %zu", i);
3241                } else {
3242                    // Small Chest
3243                    new_node->type = NODE_CHEST;
3244                    new_node->chest_type = 0;
3245                    new_node->tl.y += 16;
3246                    new_node->br = XYOP2(new_node->tl, +, XY(15, 15));
3247                    snprintf(new_node->name, sizeof new_node->name, "chest %zu", i);
3248                }
3249            }
3250            LOG("Chest %zu: " PRIXYV " (%#06x) %s",
3251                i, PRIXYVF(chest_xy), chest_tilemap, new_node->name);
3252
3253            ap_screen_commit_node(screen, &new_node);
3254            new_node_count++;
3255        }
3256    }
3257
3258}
3259
3260int
3261ap_map_record_transition_from(struct ap_node * src_node)
3262{
3263    if (src_node->adjacent_node != NULL)
3264        return 0;
3265
3266    struct ap_screen * src_screen = src_node->screen;
3267
3268    struct xy dst_xy = ap_link_xy();
3269    struct xy dst_xy2 = XYOP1(dst_xy, +15);
3270    struct ap_screen * dst_screen = map_screens[XYMAPSCREEN(dst_xy)];
3271    assert(dst_screen != src_screen);
3272
3273    // Find dst_node
3274    struct ap_node * dst_node = NULL;
3275    for (struct ap_node * node = dst_screen->node_list->next; node != dst_screen->node_list; node = node->next) {
3276        if (node->type != NODE_TRANSITION)
3277            continue;
3278        // TODO: Maybe use the type of the node (stairs) to see if it's valid to
3279        // link together nodes where the direction doesn't switch
3280        if (node->adjacent_direction != src_node->adjacent_direction &&
3281            dir_opp[node->adjacent_direction] != src_node->adjacent_direction)
3282            continue;
3283        // Check if any corner is in the ndoe
3284        if (XYL1BOXDIST(dst_xy, node->tl, node->br) && XYL1BOXDIST(dst_xy2, node->tl, node->br)
3285            && XYL1BOXDIST(XY(dst_xy.x, dst_xy2.y), node->tl, node->br) && XYL1BOXDIST(XY(dst_xy2.x, dst_xy.y), node->tl, node->br))
3286            continue;
3287        dst_node = node;
3288        break;
3289    }
3290    if (dst_node == NULL) {
3291        LOGB("Warning! dst_node is NULL so making own node");
3292        static struct ap_node * new_node = NULL;
3293        if (new_node == NULL)
3294            new_node = NONNULL(calloc(1, sizeof *new_node));
3295
3296        // TODO: Should be based on the size of src_node?
3297        new_node->tl = XYOP1(dst_xy, & ~7);
3298        new_node->br = XYOP1(new_node->tl, + 15);
3299        new_node->type = NODE_TRANSITION;
3300        if (ap_tile_attrs[src_node->tile_attr] & TILE_ATTR_LDGE)  {
3301            new_node->adjacent_direction = 0;
3302        } else if (src_node->tile_attr == 0x5e || src_node->tile_attr == 0x5f) {
3303            new_node->adjacent_direction = DIR_U;
3304            new_node->tile_attr = src_node->tile_attr ^ 0x1; // XXX
3305        } else {
3306            new_node->adjacent_direction = dir_opp[src_node->adjacent_direction];
3307        }
3308        snprintf(new_node->name, sizeof new_node->name, "0x%02X %s cross", XYMAPSCREEN(dst_xy), dir_names[new_node->adjacent_direction]);
3309        assert_bp(false);
3310
3311        dst_node = ap_screen_commit_node(dst_screen, &new_node);
3312    }
3313    assert(dst_node != NULL);
3314
3315    LOGB("Attaching nodes from transition: %s <-> %s", dst_node->name, src_node->name);
3316    src_node->adjacent_node = dst_node;
3317    dst_node->adjacent_node = src_node;
3318
3319    return 0;
3320}
3321
3322void
3323ap_node_islocked_print(struct ap_node * node) {
3324    bool unlockable;
3325    const struct ap_room_tag * unlock_tag;
3326    bool islocked = ap_node_islocked(node, &unlockable, &unlock_tag);
3327    LOG("Node: %s; locked: %u; unlockable: %u; room tag: %s",
3328            node->name, islocked, unlockable, ap_room_tag_print(unlock_tag));
3329}
3330
3331bool
3332ap_node_islocked(struct ap_node * node, bool *unlockable_out, const struct ap_room_tag ** unlock_tag_out) {
3333    assert(unlockable_out != NULL);
3334    assert(unlock_tag_out != NULL);
3335    *unlockable_out = false;
3336    *unlock_tag_out = false;
3337
3338    if (node->type == NODE_KEYBLOCK) {
3339        // Requires Big Key for dungeon
3340        assert_bp(node->screen->dungeon_id < 16);
3341        uint16_t bit = 1 << (15 - node->screen->dungeon_id);
3342        return !(*ap_ram.sram_dungeon_bigkeys & bit);
3343    }
3344
3345    // Is locked or unlockable
3346    if (node->type != NODE_CHEST && node->type != NODE_TRANSITION) {
3347        return false;
3348    }
3349
3350    const struct ap_room_tag * unlock_tag = node->screen->room_tags[0];
3351    if (unlock_tag == NULL || 
3352        (node->type == NODE_TRANSITION && unlock_tag->result != ROOM_RESULT_OPEN_DOORS) ||
3353        (node->type == NODE_CHEST && unlock_tag->result != ROOM_RESULT_CHEST)) {
3354        unlock_tag = node->screen->room_tags[1];
3355    }
3356    if (unlock_tag == NULL || 
3357        (node->type == NODE_TRANSITION && unlock_tag->result != ROOM_RESULT_OPEN_DOORS) ||
3358        (node->type == NODE_CHEST && unlock_tag->result != ROOM_RESULT_CHEST)) {
3359        unlock_tag = NULL;
3360    }
3361    *unlock_tag_out = unlock_tag;
3362
3363    if (!XYEQ(node->locked_xy, XY(0, 0))) {
3364        uint8_t lock_attr = ap_map_attr(node->locked_xy);
3365        if (node->type == NODE_TRANSITION) {
3366            if (lock_attr < 0xF0) {
3367                return false;
3368            }
3369
3370            if (ap_door_attrs[node->door_type] & (DOOR_ATTR_SKEY | DOOR_ATTR_BKEY | DOOR_ATTR_BOMB)) {
3371                *unlock_tag_out = NULL;
3372                if (ap_door_attrs[node->door_type] & DOOR_ATTR_SKEY) {
3373                    assert_bp(node->screen->dungeon_id < 16);
3374                    bool available_keys = ap_ram.sram_dungeon_keys[node->screen->dungeon_id] > 0;
3375                    if (*ap_ram.dungeon_id / 2 == node->screen->dungeon_id) {
3376                        available_keys = available_keys || (*ap_ram.dungeon_current_keys > 0);
3377                    }
3378                    *unlockable_out = available_keys;
3379                } else if (ap_door_attrs[node->door_type] & DOOR_ATTR_BKEY) {
3380                    assert_bp(node->screen->dungeon_id < 16);
3381                    uint16_t bit = 1 << (15 - node->screen->dungeon_id);
3382                    *unlockable_out =  *ap_ram.sram_dungeon_bigkeys & bit;
3383                } else if (ap_door_attrs[node->door_type] & DOOR_ATTR_BOMB) {
3384                    *unlockable_out = true;
3385                }
3386
3387                uint16_t bit = 0;
3388                switch (node->tile_attr) {
3389                case 0xF0: bit = 0x8000; break; // checked
3390                case 0xF1: bit = 0x2000; break; // guess
3391                case 0xF2: bit = 0x4000; break; // guess
3392                case 0xF3: bit = 0x8000; break; // guess
3393                case 0xF5: bit = 0x8000; break; // guess
3394                case 0xF6: bit = 0x4000; break; // guess
3395                case 0xF7: bit = 0x2000; break; // guess
3396                case 0xF8: bit = 0x8000; break; // contradictory; maybe 0x1000?
3397                case 0xF9: bit = 0x8000; break; // total guess
3398                case 0xFA: bit = 0x8000; break; // total guess
3399                }
3400                assert_bp(bit != 0);
3401                if (bit != 0) {
3402                    uint16_t state = ap_ram.sram_room_state[node->screen->dungeon_room];
3403                    if (node->screen->dungeon_room == *ap_ram.dungeon_room) {
3404                        // Read from RAM instead of SRAM
3405                        state = *ap_ram.room_state;
3406                    }
3407                    return !(state & bit);
3408                }
3409            }
3410        }
3411
3412        if (node->lock_node == NULL) {
3413            // Just-in-time assign switch unlock nodes
3414            for (struct ap_node * sw_node = node->screen->node_list->next; sw_node != node->screen->node_list; sw_node = sw_node->next) {
3415                if (sw_node->type == NODE_SWITCH) {
3416                    LOGB("Assigning switch %s to unlock door %s", sw_node->name, node->name);
3417                    node->lock_node = sw_node;
3418                    break;
3419                }
3420            }
3421        }
3422        if (unlock_tag != NULL) {
3423            switch (unlock_tag->action) {
3424            case ROOM_ACTION_KILL_ENEMY:
3425                *unlockable_out = true;
3426                break;
3427            case ROOM_ACTION_SWITCH_TOGGLE:
3428            case ROOM_ACTION_SWITCH_HOLD:
3429                *unlockable_out = (node->lock_node != NULL);
3430                break;
3431            case ROOM_ACTION_OPEN_CHEST:
3432            case ROOM_ACTION_LIGHT_TORCHES:
3433            case ROOM_ACTION_MOVE_BLOCK:
3434            case ROOM_ACTION_PULL_LEVER:
3435            case ROOM_ACTION_CLEAR_QUADRANT:
3436            case ROOM_ACTION_CLEAR_ROOM:
3437            case ROOM_ACTION_CLEAR_LEVEL:
3438            case ROOM_ACTION_TURN_OFF_WATER:
3439            case ROOM_ACTION_TURN_ON_WATER:
3440            case ROOM_ACTION_NONE:
3441                *unlockable_out = false;
3442                break;
3443            }
3444        } else {
3445            *unlockable_out = false;
3446        }
3447        if (node->type == NODE_TRANSITION && *ap_ram.room_trap_doors && node->screen->dungeon_room == *ap_ram.dungeon_room) {
3448            return true;
3449        }
3450        if (node->type == NODE_CHEST) {
3451            return !(ap_tile_attrs[lock_attr] & TILE_ATTR_CHST || lock_attr == 0x27); // 0x27: opened chest
3452        }
3453        return false;
3454    }
3455    assert(node->type == NODE_TRANSITION);
3456
3457    // Overworld overlays (bombable walls)
3458    if (node->lock_node != NULL && node->lock_node->type == NODE_OVERLAY) {
3459        *unlockable_out = true;
3460        return !(ap_ram.sram_overworld_state[node->lock_node->overlay_index] & 0x02);
3461    }
3462
3463
3464    if (ap_update_map_screen(false) != node->screen) {
3465        return false;
3466    }
3467
3468    return false;
3469}
3470
3471void
3472ap_map_export(const char * filename) {
3473    FILE * f = fopen(filename, "w");
3474    if (f == NULL) return;
3475    for (size_t s = 0; s < 0x100 * 0x100; s++) {
3476        struct ap_screen *screen = map_screens[s];
3477        if (screen == NULL) continue;
3478        if (XYMAPSCREEN(screen->tl) != s) continue;
3479        fprintf(f, ">0x%04x 0x%04x,0x%04x 0x%04x,0x%04x %u %u %u # %s\n",
3480                screen->id, screen->tl.x, screen->tl.y, screen->br.x, screen->br.y, 
3481                screen->dungeon_room, screen->dungeon_id, screen->dungeon_tags, screen->name);
3482        for (const struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
3483            fprintf(f, "@%p 0x%04x,0x%04x 0x%04x,0x%04x",
3484                    node, node->tl.x, node->tl.y, node->br.x, node->br.y);
3485            fprintf(f, " %u %u %p %d %u %u %u %s\n",
3486                    node->type, node->adjacent_direction, node->adjacent_node, node->tile_attr, node->sprite_type, node->sprite_subtype, node->door_type, node->name);
3487        }
3488        for (size_t y = 0; y < 0x80; y++) {
3489            fprintf(f, "|");
3490            for (size_t x = 0; x < 0x80; x++) {
3491                fprintf(f, "%02x ", screen->attr_cache[y][x]);
3492            }
3493            fprintf(f, "\n");
3494        }
3495    }
3496    fclose(f);
3497}
3498
3499void
3500ap_map_import(const char * filename) {
3501    array_reverse_test();
3502    FILE * f = fopen(filename, "r");
3503    if (f == NULL) return;
3504    struct pm * pm = pm_create();
3505    struct ap_screen *screen = NULL;
3506    size_t attr_row = 0;
3507    char *line = NULL;
3508    size_t line_len = 0;
3509    size_t line_number = 0;
3510    while (getline(&line, &line_len, f) != -1) {
3511        line_number++;
3512        if (line[0] == '#') {
3513            continue;
3514        } else if (line[0] == '>') {
3515            screen = NONNULL(calloc(1, sizeof *screen));
3516            int rc = sscanf(line, ">0x%hx 0x%hx,0x%hx 0x%hx,0x%hx %hu %hhu %hu",
3517                &screen->id, &screen->tl.x, &screen->tl.y, &screen->br.x, &screen->br.y,
3518                &screen->dungeon_room, &screen->dungeon_id, &screen->dungeon_tags);
3519            assert_bp(rc == 8);
3520            /*
3521            if (XYINDOORS(screen->tl)) {
3522                screen->dungeon_room = (screen->tl.x - 0x4000) / 0x800;
3523                screen->dungeon_room += ((screen->tl.y) / 0x200) * 16;
3524            } else {
3525                screen->dungeon_room = -1;
3526            }
3527            */
3528            for (const struct ap_screen_info * info = ap_screen_infos; info->id != (uint16_t) -1; info++) {
3529                if (info->id == screen->id) {
3530                    screen->info = info;
3531                    break;
3532                }
3533            }
3534            //ap_graph_init(&screen->graph, screen->name);
3535            LL_INIT(screen->node_list);
3536            const char * suffix = "";
3537            if (XYINDOORS(screen->tl)) {
3538                if (XYONUPPER(screen->tl)) {
3539                    screen->id &= ~0x2;
3540                    suffix = " ^";
3541                } else {
3542                    suffix = " v";
3543                }
3544            }
3545            if (screen->info) {
3546                snprintf(screen->name, sizeof screen->name, "%s%s " PRIXY " x " PRIXY, screen->info->name, suffix, PRIXYF(screen->tl), PRIXYF(screen->br));
3547            } else {
3548                snprintf(screen->name, sizeof screen->name, "%#06x%s " PRIXYV " x " PRIXYV, screen->id, suffix, PRIXYVF(screen->tl), PRIXYVF(screen->br));
3549
3550            }
3551
3552            struct xy xy;
3553            for (xy.y = screen->tl.y; xy.y < screen->br.y; xy.y += 0x100) {
3554                for (xy.x = screen->tl.x; xy.x < screen->br.x; xy.x += 0x100) {
3555                    map_screens[XYMAPSCREEN(xy)] = screen;
3556                    map_screen_mask_x[xy.x / 0x100] = true;
3557                    map_screen_mask_y[xy.y / 0x100] = true;
3558                }
3559            }
3560
3561            ap_map_add_scripts_to_screen(screen);
3562            ap_map_add_constants_to_screen(screen);
3563
3564            attr_row = 0;
3565        } else if (line[0] == '@') {
3566            assert(screen != NULL);
3567            struct ap_node * node = NONNULL(calloc(1, sizeof *node));
3568            void * node_ptr = 0;
3569            void * adj_node_ptr = 0;
3570            int node_type = NODE_NONE;
3571            int rc = sscanf(line, "@%p 0x%hx,0x%hx 0x%hx,0x%hx %d %hhu %p %hhd %hhu %hu %hhu %[^\n]",
3572                &node_ptr, &node->tl.x, &node->tl.y, &node->br.x, &node->br.y,
3573                &node_type, &node->adjacent_direction, &adj_node_ptr, &node->tile_attr, &node->sprite_type, &node->sprite_subtype, &node->door_type, node->name);
3574            assert_bp(rc == 13);
3575            node->type = node_type;
3576            if (node->type == NODE_SCRIPT) {
3577                free(node);
3578                continue;
3579            }
3580            assert(pm_set(pm, (uintptr_t) node_ptr, node) == 0);
3581            if (adj_node_ptr != 0) {
3582                assert(pm_get(pm, (uintptr_t) adj_node_ptr, (void **) &node->adjacent_node) == 0);
3583            }
3584
3585            ap_screen_add_raw_node(screen, node);
3586        } else if (line[0] == '|') {
3587            assert(screen != NULL);
3588            assert(attr_row < 0x80);
3589            char * buf = &line[1];
3590            for (size_t i = 0; i < 0x80; i++) {
3591                char * attr = strsep(&buf, " \n");
3592                screen->attr_cache[attr_row][i] = strtoul(attr, NULL, 16);
3593            }
3594            attr_row++;
3595        } else {
3596            LOG("unexpected line #%zu: %s", line_number, line);
3597            assert(0);
3598        }
3599    }
3600    free(line);
3601    size_t unmatched_pm = pm_destroy(pm);
3602    LOG("unmapped: %zu", unmatched_pm);
3603    assert_bp(unmatched_pm == 0);
3604
3605    for (struct xy xy = XY(0, 0); xy.y < 0x8000; xy.y += 0x100) {
3606        for (xy.x = 0; xy.x < 0xC000; xy.x += 0x100) {
3607            screen = map_screens[XYMAPSCREEN(xy)];
3608            if (screen == NULL || !XYEQ(screen->tl, xy)) continue;
3609            ap_map_screen_update_distances(screen);
3610        }
3611    }
3612}
3613
3614void
3615ap_map_tick() {
3616    struct ap_screen * screen = ap_update_map_screen(false);
3617    assert_bp(screen->dungeon_room == (uint16_t) -1 || screen->dungeon_room == *ap_ram.dungeon_room);
3618    if (ap_sprites_changed) {
3619        ap_map_add_sprite_nodes_to_screen(screen);
3620        //ap_map_screen_update_distances(screen);
3621    }
3622}