1diff --git a/third-party/c/zbanks-alttp/ap_plan.c b/third-party/c/zbanks-alttp/ap_plan.c 2index 03f78390..a1b2c3e5 100644 3--- a/third-party/c/zbanks-alttp/ap_plan.c 4+++ b/third-party/c/zbanks-alttp/ap_plan.c 5@@ -911,6 +911,51 @@ 6 //score += 1000; 7 break; 8 case GOAL_SCRIPT: 9+ // Carried patch (jev): every "HC " script exists only to progress 10+ // the castle rescue sequence (a guard's key, the miniboss guarding 11+ // Zelda's cell) - but the open-mode preset (zbanks::rando::PRESET) 12+ // sets sram_progress2's zelda_at_sanctuary bit (0x04) from frame 0, 13+ // so that sequence is already done before the bot ever moves. With 14+ // no gate, Jev was offered "HC kill guard for key" as a live 15+ // routine at 70% and the bot walked there, entered, and turned 16+ // around - the routine has nothing left to do. Evidence: 17+ // ../../research/zbanks-alttp.md, "Hyrule Castle routines are 18+ // already done in open mode". 19+ if (strncmp(goal->node->script->name, "HC ", 3) == 0 && (*ap_ram.sram_progress2 & 0x04)) { 20+ return GOAL_SCORE_UNSATISFIABLE; 21+ } 22+ // Carried patch (jev): general completion check for every routine, 23+ // not only the castle's - a routine's own screen already knows 24+ // whether its reward is claimed, the same way GOAL_CHEST's own 25+ // scoring reads sram_room_state. Jev picked "Blind's House Block 26+ // Puzzle" at 79% (frame 41857) with the room's own chests already 27+ // open: the routine has nothing left to solve. If a script's screen 28+ // has at least one chest node and every one of them already shows 29+ // its opened bit, the whole routine is complete - a puzzle with no 30+ // chest on its screen (AgTower's statues/curtain, the Kak well 31+ // jump) is untouched by this check, since has_chest stays false. 32+ // Evidence: ../../research/zbanks-alttp.md, "Never re-offer a 33+ // routine whose reward is already claimed". 34+ { 35+ bool has_chest = false; 36+ bool all_open = true; 37+ for (struct ap_node * n = goal->node->screen->node_list->next; 38+ n != goal->node->screen->node_list; n = n->next) { 39+ if (n->type != NODE_CHEST) { 40+ continue; 41+ } 42+ has_chest = true; 43+ assert(goal->node->screen->dungeon_room != (uint16_t) -1); 44+ uint16_t room_state = ap_ram.sram_room_state[goal->node->screen->dungeon_room]; 45+ if (!(room_state & (1 << (n->tile_attr - 0x58 + 4)))) { 46+ all_open = false; 47+ break; 48+ } 49+ } 50+ if (has_chest && all_open) { 51+ return GOAL_SCORE_COMPLETE; 52+ } 53+ } 54 break; 55 case GOAL_EXPLORE: 56 //if (goal->node->screen->name[0] != 'H') {