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, // Gargoyles 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)