ap_plan.c1082 lines · 35.8 KB · raw
1#include "ap_plan.h"
2#include "ap_item.h"
3#include "ap_map.h"
4#include "ap_snes.h"
5#include <limits.h>
6
7#define GOAL_SCORE_UNSATISFIABLE    (INT_MAX-1)
8#define GOAL_SCORE_GT_LIMIT         (INT_MAX)
9#define GOAL_SCORE_COMPLETE         (-2)
10static int ap_goal_score(struct ap_goal * goal, int max_cost);
11
12const char * const ap_goal_type_names[] = {
13#define X(type) [CONCAT(GOAL_, type)] = #type,
14AP_GOAL_TYPE_LIST
15#undef X
16};
17
18const char * const ap_task_type_names[] = {
19#define X(type) [CONCAT(TASK_, type)] = #type,
20AP_TASK_TYPE_LIST
21#undef X
22};
23
24static struct ap_goal _ap_goal_list = {.next = &_ap_goal_list, .prev = &_ap_goal_list};
25struct ap_goal * ap_goal_list = &_ap_goal_list;
26static struct ap_goal * ap_active_goal = NULL;
27static struct ap_task _ap_task_list = {.next = &_ap_task_list, .prev = &_ap_task_list};
28struct ap_task * ap_task_list = &_ap_task_list;
29static bool ap_new_goals = true;
30
31static int ap_goal_count[_GOAL_TYPE_MAX];
32static int ap_goal_completed[_GOAL_TYPE_MAX];
33
34void
35ap_print_goals(bool no_limit)
36{
37    FILE * f = fopen("goals.txt", "w+");
38    assert(f != NULL);
39    fprintf(f, "Goal list: (current index=%#x)\n", XYMAPSCREEN(ap_link_xy()));
40    int max_score = GOAL_SCORE_GT_LIMIT;
41    for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
42        int score = ap_goal_score(goal, max_score);
43        goal->last_score = score;
44        char score_str[16];
45        if (score == GOAL_SCORE_COMPLETE) {
46            snprintf(score_str, sizeof(score_str), TERM_GREEN("compl"));
47        } else if (score == GOAL_SCORE_UNSATISFIABLE) {
48            snprintf(score_str, sizeof(score_str), TERM_RED("unsat"));
49        } else if (score == GOAL_SCORE_GT_LIMIT) {
50            snprintf(score_str, sizeof(score_str), TERM_BLUE("limit"));
51        } else {
52            if (!no_limit) {
53                max_score = MIN(score, max_score);
54            }
55            snprintf(score_str, sizeof(score_str), TERM_BLUE("%5d"), score);
56        }
57        fprintf(f, "    " TERM_BOLD("*") " %s " PRIGOAL, score_str, PRIGOALF(goal));
58        if (goal->type == GOAL_EXPLORE) {
59            if (goal->node != NULL && goal->node->adjacent_node != NULL) {
60                fprintf(f, " to %s %p", goal->node->adjacent_node->name, goal->node);
61            } else {
62                fprintf(f, " to umapped %p", goal->node);
63            }
64        } else {
65            if (goal->node != NULL && goal->node->screen != NULL) {
66                fprintf(f, " on %s", goal->node->screen->name);
67            }
68        }
69        fprintf(f, " Needs: %s", ap_req_print(&goal->req));
70        fprintf(f, "\n");
71    }
72    fprintf(f, "\n");
73    fprintf(f, "Stats: ");
74    for (int i = 0; i < _GOAL_TYPE_MAX; i++) {
75        fprintf(f, "%s %d/%d", ap_goal_type_names[i], ap_goal_completed[i], ap_goal_count[i]);
76        if (i != _GOAL_TYPE_MAX - 1) {
77            fprintf(f, ", ");
78        }
79    }
80
81    fprintf(f, "\n");
82    fflush(f);
83    rewind(f);
84    static char buf[4096];
85    size_t rc;
86    while ((rc = fread(buf, 1, sizeof(buf), f)) > 0) {
87        fwrite(buf, 1, rc, stdout);
88    }
89    fclose(f);
90}
91
92static struct ap_goal *
93ap_goal_append()
94{
95    struct ap_goal * goal = calloc(1, sizeof *goal);
96    assert(goal != NULL);
97
98    LL_INIT(goal);
99    LL_PUSH(ap_goal_list, goal);
100    //ap_graph_init(&goal->graph, goal->name);
101    ap_req_init(&goal->req);
102    ap_new_goals = true;
103    return goal;
104}
105
106struct ap_goal *
107ap_goal_add(enum ap_goal_type type, struct ap_node * node)
108{
109    for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
110        if (goal->type == type && goal->node == node)
111            return NULL;
112    }
113    struct ap_goal * goal = ap_goal_append();
114    goal->type = type;
115    goal->node = node;
116    snprintf(goal->name, sizeof goal->name, PRIGOAL, PRIGOALF(goal));
117    if (type == GOAL_EXPLORE) {
118        LOG("New explore, attr: %#x", node->tile_attr);
119    } else if (type == GOAL_SCRIPT) {
120        if (goal->node->script->start_item == INVENTORY_BOMBS) {
121            ap_req_require(&goal->req, 0, REQUIREMENT_BOMBS);
122        }
123        if (goal->node->script->type == SCRIPT_KILLALL) {
124            ap_req_require(&goal->req, 0, REQUIREMENT_SWORD);
125        }
126    }
127    /*
128        if (!(type == GOAL_NPC && node->sprite_type == 0x73 && node->sprite_subtype == 0x100)) {
129            // Don't do anything but explore until we get sword from uncle
130            ap_req_require(&goal->req, 0, REQUIREMENT_SWORD);
131        }
132        */
133    if (type == GOAL_NPC && node->sprite_type == 0x16 && node->sprite_subtype == 0x0000) {
134        // Sahashrala needs the green pendant before giving us an item
135        ap_req_require(&goal->req, 0, REQUIREMENT_GREEN_PENDANT);
136    }
137    if (type == GOAL_EXPLORE) {
138        uint16_t attrs = ap_tile_attrs[node->tile_attr];
139        if (attrs & TILE_ATTR_SWIM) {
140            ap_req_require(&goal->req, 0, REQUIREMENT_FLIPPERS);
141        }
142        if (attrs & TILE_ATTR_BONK) {
143            ap_req_require(&goal->req, 0, REQUIREMENT_BOOTS);
144        }
145        if (node->tile_attr == 0x55) {
146            ap_req_require(&goal->req, 0, REQUIREMENT_GLOVES_1);
147        }
148        if (strcmp(node->name, "door 0x24") == 0) {
149            // Castle door to Agahnim
150            ap_req_require(&goal->req, 0, REQUIREMENT_MASTER_SWORD);
151        }
152        if (strcmp(node->name, "door 0x09") == 0) {
153            // Desert Palace
154            ap_req_require(&goal->req, 0, REQUIREMENT_BOOK);
155        }
156        if (strcmp(node->name, "door 0x63") == 0) {
157            // Big-bomb Fairy
158            ap_req_require(&goal->req, 0, REQUIREMENT_FIVESIX_CRYSTALS);
159        }
160    }
161    ap_goal_count[goal->type]++;
162    return goal;
163}
164
165static struct ap_task *
166ap_task_append()
167{
168    struct ap_task * task = NONNULL(calloc(1, sizeof *task));
169    LL_INIT(task);
170    LL_PUSH(ap_task_list, task);
171    return task;
172}
173
174static struct ap_task *
175ap_task_prepend()
176{
177    struct ap_task * task = NONNULL(calloc(1, sizeof *task));
178    LL_INIT(task);
179    LL_PREPEND(ap_task_list, task);
180    return task;
181}
182
183const char *
184ap_print_task(const struct ap_task * task)
185{
186    static char sbuf[2048];
187    char * buf = sbuf;
188    buf += sprintf(buf, "%s %s", ap_task_type_names[task->type], task->name);
189    switch (task->type) {
190    case TASK_GOTO_POINT:
191    case TASK_OPEN_CHEST:
192    case TASK_LIFT_POT:
193    case TASK_TRANSITION:
194    case TASK_STEP_OFF_SWITCH:
195    case TASK_TALK_NPC:
196        buf += sprintf(buf, " [node=" PRINODE "]", PRINODEF(task->node));
197        break;
198    case TASK_SCRIPT_SEQUENCE:
199    case TASK_SCRIPT_KILLALL:
200    case TASK_SCRIPT_KILLDROPS:
201        if (task->node != NULL) {
202            buf += sprintf(buf, " [script=%s start_tl=" PRIXYV "]", task->node->script->name, PRIXYVF(task->node->script->start_tl));
203        }
204        break;
205    case TASK_SET_INVENTORY:
206        buf += sprintf(buf, " [item=%#x]", task->item);
207        break;
208    case TASK_BOMB:
209        buf += sprintf(buf, " [bomb=%s]", task->node->name);
210        break;
211    case TASK_NONE:
212        break;
213    }
214    return sbuf;
215}
216
217void
218ap_print_tasks()
219{
220    if (ap_active_goal == NULL) {
221        printf("Current Goal: " PRIGOAL "\n", PRIGOALF(ap_active_goal));
222    }
223    printf("Task List:\n");
224    for (struct ap_task * task = ap_task_list->next; task != ap_task_list; task = task->next) {
225        printf("    > " PRITASK "\n", PRITASKF(task));
226    }
227    printf("\n");
228}
229
230static int
231ap_task_follow_targets(struct ap_task * task, uint16_t * joypad)
232{
233    enum ap_inventory equip = *ap_ram.current_item;
234    int rc = ap_follow_targets(joypad, &equip);
235    if (rc != RC_INPR) return rc;
236    if (equip != *ap_ram.current_item) {
237        struct ap_task * new_task = ap_task_prepend(); 
238        new_task->type = TASK_SET_INVENTORY;
239        new_task->item = equip;
240        snprintf(new_task->name, sizeof new_task->name, "equip to follow_targets");
241
242        task->state = 0;
243        task->timeout = 1;
244    }
245    return rc;
246}
247
248// return -1 for failure, 0 for success, 1 for in-progress
249static int
250ap_task_evaluate(struct ap_task * task, uint16_t * joypad)
251{
252    int rc;
253    struct ap_screen * screen = ap_update_map_screen(false);
254    struct xy link = ap_link_xy();
255    bool unlockable = false;
256    const struct ap_room_tag * unlock_tag = NULL;
257    if (task->node != NULL && ap_node_islocked(task->node, &unlockable, &unlock_tag)) {
258        if (!unlockable) {
259            LOG("Cannot unlock door");
260            return RC_FAIL;
261        }
262
263        if (task->node->type == NODE_TRANSITION && (ap_door_attrs[task->node->door_type] & (DOOR_ATTR_SKEY | DOOR_ATTR_BKEY))) {
264            // pass
265        } else if (task->node->type == NODE_TRANSITION && (ap_door_attrs[task->node->door_type] & DOOR_ATTR_BOMB) && task->type != TASK_TRANSITION) {
266            // pass
267        } else {
268            if (task->node->type == NODE_TRANSITION &&
269                ((ap_door_attrs[task->node->door_type] & DOOR_ATTR_BOMB) ||
270                 (task->node->lock_node != NULL && task->node->lock_node->type == NODE_OVERLAY && task->node->lock_node->overlay_index != 0x5B))) {
271                // Try bombing it
272                struct ap_node * bomb_node = task->node->lock_node;
273                if (bomb_node == NULL) {
274                    bomb_node = task->node;
275                }
276
277                struct ap_task * new_task = ap_task_prepend(); 
278                new_task->type = TASK_GOTO_POINT;
279                new_task->node = task->node;
280                snprintf(new_task->name, sizeof new_task->name, "goto bombed %s", task->node->name);
281
282                new_task = ap_task_prepend(); 
283                new_task->type = TASK_BOMB;
284                new_task->node = bomb_node;
285                snprintf(new_task->name, sizeof new_task->name, "bomb open");
286
287                new_task = ap_task_prepend(); 
288                new_task->type = TASK_SET_INVENTORY;
289                new_task->item = INVENTORY_BOMBS;
290                snprintf(new_task->name, sizeof new_task->name, "set bombs");
291
292                if (!XYIN(link, bomb_node->tl, bomb_node->br)) {
293                    new_task = ap_task_prepend(); 
294                    new_task->type = TASK_GOTO_POINT;
295                    new_task->node = bomb_node;
296                    snprintf(new_task->name, sizeof new_task->name, "goto bomb %s", task->node->name);
297                }
298            } else if (unlock_tag != NULL && unlock_tag->action == ROOM_ACTION_SWITCH_TOGGLE) {
299                struct ap_task * new_task = ap_task_prepend(); 
300                if (task->type != TASK_GOTO_POINT) {
301                    new_task->type = TASK_GOTO_POINT;
302                    new_task->node = task->node;
303                    snprintf(new_task->name, sizeof new_task->name, "goto %s", task->node->name);
304                    new_task = ap_task_prepend(); 
305                }
306                // Prepend an unlock task
307                new_task->type = TASK_GOTO_POINT;
308                new_task->node = task->node->lock_node;
309                snprintf(new_task->name, sizeof new_task->name, "unlock %s", task->node->name);
310
311                // Maybe we need to step off first
312                new_task = ap_task_prepend(); 
313                new_task->type = TASK_STEP_OFF_SWITCH;
314                new_task->node = task->node->lock_node;
315                snprintf(new_task->name, sizeof new_task->name, "step off");
316            } else if (unlock_tag != NULL &&
317                    (unlock_tag->action == ROOM_ACTION_KILL_ENEMY || unlock_tag->action == ROOM_ACTION_CLEAR_LEVEL)) {
318                // XXX CLEAR_LEVEL is separate from KILL_ENEMY
319                struct ap_task * new_task = ap_task_prepend(); 
320                if (task->type != TASK_GOTO_POINT) {
321                    new_task->type = TASK_GOTO_POINT;
322                    new_task->node = task->node;
323                    snprintf(new_task->name, sizeof new_task->name, "goto %s", task->node->name);
324                    new_task = ap_task_prepend(); 
325                }
326
327                // Prepend an killall task
328                new_task->type = TASK_SCRIPT_KILLALL;
329                snprintf(new_task->name, sizeof new_task->name, "killall");
330
331                // Spawn the stalfos
332                if (task->node->screen->id == 0x1580) {
333                    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
334                        if (node->type == NODE_ITEM && node->tile_attr == 0x74) {
335                            new_task = ap_task_prepend(); 
336                            new_task->type = TASK_LIFT_POT;
337                            new_task->node = node;
338                            snprintf(new_task->name, sizeof new_task->name, "spawn");
339                            break;
340                        }
341                    }
342                }
343            } else {
344                LOG("Don't know how to unlock door (%s)", ap_room_tag_print(unlock_tag));
345                return RC_FAIL;
346            }
347
348            LOGB("Locked/bombable door; prepending open steps");
349            *joypad = 0;
350            return RC_INPR; // XXX should there be RC_RTRY?
351        }
352    }
353
354    uint8_t module_index = *ap_ram.module_index;
355    uint8_t submodule_index = *ap_ram.submodule_index;
356    // Dialog, Textbox, Inventory Screen
357    if (module_index == 0x0E) {
358        if (submodule_index == 0x01) {
359            // Inventory
360            if (task->type != TASK_SET_INVENTORY) {
361                JOYPAD_MASH(START, 0x01);
362            }
363        } else {
364            // Dialog?
365            LOGB("Mashing dialog; submodule = %#x", submodule_index);
366            JOYPAD_MASH(A, 0x01);
367        }
368    }
369
370    switch (task->type) {
371    case TASK_STEP_OFF_SWITCH:
372        if (!*ap_ram.link_on_switch && task->state < 5) {
373            task->state = 5;
374            task->timeout = 16;
375        }
376        switch (task->state) {
377        case 0:
378            task->timeout = 32;
379            task->state++;
380            task->state += rand() % 4;
381            break;
382            // FIXME
383            // Going in a random direction means it usually works if we retry a few times
384        case 1: JOYPAD_SET(LEFT); return RC_INPR;
385        case 2: JOYPAD_SET(RIGHT); return RC_INPR;
386        case 3: JOYPAD_SET(UP); return RC_INPR;
387        case 4: JOYPAD_SET(DOWN); return RC_INPR;
388        case 5:
389            if (task->timeout == 1)
390                return RC_DONE;
391        }
392        break;
393    case TASK_GOTO_POINT:
394        switch (task->state) {
395        case 0:
396            rc = ap_pathfind_node(task->node, true, 0);
397            if (rc < 0) {
398                LOG("ap_pathfind_node failed");
399                return RC_FAIL;
400            }
401            task->timeout = rc + 32;
402            task->state++;
403        case 1:
404        case 2:
405            /*
406            if (task->state == 2) {
407                JOYPAD_CLEAR(A);
408                task->state = 1;
409            } else if (*ap_ram.push_timer != 0x20) {
410                // TODO: Need a way of peaking ahead here to see if we need to use hammer
411                // then we can insert a TASK_SET_INVENTORY
412                JOYPAD_SET(A);
413                task->state = 2;
414            } else if (*ap_ram.carrying_bit7) {
415                JOYPAD_SET(A);
416                task->state = 2;
417            }
418            */
419            rc = ap_task_follow_targets(task, joypad);
420            if (rc != RC_INPR) return rc;
421        }
422        break;
423    case TASK_OPEN_CHEST:
424        LOG("timeout: %d; state: %d; touching: %x; push: %x; item_recv_method: %x; recving_item: %#x", task->timeout, task->state, *ap_ram.touching_chest, *ap_ram.push_timer, *ap_ram.item_recv_method, *ap_ram.recving_item);
425        switch (task->state) {
426        case 0:
427            task->timeout = 64;
428            task->state++;
429        case 1:
430            JOYPAD_SET(UP);
431            if (!(*ap_ram.touching_chest & 0xF) && (*ap_ram.push_timer == 0x20)) {
432                break;
433            }
434            task->state++;
435        case 2:
436        case 3:
437            JOYPAD_CLEAR(UP);
438            if (task->state == 2) { JOYPAD_SET(A); task->state = 3; }
439            else { JOYPAD_CLEAR(A); task->state = 2; }
440            if (*ap_ram.item_recv_method == 1) {
441                ap_item_loc_set_raw(task->node->item_loc, *ap_ram.recving_item);
442                task->timeout = 64;
443                task->state = 4;
444            }
445            break;
446        case 4:
447            if (*ap_ram.item_recv_method != 1) return RC_DONE;
448        }
449        break;
450    case TASK_TALK_NPC:
451        LOG("timeout: %d; state: %d; recving: %x; push: %x; item_recv_method: %x; link_state: %x; recving_item: %#x", task->timeout, task->state, *ap_ram.recving_item, *ap_ram.push_timer, *ap_ram.item_recv_method, *ap_ram.link_state, *ap_ram.recving_item);
452        switch (task->state) {
453        case 0:
454            task->timeout = 64;
455            task->state++;
456        case 1:
457            JOYPAD_SET(UP);
458            JOYPAD_MASH(A, 1);
459            if (*ap_ram.link_state == LINK_STATE_RECVING_ITEM || *ap_ram.link_state == LINK_STATE_RECVING_ITEM2 || *ap_ram.recving_item) {
460                // Holding item (uncle)
461                LOGB("holding item");
462                ap_item_loc_set_raw(task->node->item_loc, *ap_ram.recving_item);
463                task->timeout = 128;
464                task->state++;
465            }
466            if (task->node->sprite_type == 0x73) {
467                // Return success early from the uncle
468                return RC_DONE;
469            }
470            break;
471        case 2:
472            JOYPAD_CLEAR(UP);
473            if (*ap_ram.link_state != LINK_STATE_RECVING_ITEM && *ap_ram.link_state != LINK_STATE_RECVING_ITEM2) {
474                return RC_DONE;
475            }
476            break;
477        }
478        break;
479    case TASK_LIFT_POT:
480        switch (task->state) {
481        case 0:
482            rc = ap_pathfind_node(task->node, true, 0);
483            if (rc < 0) {
484                LOG("ap_pathfind_node failed");
485                return RC_FAIL;
486            }
487            task->timeout = rc + 32;
488            task->state++;
489        case 1:
490        case 2:
491            if (task->state == 1) { JOYPAD_SET(A); task->state = 2; }
492            else { JOYPAD_CLEAR(A); task->state = 1; }
493            rc = ap_task_follow_targets(task, joypad);
494            if (rc != RC_INPR) return rc;
495        }
496        break;
497    case TASK_TRANSITION:
498        INFO("cross: st=%d timeout=%d dir=%s", task->state, task->timeout, dir_names[task->direction]);
499        switch (task->state) {
500        case 0:
501            task->timeout = 200;
502            task->state++;
503        case 1:
504        case 2:
505            if (screen != task->node->screen) {
506                task->timeout = 8;
507                task->state = 3;
508            } else {
509                if (task->node->tile_attr == 0x55 && *ap_ram.push_timer != 0x20) {
510                    JOYPAD_SET(A);
511                    if (task->state == 1) {
512                        task->timeout += 500;
513                        task->state = 2;
514                    }
515                }
516                ap_joypad_setdir(joypad, task->direction);
517                break;
518            }
519        case 3:
520        case 4:
521            if (submodule_index == 0x10) {
522                task->timeout++;
523                break;
524            }
525            if (task->node->tile_attr == 0x55 && *ap_ram.push_timer != 0x20) {
526                LOG("Push timer in transition!");
527                JOYPAD_SET(A);
528                if (task->state == 3) {
529                    task->timeout += 500;
530                    task->state = 4;
531                }
532            }
533            if (task->timeout == 1) {
534                if (screen != task->node->screen) {
535                    ap_map_record_transition_from(task->node);
536                    return RC_DONE;
537                    //LOG("transition: %p %p %p", screen, task->node->screen, task->node->adjacent_screen[0]);
538                    //LOG("transition: %s; %s; -", screen->name, task->node->screen->name);
539                    //if (screen == task->node->adjacent_screen[0]) return RC_DONE;
540                }
541                LOG("Failed to transition in time");
542                return RC_FAIL;
543            }
544        }
545        break;
546    case TASK_SET_INVENTORY:
547        INFO("inv to %u", task->item);
548        if (*ap_ram.module_index != 0x0E && *ap_ram.current_item == task->item) {
549            return RC_DONE;
550        }
551        switch (task->state) {
552        case 0:
553            task->timeout = 200;
554            task->state++;
555        case 1:
556            if (*ap_ram.module_index != 0x0E) {
557                if (task->timeout & 1) {
558                    JOYPAD_SET(START);
559                }
560                break;
561            }
562            task->state++;
563        case 2:
564            if (*ap_ram.current_item != task->item) {
565                if (task->timeout & 1) {
566                    // XXX: Actually navigate to the right item
567                    JOYPAD_SET(LEFT);
568                }
569                break;
570            }
571            task->state++;
572        case 3: 
573            if (task->timeout & 1) {
574                JOYPAD_SET(START);
575            }
576        }
577        break;
578    case TASK_BOMB:;
579        bool active_bomb = false;
580        for (size_t i = 0; i < N_ANCILLIA; i++) {
581            if (ap_ancillia[i].type == 0x07) {
582                active_bomb = true;
583                break;
584            }
585        }
586        switch (task->state) {
587        case 0:
588            if (*ap_ram.current_item != INVENTORY_BOMBS) {
589                LOG("Bombs not equipped");
590                return RC_FAIL;
591            }
592            if (*ap_ram.inventory_bombs == 0) {
593                LOG("No bombs in inventory");
594                return RC_FAIL;
595            }
596            task->timeout = 200;
597            task->state++;
598            if (task->node != NULL && task->node->adjacent_direction) {
599                ap_joypad_setdir(joypad, task->node->adjacent_direction);
600            }
601            break;
602        case 1:
603            JOYPAD_SET(Y);
604            task->state++;
605            break;
606        case 2:
607            JOYPAD_CLEAR(Y);
608            if (active_bomb) {
609                task->state++;
610            }
611            break;
612        case 3:
613            // TODO: Dodge the bomb
614            if (!active_bomb) {
615                return RC_DONE;
616            }
617            break;
618        }
619        break;
620    case TASK_SCRIPT_SEQUENCE:
621        switch (task->state) {
622        case 0:
623            rc = ap_set_script(task->node->script);
624            if (rc < 0) {
625                LOG("ap_set_script failed: %s", task->node->script->sequence);
626                return RC_FAIL;
627            }
628            task->timeout = rc + 32;
629            task->state++;
630        case 1:
631            rc = ap_task_follow_targets(task, joypad);
632            if (rc != RC_INPR) return rc;
633        }
634        break;
635    case TASK_SCRIPT_KILLALL:
636    case TASK_SCRIPT_KILLDROPS:
637        if (*ap_ram.inventory_sword == 0) {
638            ap_req_require(&ap_active_goal->req, 0, REQUIREMENT_SWORD);
639            return RC_FAIL;
640        }
641        if (task->state == -100) {
642            if (task->timeout == 1) {
643                ap_update_map_screen(true);
644                return RC_DONE;
645            }
646            break;
647        }
648        if (ap_ram.overlord_types[7] == 0x19) {
649            if (*ap_ram.room_chest_state & 0x80) {
650                return RC_DONE;
651            }
652            // Wait for armos knights to spawn
653            //task->timeout = 2;
654            //if (!ap_sprites[0].active) {
655            //    break;
656            //}
657        }
658        bool no_sword = false;
659        if (task->state == 0 || task->timeout == 1) {
660            size_t target = -1;
661            for (size_t i = 0; i < 16; i++) {
662                if (!ap_sprites[i].active)
663                    continue;
664                if (!XYIN(ap_sprites[i].tl, screen->tl, screen->br))
665                    continue;
666                if (task->type == TASK_SCRIPT_KILLDROPS && ap_sprites[i].drop == 0)
667                    continue;
668                target = i;
669                LOGB("Targeting sprite #%zu", target);
670                break;
671            }
672            if (target == (size_t)-1) {
673                // Almost done; wait ~120 frames for loot to drop
674                LOG("Done killing; waiting 128 frames for loot to drop");
675                task->state = -100;
676                task->timeout = 128;
677                break;
678            }
679            if (ap_sprites[target].attrs & SPRITE_ATTR_VBOW) {
680                if (*ap_ram.current_item != INVENTORY_BOW) {
681                    struct ap_task * new_task = ap_task_prepend(); 
682                    new_task->type = TASK_SET_INVENTORY;
683                    new_task->item = INVENTORY_BOW;
684                    snprintf(new_task->name, sizeof new_task->name, "set bow to kill %zu", target);
685
686                    task->state = 0;
687                    task->timeout = 0;
688                    return RC_INPR;
689                }
690                if (ap_sprites[target].type == 0x84) {
691                    if (ap_sprites[target].interaction == 0x07) {
692                        JOYPAD_MASH(Y, 0x04);
693                    }
694                } else {
695                    JOYPAD_MASH(Y, 0x08);
696                }
697
698                no_sword = true;
699            }
700            rc = ap_pathfind_sprite(target);
701            if (rc == RC_FAIL) {
702                LOG("ap_pathfind_sprite failed");
703                return RC_FAIL;
704            }
705            task->timeout = MIN(20, MAX(8, rc));
706            task->state++;
707        } 
708        if (task->state == 8000) { // Actual timeout
709            LOG("timeout");
710            return RC_FAIL;
711        }
712        rc = ap_task_follow_targets(task, joypad);
713        if (rc == RC_FAIL) {
714            LOG("ap_follow_targets failed");
715            return RC_FAIL;
716        }
717        if (no_sword) {
718            JOYPAD_CLEAR(B);
719        }
720        break;
721    case TASK_NONE:
722    default:
723        LOG("unknown task type %d", task->type);
724        assert_bp(false);
725        return RC_FAIL;
726    }
727    if (task->timeout-- <= 0) {
728        LOG("task timeout: " PRITASK, PRITASKF(task));
729        return RC_FAIL;
730    }
731    return RC_INPR;
732}
733
734static int
735ap_goal_score(struct ap_goal * goal, int max_score)
736{
737    assert(goal != NULL);
738
739    struct ap_screen * screen = ap_update_map_screen(false);
740
741    /*
742    bool is_accessible = false;
743    for (struct ap_node * node = screen->node_list->next; node != screen->node_list; node = node->next) {
744        if (node->type == NODE_TRANSITION && node->adjacent_node != NULL) {
745            is_accessible = true;
746            break;
747        }
748    }
749    if (!is_accessible) {
750        return GOAL_SCORE_UNSATISFIABLE;
751    }
752    */
753    if (!ap_req_is_satisfied(&goal->req)) {
754        return GOAL_SCORE_UNSATISFIABLE;
755    }
756
757    int score = 0;
758
759    if (*ap_ram.inventory_sword == 0 && goal->type != GOAL_NPC) {
760        score += 10000;
761    }
762    /*
763    if (*ap_ram.inventory_sword == 0 && !(goal->node == NULL || goal->node->sprite_type == 0x73 || goal->node->sprite_subtype == 0x100)) {
764        //score += 1000000;
765        return GOAL_SCORE_UNSATISFIABLE;
766    }
767    */
768
769    //if (goal->type != GOAL_SCRIPT && goal->type != GOAL_NPC) {
770    //    score += 1000000;
771    //}
772    score += goal->attempts * 100; //1000000;
773    switch (goal->type) {
774    case GOAL_PICKUP:
775        score += 0;
776        if (goal->node->screen != screen) {
777            return GOAL_SCORE_UNSATISFIABLE;
778        }
779        if (ap_map_attr(goal->node->tl) == 0x27) {
780            return GOAL_SCORE_COMPLETE;
781        }
782        break;
783    case GOAL_CHEST:;
784        uint8_t attr = goal->node->tile_attr;
785        assert(attr >= 0x58 && attr <= 0x5D);
786        assert(goal->node->screen->dungeon_room != (uint16_t) -1);
787        uint16_t room_state = ap_ram.sram_room_state[goal->node->screen->dungeon_room];
788        if (room_state & (1 << (attr - 0x58 + 4))) {
789            return GOAL_SCORE_COMPLETE;
790        }
791        break;
792    case GOAL_NPC:
793        //score += 1000;
794        break;
795    case GOAL_SCRIPT:
796        break;
797    case GOAL_EXPLORE:
798        //if (goal->node->screen->name[0] != 'H') {
799        //    score += 100000;
800        //}
801        //if (goal->node->type != NODE_SWITCH)
802            //score += 1000;
803        if (goal->node->adjacent_node != NULL) {
804            return GOAL_SCORE_COMPLETE;
805        }
806        break;
807    case GOAL_ITEM:
808        if (ap_ram.inventory_base[goal->item]) {
809            return GOAL_SCORE_COMPLETE;
810        } else {
811            return GOAL_SCORE_UNSATISFIABLE;
812        }
813        break;
814    default:
815        return GOAL_SCORE_UNSATISFIABLE;
816        break;
817    }
818
819    /*
820    if (ap_graph_is_blocked(&goal->graph)) {
821        return GOAL_SCORE_UNSATISFIABLE;
822    }
823    */
824
825    if (goal->node != NULL) {
826        //struct xy link = ap_link_xy();
827        //int heuristic = ap_path_heuristic(link, goal->node->tl, goal->node->br);
828        int max_distance = max_score - score;
829        if (max_distance <= 0) {
830            return GOAL_SCORE_GT_LIMIT;
831        }
832        int distance = ap_pathfind_node(goal->node, false, max_distance);
833        if (distance < 0) {
834            return GOAL_SCORE_UNSATISFIABLE;
835        } else if (distance > max_distance) {
836            return GOAL_SCORE_GT_LIMIT;
837        }
838        score += distance;
839    }
840
841    return score;
842}
843
844static void
845ap_goal_complete(struct ap_goal * goal)
846{
847    assert(goal != NULL);
848    LOG(TERM_BOLD("Completed goal: ") TERM_GREEN(PRIGOAL), PRIGOALF(goal));
849
850    switch (goal->type) {
851    case GOAL_PICKUP:
852    case GOAL_CHEST:
853    case GOAL_NPC:
854    case GOAL_SCRIPT:
855        //LL_EXTRACT(goal->node->node_parent, goal->node);
856        //free(goal->node);
857        break;
858    default:
859        break;
860    }
861    ap_goal_completed[goal->type]++;
862
863    LL_EXTRACT(goal);
864    //ap_graph_mark_done(&goal->graph);
865    //free(goal);
866    if (ap_active_goal == goal)
867        ap_active_goal = NULL;
868}
869
870static void
871ap_goal_fail(struct ap_goal * goal)
872{
873    assert(goal != NULL);
874    assert_bp(false);
875    LOG(TERM_BOLD("Failed goal: ") TERM_RED(PRIGOAL), PRIGOALF(goal));
876    goal->attempts++;
877    if (goal->attempts > 3) {
878        LOG(TERM_BOLD("Permafailing goal: ") TERM_RED(TERM_BOLD(PRIGOAL)), PRIGOALF(goal));
879        LL_EXTRACT(goal);
880        //ap_graph_extract(&goal->graph);
881    }
882    if (ap_active_goal == goal)
883        ap_active_goal = NULL;
884}
885
886static void
887ap_goal_evaluate()
888{
889    ap_update_map_screen(true);
890    if (ap_active_goal == NULL && !ap_new_goals)
891        return;
892    ap_print_goals(false);
893
894retry_new_goal:;
895    int min_score = INT_MAX;
896    struct ap_goal * min_goal = NULL;
897    for (struct ap_goal * goal = ap_goal_list->next; goal != ap_goal_list; goal = goal->next) {
898        int score = goal->last_score; // ap_goal_score(goal);
899        if (score == GOAL_SCORE_COMPLETE) {
900            struct ap_goal * g = goal;
901            goal = goal->prev;
902            ap_goal_complete(g);
903            continue;
904        }
905        if (score == GOAL_SCORE_UNSATISFIABLE || score == GOAL_SCORE_GT_LIMIT) {
906            continue;
907        }
908        assert(score >= 0);
909        if (score < min_score) {
910            min_score = score;
911            min_goal = goal;
912        }
913    }
914    ap_active_goal = min_goal;
915    if (min_goal == NULL) {
916        //ap_new_goals = false;
917        ap_manual_mode = true;
918        ap_print_map_full();
919        LOGB("No goals available; falling back to manual mode");
920        //assert_bp(false);
921        return;
922    }
923
924    LOG(TERM_BOLD("Active goal: ") TERM_BLUE(PRIGOAL), PRIGOALF(min_goal));
925
926    while (LL_PEEK(ap_task_list) != NULL) {
927        free(LL_POP(ap_task_list));
928    }
929    assert(ap_task_list->next == ap_task_list);
930    assert(ap_task_list->prev == ap_task_list);
931    struct ap_task * task = NULL;
932    
933    // Get to point
934    switch (min_goal->type) {
935    case GOAL_CHEST:
936    case GOAL_PICKUP:
937    case GOAL_NPC:
938    case GOAL_SCRIPT:
939    case GOAL_EXPLORE:;
940        // For debugging: set inventory
941        //task = ap_task_prepend(); 
942        //task->type = TASK_SET_INVENTORY;
943        //task->item = 0x4;
944
945        struct ap_node * node = min_goal->node;
946        int rc = ap_pathfind_node(node, true, 0);
947        if (rc < 0) {
948            ap_goal_fail(min_goal);
949            ap_active_goal = NULL;
950            goto retry_new_goal;
951        }
952
953        uint64_t iter = node->pgsearch.iter;
954        while (node->pgsearch.from != NULL) {
955            assert_bp(node->pgsearch.iter == iter);
956            if (node->screen == node->pgsearch.from->screen) {
957                task = ap_task_prepend(); 
958                task->type = TASK_GOTO_POINT;
959                task->node = node;
960                snprintf(task->name, sizeof task->name, "goto point onscreen");
961            } else {
962                task = ap_task_prepend(); 
963                task->type = TASK_TRANSITION;
964                task->node = node->pgsearch.from;
965                task->direction = node->pgsearch.from->adjacent_direction;
966                snprintf(task->name, sizeof task->name, "transition %s", dir_names[task->direction]);
967            }
968            node = node->pgsearch.from;
969        }
970    default:;
971    }
972
973    switch (min_goal->type) {
974    case GOAL_PICKUP:
975        task = ap_task_append();
976        task->type = TASK_LIFT_POT;
977        task->node = min_goal->node;
978        snprintf(task->name, sizeof task->name, "item");
979        break;
980    case GOAL_CHEST:
981        task = ap_task_append();
982        task->type = TASK_OPEN_CHEST;
983        task->node = min_goal->node;
984        snprintf(task->name, sizeof task->name, "chest");
985        break;
986    case GOAL_EXPLORE:
987        if (min_goal->node->adjacent_direction == 0)
988            break;
989        task = ap_task_append(); 
990        task->type = TASK_TRANSITION;
991        task->node = min_goal->node;
992        task->direction = min_goal->node->adjacent_direction;
993        snprintf(task->name, sizeof task->name, "final transition %s", dir_names[task->direction]);
994        break;
995    case GOAL_NPC:
996        task = ap_task_append();
997        task->type = TASK_TALK_NPC;
998        task->node = min_goal->node;
999        snprintf(task->name, sizeof task->name, "npc");
1000        break;
1001    case GOAL_SCRIPT:;
1002        int item = min_goal->node->script->start_item;
1003        if (item > 0) {
1004            assert(item <= 0xff && ap_inventory_names[item] != NULL);
1005            task = ap_task_append();
1006            task->type = TASK_SET_INVENTORY;
1007            task->item = item;
1008            snprintf(task->name, sizeof task->name, "start item: %s", ap_inventory_names[item]);
1009        }
1010        task = ap_task_append();
1011        task->node = min_goal->node;
1012        enum script_type script_type = task->node->script->type;
1013        switch (task->node->script->type) {
1014        case SCRIPT_SEQUENCE: task->type = TASK_SCRIPT_SEQUENCE; break;
1015        case SCRIPT_KILLALL: task->type = TASK_SCRIPT_KILLALL; break;
1016        case SCRIPT_KILLDROPS: task->type = TASK_SCRIPT_KILLDROPS; break;
1017        default: assert(0);
1018        }
1019        snprintf(task->name, sizeof task->name, "script");
1020        break;
1021    default:
1022        LOG("Invalid goal type: %d", min_goal->type);
1023        assert_bp(false);
1024        break;
1025    }
1026}
1027
1028void
1029ap_plan_evaluate(uint16_t * joypad)
1030{
1031    while (true) {
1032        if (ap_active_goal == NULL) {
1033            ap_goal_evaluate();
1034            if (ap_active_goal == NULL) goto done;
1035            ap_print_tasks();
1036        }
1037
1038        struct ap_task * task = LL_PEEK(ap_task_list);
1039        if (task == NULL) {
1040            ap_goal_complete(ap_active_goal);
1041            task = LL_PEEK(ap_task_list);
1042            if (task == NULL) goto done;
1043        }
1044
1045        enum rc rc = ap_task_evaluate(task, joypad);
1046        if (rc != RC_INPR) {
1047            ap_print_tasks();
1048        }
1049        switch (rc) {
1050        case RC_DONE:
1051            LOG("task complete: " TERM_GREEN(PRITASK), PRITASKF(task));
1052            free(LL_POP(ap_task_list));
1053            break;
1054        case RC_FAIL:
1055            LOG("task failed: " TERM_RED(PRITASK), PRITASKF(task));
1056            ap_goal_fail(ap_active_goal);
1057            break;
1058        case RC_INPR:;
1059            goto done;
1060        }
1061    }
1062
1063done:;
1064    // Debug
1065    const char *goal_name = "(no goal)";
1066    if (ap_active_goal != NULL) {
1067        goal_name = ap_goal_type_names[ap_active_goal->type];
1068    }
1069    const char *task_name = "(no task)";
1070    int task_timeout = 0;
1071    if (LL_PEEK(ap_task_list) != NULL) {
1072        task_name = ap_task_type_names[LL_PEEK(ap_task_list)->type];
1073        task_timeout = LL_PEEK(ap_task_list)->timeout;
1074    }
1075    struct ap_screen * screen = ap_update_map_screen(NULL);
1076    INFO("Plan: %s via %s (%d)", goal_name, task_name, task_timeout);
1077}
1078
1079void
1080ap_plan_init()
1081{
1082}