jevsnes.git / third-party / c / patches / 0031-dash-as-path-action.patch
0031-dash-as-path-action.patch71 lines · 4.3 KB · raw
1--- a/third-party/c/zbanks-alttp/ap_snes.h
2+++ b/third-party/c/zbanks-alttp/ap_snes.h
3@@ -155,6 +155,7 @@
4     X(inventory_base,       uint8_t,   0x7EF33F)   \
5     X(inventory_bombs,      uint8_t,   0x7EF343)   \
6     X(inventory_hammer,     uint8_t,   0x7EF34B)   \
7+    X(inventory_boots,      uint8_t,   0x7EF355)   \
8     X(inventory_gloves,     uint8_t,   0x7EF354)   \
9     X(inventory_sword,      uint8_t,   0x7EF359)   \
10     X(inventory_quiver,     uint8_t,   0x7EF377)   \
11--- a/third-party/c/zbanks-alttp/ap_map.c
12+++ b/third-party/c/zbanks-alttp/ap_map.c
13@@ -1156,6 +1156,22 @@
14     if (*ap_ram.inventory_gloves >= 1) lift_mask |= TILE_ATTR_LFT1;
15     if (*ap_ram.inventory_gloves >= 2) lift_mask |= TILE_ATTR_LFT2;
16     if (*ap_ram.inventory_hammer >= 1) lift_mask |= TILE_ATTR_HMMR;
17+    // Carried patch (jev): fold TILE_ATTR_BONK (raw tile 0x57,
18+    // TileBehavior_BonkRocks - a rock/bramble row the disassembly's own
19+    // player.c:Link_BonkAndSmash only ever breaks while `link_is_running`)
20+    // into the SAME mask that already governs cost and the diagonal
21+    // "square with it" restriction for pots/rocks/hammer pegs below.
22+    // Folding it in here, rather than adding a parallel branch, is what
23+    // makes the existing corner logic forbid a diagonal approach to a
24+    // dash-only obstacle for free - the same reason a diagonal approach to
25+    // a liftable is already forbidden. `ap_goal_add` (ap_plan.c) already
26+    // requires REQUIREMENT_BOOTS for an EXPLORE goal whose own destination
27+    // tile is BONK; this is the missing other half - a path that merely
28+    // CROSSES a bonk row (Lost Woods 0x0a06, frame 92877 - Link had the
29+    // boots but only walked Up into the obstacle) was walled off outright,
30+    // with no route at all past it, goal or no goal. Evidence:
31+    // research/zbanks-alttp.md, "Dash as a path action".
32+    if (*ap_ram.inventory_boots) lift_mask |= TILE_ATTR_BONK;
33 
34     static struct point_state buf[0x82 * 0x82]; 
35 
36@@ -1632,7 +1648,34 @@
37                 //XXX This is normally bad, but for now assume recoil
38                 //assert_bp(!((next->xy.x <= screen->tl.x + 15) || (next->xy.y <= screen->tl.y + 15)));
39                 //assert_bp(!((next->xy.x >= screen->br.x - 15) || (next->xy.y >= screen->br.y - 15)));
40-                ap_targets[count++] = (struct ap_target) { .tl = next->xy };
41+                struct ap_target dash_target = { .tl = next->xy };
42+                // Carried patch (jev): a step onto a BONK tile (bonk rocks,
43+                // dash-only obstacles - see this function's own lift_mask
44+                // above) does not respond to ordinary walking: the real
45+                // game only breaks one while `link_is_running`
46+                // (zelda3 player.c's Link_BonkAndSmash). Give this ONE
47+                // waypoint the exact A-held-with-direction bits patch 0009
48+                // already gives SCRIPT_SEQUENCE's numpad dash characters,
49+                // so ap_follow_targets's existing joypad_mask handling (no
50+                // new per-frame logic) drives both the ~29-frame charge and
51+                // the dash itself. `next->from` is the predecessor (one
52+                // step closer to `start_xy`), so `next->xy - next->from->xy`
53+                // is the direction Link actually travels arriving here.
54+                if ((next->tile_attrs & TILE_ATTR_BONK) && *ap_ram.inventory_boots) {
55+                    struct xy travel = XYOP2(next->xy, -, next->from->xy);
56+                    const uint16_t dash_dir_mask = SNES_MASK(UP) | SNES_MASK(DOWN) |
57+                        SNES_MASK(LEFT) | SNES_MASK(RIGHT);
58+                    uint16_t dir_bit = 0;
59+                    if (travel.x > 0 && travel.y == 0) dir_bit = SNES_MASK(RIGHT);
60+                    else if (travel.x < 0 && travel.y == 0) dir_bit = SNES_MASK(LEFT);
61+                    else if (travel.y > 0 && travel.x == 0) dir_bit = SNES_MASK(DOWN);
62+                    else if (travel.y < 0 && travel.x == 0) dir_bit = SNES_MASK(UP);
63+                    if (dir_bit != 0) {
64+                        dash_target.joypad = dir_bit | SNES_MASK(A);
65+                        dash_target.joypad_mask = dash_dir_mask | SNES_MASK(A);
66+                    }
67+                }
68+                ap_targets[count++] = dash_target;
69                 //printf("%zu: "PRIXY "\n", count, PRIXYF(ap_targets[count-1]));
70             }
71         }