jevsnes.git / third-party / c / patches / 0003-anti-fairy-circle.patch
0003-anti-fairy-circle.patch127 lines · 5.9 KB · raw
1diff --git a/third-party/c/zbanks-alttp/ap_map.c b/third-party/c/zbanks-alttp/ap_map.c
2index e481fe75..7b51a106 100644
3--- a/third-party/c/zbanks-alttp/ap_map.c
4+++ b/third-party/c/zbanks-alttp/ap_map.c
5@@ -113,6 +113,17 @@ static const struct ap_script ap_scripts[] = {
6         .type = SCRIPT_KILLALL,
7         .name = "AgTower boss",
8     },
9+    // Carried patch (jev): Eastern Palace $B8, the big key room. The switch
10+    // that brings out the big key chest is under the pot an anti-fairy circle
11+    // orbits, and the circle only breaks up once the room's other enemies are
12+    // dead (usdasm bank_1E.asm:12749-12799). Start below the circle.
13+    {
14+        .start_tl = XY(0x8380, 0x1730),
15+        .start_item = -1,
16+        .sequence = NULL,
17+        .type = SCRIPT_KILLALL,
18+        .name = "EP clear big key room",
19+    },
20     {
21         .start_tl = XY(0x0058, 0x0681),
22         .start_item = -1,
23diff --git a/third-party/c/zbanks-alttp/ap_plan.c b/third-party/c/zbanks-alttp/ap_plan.c
24index 03f78390..0a14551e 100644
25--- a/third-party/c/zbanks-alttp/ap_plan.c
26+++ b/third-party/c/zbanks-alttp/ap_plan.c
27@@ -246,6 +246,23 @@ ap_task_follow_targets(struct ap_task * task, uint16_t * joypad)
28 }
29 
30 // return -1 for failure, 0 for success, 1 for in-progress
31+// Carried patch (jev): a sprite SCRIPT_KILLALL/KILLDROPS may go after - the
32+// test upstream wrote inline, shared by the fallback below.
33+static bool
34+ap_killall_candidate(const struct ap_task * task, const struct ap_screen * screen, size_t i)
35+{
36+    if (!ap_sprites[i].active)
37+        return false;
38+    if (!XYIN(ap_sprites[i].tl, screen->tl, screen->br))
39+        return false;
40+    if (task->type == TASK_SCRIPT_KILLDROPS && ap_sprites[i].drop == 0)
41+        return false;
42+    // An invulnerable sprite is never killed.
43+    if (ap_sprites[i].attrs & SPRITE_ATTR_NVUL)
44+        return false;
45+    return true;
46+}
47+
48 static int
49 ap_task_evaluate(struct ap_task * task, uint16_t * joypad)
50 {
51@@ -659,11 +676,7 @@ ap_task_evaluate(struct ap_task * task, uint16_t * joypad)
52         if (task->state == 0 || task->timeout == 1) {
53             size_t target = -1;
54             for (size_t i = 0; i < 16; i++) {
55-                if (!ap_sprites[i].active)
56-                    continue;
57-                if (!XYIN(ap_sprites[i].tl, screen->tl, screen->br))
58-                    continue;
59-                if (task->type == TASK_SCRIPT_KILLDROPS && ap_sprites[i].drop == 0)
60+                if (!ap_killall_candidate(task, screen, i))
61                     continue;
62                 target = i;
63                 LOGB("Targeting sprite #%zu", target);
64@@ -698,10 +711,35 @@ ap_task_evaluate(struct ap_task * task, uint16_t * joypad)
65                 no_sword = true;
66             }
67             rc = ap_pathfind_sprite(target);
68+            // Carried patch (jev): an enemy with no path to it right now (in
69+            // a wall, behind a pot, mid-knockback) failed the whole goal, and
70+            // three such frames in a row permafail it. Try the others, and
71+            // if none can be reached, wait a few frames and look again; give
72+            // up only when that keeps happening.
73+            static int unreachable = 0;
74+            for (size_t j = target + 1; rc == RC_FAIL && j < 16; j++) {
75+                // Not one that needs the bow: the bow is set up above.
76+                if (!ap_killall_candidate(task, screen, j) || (ap_sprites[j].attrs & SPRITE_ATTR_VBOW))
77+                    continue;
78+                rc = ap_pathfind_sprite(j);
79+                if (rc != RC_FAIL) {
80+                    target = j;
81+                    LOGB("Targeting sprite #%zu instead", target);
82+                }
83+            }
84+            if (rc == RC_FAIL && ++unreachable <= 32) {
85+                LOG("ap_pathfind_sprite failed; looking again in 8 frames");
86+                *joypad = 0;
87+                task->timeout = 8;
88+                task->state++;
89+                break;
90+            }
91             if (rc == RC_FAIL) {
92+                unreachable = 0;
93                 LOG("ap_pathfind_sprite failed");
94                 return RC_FAIL;
95             }
96+            unreachable = 0;
97             task->timeout = MIN(20, MAX(8, rc));
98             task->state++;
99         } 
100diff --git a/third-party/c/zbanks-alttp/ap_snes.h b/third-party/c/zbanks-alttp/ap_snes.h
101index 7080b203..00647c23 100644
102--- a/third-party/c/zbanks-alttp/ap_snes.h
103+++ b/third-party/c/zbanks-alttp/ap_snes.h
104@@ -493,7 +493,10 @@ static const uint16_t ap_sprite_attrs[256] = {
105     [0x12] = SPRITE_ATTR_ENMY, // Moblin Spear Throwers
106     [0x13] = SPRITE_ATTR_ENMY, // Helmasaur?
107     [0x14] = SPRITE_ATTR_BLKF, // Gargoyle’s Domain Gate
108-    [0x15] = SPRITE_ATTR_ENMY, // Fire Faery
109+    // Carried patch (jev): the anti-fairy (HP 255) is not hurt by the sword
110+    // and not counted when the game decides a room is clear - the circle in
111+    // Eastern Palace $B8 breaks up into these once it is.
112+    [0x15] = SPRITE_ATTR_ENMY | SPRITE_ATTR_NVUL, // Fire Faery
113     [0x16] = SPRITE_ATTR_SUBT, // Sahashrala / Aginah, sage of the desert
114     [0x17] = SPRITE_ATTR_ENMY, // Water Bubbles?
115     [0x18] = SPRITE_ATTR_ENMY, // Moldorm
116@@ -608,7 +611,10 @@ static const uint16_t ap_sprite_attrs[256] = {
117     
118     [0x80] = SPRITE_ATTR_ENMY, // Wandering Fireball Chains
119     [0x81] = SPRITE_ATTR_ENMY, // Waterhoppers
120-    [0x82] = SPRITE_ATTR_ENMY, // Swirling Fire Faeries (Eastern Palace)
121+    // Carried patch (jev): the anti-fairy circle cannot be hurt (HP 255) and
122+    // orbits a fixed point until the room is clear; as a plain enemy the bot
123+    // paths through it and is pinned by knockback (Eastern Palace $B8).
124+    [0x82] = SPRITE_ATTR_ENMY | SPRITE_ATTR_NVUL | SPRITE_ATTR_BLKF, // Swirling Fire Faeries (Eastern Palace)
125     [0x83] = SPRITE_ATTR_ENMY, // Green Rocklops (igor, eyegor)
126     [0x84] = SPRITE_ATTR_ENMY | SPRITE_ATTR_VBOW, // Red Rocklops
127     [0x85] = SPRITE_ATTR_ENMY, // Yellow Stalfos (drops to the ground, dislodges head)