ap_item.c641 lines · 22.7 KB · raw
1#include "ap_item.h"
2#include "ap_map.h"
3#include "ap_snes.h"
4#include "ap_macro.h"
5#include "lb.h"
6#include "bm.h"
7
8// 216 randomized locations + 10 crystals/pendants
9#define N_ITEMS ((size_t) (216 + 10))
10
11static struct ap_item items[] = {
12    { .copies = 1, .is_progression = true, .name = "Green Pendant", .type = MISC_ITEM_GREEN_PENDANT, },
13    { .copies = 2, .is_progression = true, .name = "Red/Blue Pendant", .type = MISC_ITEM_REDBLUE_PENDANT, },
14    { .copies = 7, .is_progression = true, .name = "Crystal", .type = MISC_ITEM_CRYSTAL, },
15#define X(n, abbr) \
16    { .dungeon = CONCAT(DUNGEON_, n), .type = DUNGEON_ITEM_SKEY, .is_progression = true, .name = abbr " skey", }, \
17    { .dungeon = CONCAT(DUNGEON_, n), .type = DUNGEON_ITEM_BKEY, .is_progression = true, .name = abbr " bkey", }, \
18    { .dungeon = CONCAT(DUNGEON_, n), .type = DUNGEON_ITEM_MAP,  .name = abbr " map", }, \
19    { .dungeon = CONCAT(DUNGEON_, n), .type = DUNGEON_ITEM_COMPASS, .name = abbr " comp", },
20DUNGEON_LIST
21#undef X
22#define X(i, n) \
23    { .copies = 1, .type = CONCAT(INVENTORY_, n), .is_progression = true, .name = STRINGIFY(n), },
24INVENTORY_LIST
25#undef X
26    { .copies = 1,  .name = "Half Magic", .type = MISC_ITEM_MAGIC_UPGRADE, },
27    { .copies = 24, .name = "Heart Piece", .type = MISC_ITEM_HEART_PIECE, },
28    { .copies = 11, .name = "Heart Cont.", .type = MISC_ITEM_HEART_CONTAINER, }, //{ .copies = 1,  .name = "S. Heart Cont.", },
29    { .copies = 2,  .name = "1 Rupee", .type = MISC_ITEM_RUPEES_1, },
30    { .copies = 4,  .name = "5 Rupees", .type = MISC_ITEM_RUPEES_5, },
31    { .copies = 28, .name = "20 Rupees", .type = MISC_ITEM_RUPEES_20, },
32    { .copies = 7,  .name = "50 Rupees", .type = MISC_ITEM_RUPEES_50, },
33    { .copies = 1,  .name = "100 Rupees", .type = MISC_ITEM_RUPEES_100, },
34    { .copies = 5,  .name = "300 Rupees", .type = MISC_ITEM_RUPEES_300, },
35    { .copies = 1,  .name = "1 Arrow", .type = MISC_ITEM_ARROWS_1, },
36    { .copies = 12, .name = "10 Arrows", .type = MISC_ITEM_ARROWS_10, },
37    { .copies = 16, .name = "3 Bombs", .type = MISC_ITEM_BOMBS_3, },
38    { .copies = 1,  .name = "10 Bombs", .type = MISC_ITEM_BOMBS_10, },
39};
40
41// TODO: map multiple items correctly; rupees/bombs/arrows/magic
42// From alttpr Item.php
43static int recv_item_map [0xB0] = {
44    INVENTORY_SWORD,                    // 0x00 L1SwordAndShield
45    INVENTORY_SWORD,                    // 0x01 L2Sword
46    INVENTORY_SWORD,                    // 0x02 L3Sword
47    INVENTORY_SWORD,                    // 0x03 L4Sword
48    INVENTORY_SHIELD,                   // 0x04 BlueShield
49    INVENTORY_SHIELD,                   // 0x05 RedShield
50    INVENTORY_SHIELD,                   // 0x06 MirrorShield
51    INVENTORY_FIRE_ROD,                 // 0x07 FireRod
52    INVENTORY_ICE_ROD,                  // 0x08 IceRod
53    INVENTORY_HAMMER,                   // 0x09 Hammer
54    INVENTORY_HOOKSHOT,                 // 0x0A Hookshot
55    INVENTORY_BOW,                      // 0x0B Bow
56    INVENTORY_BOOMERANG,                // 0x0C Boomerang
57    INVENTORY_POWDER,                   // 0x0D Powder
58    INVENTORY_BOTTLE,                   // 0x0E Bee
59    INVENTORY_BOMBOS,                   // 0x0f Bombos
60    INVENTORY_ETHER,                    // 0x10 Ether
61    INVENTORY_QUAKE,                    // 0x11 Quake
62    INVENTORY_LAMP,                     // 0x12 Lamp
63    INVENTORY_FLUTE,                    // 0x13 Shovel [XXX]
64    INVENTORY_FLUTE,                    // 0x14 OcarinaInactive
65    INVENTORY_RED_CANE,                 // 0x15 CaneOfSomaria
66    INVENTORY_BOTTLE,                   // 0x16 Bottle
67    MISC_ITEM_HEART_PIECE,              // 0x17 PieceOfHeart
68    INVENTORY_BLUE_CANE,                // 0x18 CaneOfByrna
69    INVENTORY_MAGIC_CAPE,               // 0x19 Cape
70    INVENTORY_MIRROR,                   // 0x1A MagicMirror
71    INVENTORY_GLOVES,                   // 0x1B PowerGlove
72    INVENTORY_GLOVES,                   // 0x1C TitansMitt
73    INVENTORY_BOOK,                     // 0x1D BookOfMudora
74    INVENTORY_FLIPPERS,                 // 0x1E Flippers
75    INVENTORY_MOON_PEARL,               // 0x1F MoonPearl
76    0,                                  // [0x20 Crystal???]
77    INVENTORY_NET,                      // 0x21 BugCatchingNet
78    INVENTORY_ARMOR,                    // 0x22 BlueMail
79    INVENTORY_ARMOR,                    // 0x23 RedMail
80    DUNGEON_ITEM_SKEY,                  // 0x24 Key
81    DUNGEON_ITEM_COMPASS,               // 0x25 Compass
82    MISC_ITEM_HEART_CONTAINER,          // 0x26 HeartContainerNoAnimation
83    MISC_ITEM_BOMBS_1,                  // 0x27 Bomb
84    MISC_ITEM_BOMBS_3,                  // 0x28 ThreeBombs
85    INVENTORY_POWDER,                   // 0x29 Mushroom [XXX]
86    INVENTORY_BOOMERANG,                // 0x2A RedBoomerang
87    INVENTORY_BOTTLE,                   // 0x2B BottleWithRedPotion
88    INVENTORY_BOTTLE,                   // 0x2C BottleWithGreenPotion
89    INVENTORY_BOTTLE,                   // 0x2D BottleWithBluePotion
90    MISC_ITEM_POTION_RED,               // 0x2E RedPotion
91    MISC_ITEM_POTION_GREEN,             // 0x2F GreenPotion
92    MISC_ITEM_POTION_BLUE,              // 0x30 BluePotion
93    MISC_ITEM_BOMBS_10,                 // 0x31 TenBombs
94    DUNGEON_ITEM_BKEY,                  // 0x32 BigKey
95    DUNGEON_ITEM_MAP,                   // 0x33 Map
96    MISC_ITEM_RUPEES_1,                 // 0x34 OneRupee
97    MISC_ITEM_RUPEES_5,                 // 0x35 FiveRupees
98    MISC_ITEM_RUPEES_20,                // 0x36 TwentyRupees
99    MISC_ITEM_GREEN_PENDANT,            // 0x37 PendantOfCourage
100    MISC_ITEM_REDBLUE_PENDANT,          // 0x38 PendantOfWisdom
101    MISC_ITEM_REDBLUE_PENDANT,          // 0x39 PendantOfPower
102    INVENTORY_BOW,                      // 0x3A BowAndArrows
103    INVENTORY_BOW,                      // 0x3B BowAndSilverArrows
104    INVENTORY_BOTTLE,                   // 0x3C BottleWithBee
105    INVENTORY_BOTTLE,                   // 0x3D BottleWithFairy
106    MISC_ITEM_HEART_CONTAINER,          // 0x3E BossHeartContainer
107    MISC_ITEM_HEART_CONTAINER,          // 0x3F HeartContainer
108    MISC_ITEM_RUPEES_100,               // 0x40 OneHundredRupees
109    MISC_ITEM_RUPEES_50,                // 0x41 FiftyRupees
110    MISC_ITEM_HEART_SMALL,              // 0x42 Heart
111    MISC_ITEM_ARROWS_1,                 // 0x43 Arrow
112    MISC_ITEM_ARROWS_10,                // 0x44 TenArrows
113    MISC_ITEM_MAGIC_SMALL,              // 0x45 SmallMagic
114    MISC_ITEM_RUPEES_300,               // 0x46 ThreeHundredRupees
115    MISC_ITEM_RUPEES_20,                // 0x47 TwentyRupees2
116    INVENTORY_BOTTLE,                   // 0x48 BottleWithGoldBee
117    INVENTORY_SWORD,                    // 0x49 L1Sword
118    INVENTORY_FLUTE,                    // 0x4A OcarinaActive
119    INVENTORY_BOOTS,                    // 0x4B PegasusBoots
120    MISC_ITEM_BOMB_UPGRADE,             // 0x4C BombUpgrade50
121    MISC_ITEM_ARROW_UPGRADE,            // 0x4D ArrowUpgrade70
122    MISC_ITEM_MAGIC_UPGRADE,            // 0x4E HalfMagic
123    MISC_ITEM_MAGIC_UPGRADE,            // 0x4F QuarterMagic
124    INVENTORY_SWORD,                    // 0x50 MasterSword
125    MISC_ITEM_BOMB_UPGRADE,             // 0x51 BombUpgrade5
126    MISC_ITEM_BOMB_UPGRADE,             // 0x52 BombUpgrade10
127    MISC_ITEM_BOMB_UPGRADE,             // 0x53 ArrowUpgrade5
128    MISC_ITEM_BOMB_UPGRADE,             // 0x54 ArrowUpgrade10
129    MISC_ITEM_BOMB_UPGRADE,             // 0x55 Programmable1
130    0,                                  // 0x56 Programmable2
131    0,                                  // 0x57 Programmable3
132    INVENTORY_BOW,                      // 0x58 SilverArrowUpgrade
133    MISC_ITEM_RUPEES_NEGATIVE,          // 0x59 Rupoor
134    0,                                  // 0x5A Nothing
135    0,                                  // 0x5B RedClock
136    0,                                  // 0x5C BlueClock
137    0,                                  // 0x5D GreenClock
138    INVENTORY_SWORD,                    // 0x5E ProgressiveSword
139    INVENTORY_SHIELD,                   // 0x5F ProgressiveShield
140    INVENTORY_ARMOR,                    // 0x60 ProgressiveArmor
141    INVENTORY_GLOVES,                   // 0x61 ProgressiveGlove
142    0,                                  // 0x62 singleRNG
143    0,                                  // 0x63 multiRNG
144    INVENTORY_BOW,                      // 0x64 ProgressiveBow
145    INVENTORY_BOW,                      // 0x65 ProgressiveBow
146    INVENTORY_BOW,                      // 0x65 ProgressiveBowAlternate
147    0,                                  // 0x6A Triforce
148    0,                                  // 0x6B PowerStar
149    0,                                  // 0x6C TriforcePiece
150    // 0x70 MapLW
151    // 0x71 MapDW
152    // 0x72 MapA2
153    // 0x73 MapD7
154    // 0x74 MapD4
155    // 0x75 MapP3
156    // 0x76 MapD5
157    // 0x77 MapD3
158    // 0x78 MapD6
159    // 0x79 MapD1
160    // 0x7A MapD2
161    // 0x7B MapA1
162    // 0x7C MapP2
163    // 0x7D MapP1
164    // 0x7E MapH1
165    // 0x7F MapH2
166    [0x70 ... 0x7F] = DUNGEON_ITEM_MAP,
167    // 0x82 CompassA2
168    // 0x83 CompassD7
169    // 0x84 CompassD4
170    // 0x85 CompassP3
171    // 0x86 CompassD5
172    // 0x87 CompassD3
173    // 0x88 CompassD6
174    // 0x89 CompassD1
175    // 0x8A CompassD2
176    // 0x8B CompassA1
177    // 0x8C CompassP2
178    // 0x8D CompassP1
179    // 0x8E CompassH1
180    // 0x8F CompassH2
181    [0x80 ... 0x8F] = DUNGEON_ITEM_COMPASS,
182    // 0x92 BigKeyA2
183    // 0x93 BigKeyD7
184    // 0x94 BigKeyD4
185    // 0x95 BigKeyP3
186    // 0x96 BigKeyD5
187    // 0x97 BigKeyD3
188    // 0x98 BigKeyD6
189    // 0x99 BigKeyD1
190    // 0x9A BigKeyD2
191    // 0x9B BigKeyA1
192    // 0x9C BigKeyP2
193    // 0x9D BigKeyP1
194    // 0x9E BigKeyH1
195    // 0x9F BigKeyH2
196    [0x90 ... 0x9F] = DUNGEON_ITEM_BKEY,
197    // 0xA0 KeyH2
198    // 0xA1 KeyH1
199    // 0xA2 KeyP1
200    // 0xA3 KeyP2
201    // 0xA4 KeyA1
202    // 0xA5 KeyD2
203    // 0xA6 KeyD1
204    // 0xA7 KeyD6
205    // 0xA8 KeyD3
206    // 0xA9 KeyD5
207    // 0xAA KeyP3
208    // 0xAB KeyD4
209    // 0xAC KeyD7
210    // 0xAD KeyA2
211    // 0xAF KeyGK
212    [0xA0 ... 0xAF] = DUNGEON_ITEM_SKEY,
213    // null BigRedBomb
214    // null Crystal1
215    // null Crystal2
216    // null Crystal3
217    // null Crystal4
218    // null Crystal5
219    // null Crystal6
220    // null Crystal7
221    // null DefeatAgahnim
222    // null DefeatAgahnim2
223    // null DefeatGanon
224    // null RescueZelda
225};
226
227static const struct ap_item *item_by_index[N_ITEMS];
228static const struct ap_item *item_by_type[_MISC_ITEM_MAX];
229static const struct ap_item *item_by_dungeon_item[_DUNGEON_MAX][_DUNGEON_ITEM_MAX];
230
231// items x item_locs
232static struct lb item_lb;
233
234enum area {
235#define X(name, abbr) CONCAT(AREA_, name),
236DUNGEON_LIST
237#undef X
238    AREA_LIGHT_OVERWORLD,
239    AREA_DARK_OVERWORLD,
240    AREA_INSIDE,
241    _AREA_MAX,
242};
243
244static const size_t area_loc_count[_AREA_MAX] = {
245    [AREA_SEWERS] = 4,
246    [AREA_HYRULE_CASTLE] = 6, // idk
247    [AREA_EASTERN_PALACE] = 8,
248    [AREA_DESERT_PALACE] = 7,
249    [AREA_AGAHANIMS_TOWER] = 2,
250    [AREA_SWAMP_PALACE] = 11,
251    [AREA_PALACE_OF_DARKNESS] = 15,
252    [AREA_MISERY_MIRE] = 9,
253    [AREA_SKULL_WOODS] = 9,
254    [AREA_ICE_PALACE] = 9,
255    [AREA_TOWER_OF_HERA] = 7,
256    [AREA_THEIVES_TOWN] = 9,
257    [AREA_TURTLE_ROCK] = 13,
258    [AREA_GANONS_TOWER] = 25,
259
260    // The following are very very rough estimates
261    [AREA_LIGHT_OVERWORLD] = 8,
262    [AREA_DARK_OVERWORLD] = 6,
263    [AREA_INSIDE] = 78,
264};
265
266static size_t area_loc_start[_AREA_MAX] = {0};
267
268static struct ap_item_loc item_locs[N_ITEMS];
269static struct ap_item_loc * item_locs_by_item[N_ITEMS];
270
271static void
272ap_item_init() {
273    lb_init(&item_lb, N_ITEMS);
274    size_t index = 0;
275
276    for (size_t i = 0; i < ARRAYLEN(items); i++) {
277        struct ap_item * item = &items[i];
278
279        switch (item->type) {
280        case 0:
281            assert(0);
282            break;
283        case INVENTORY_SWORD:
284        case INVENTORY_BOTTLE:
285            item->copies = 4;
286            break;
287        case INVENTORY_SHIELD:
288            item->copies = 3;
289            break;
290        case INVENTORY_GLOVES:
291        case INVENTORY_BOW:
292        case INVENTORY_BOOMERANG:
293        case INVENTORY_ARMOR:
294        case INVENTORY_FLUTE:   // XXX actually FLUTE/SHOVEL
295        case INVENTORY_POWDER:  // XXX actually POWDER/MUSHROOM
296            item->copies = 2;
297            break;
298        case INVENTORY_BOMBS:
299            item->index = -1;
300            break;
301        default: 
302            break;
303        }
304
305        switch (item->type) {
306        case _INVENTORY_MIN ... (_INVENTORY_MAX-1):
307        case _MISC_ITEM_MIN ... (_MISC_ITEM_MAX-1):
308            item->dungeon = -1;
309            break;
310        case DUNGEON_ITEM_SKEY:
311            switch(item->dungeon) {
312            // This only counts randomized keys; not keys dropped by enemies
313            case DUNGEON_AGAHANIMS_TOWER: item->copies = 2; break;
314            case DUNGEON_GANONS_TOWER: item->copies = 4; break;
315            case DUNGEON_PALACE_OF_DARKNESS: item->copies = 6; break;
316            case DUNGEON_SWAMP_PALACE: item->copies = 1; break;
317            case DUNGEON_SKULL_WOODS: item->copies = 3; break;
318            case DUNGEON_THEIVES_TOWN: item->copies = 1; break;
319            case DUNGEON_ICE_PALACE: item->copies = 2; break;
320            case DUNGEON_MISERY_MIRE: item->copies = 3; break;
321            case DUNGEON_TURTLE_ROCK: item->copies = 4; break;
322            case DUNGEON_HYRULE_CASTLE: item->index = -1; break;
323            case DUNGEON_SEWERS: item->copies = 1; break;
324            case DUNGEON_EASTERN_PALACE: item->index = -1; break;
325            case DUNGEON_DESERT_PALACE: item->copies = 1; break;
326            case DUNGEON_TOWER_OF_HERA: item->copies = 1; break;
327            default: assert(0);
328            }
329            break;
330        case DUNGEON_ITEM_BKEY:
331            switch(item->dungeon) {
332            // HC has a big key but it's a guaranteed drop
333            case DUNGEON_HYRULE_CASTLE:
334            case DUNGEON_AGAHANIMS_TOWER:
335            case DUNGEON_SEWERS:
336                item->index = -1;
337                break;
338            default:
339                item->copies = 1;
340                break;
341            }
342            break;
343        case DUNGEON_ITEM_MAP:
344            switch(item->dungeon) {
345            case DUNGEON_SEWERS:    // XXX: Sewers vs. HC?
346            case DUNGEON_AGAHANIMS_TOWER:
347                item->index = -1;
348                break;
349            default:
350                item->copies = 1;
351                break;
352            }
353            break;
354        case DUNGEON_ITEM_COMPASS:
355            switch(item->dungeon) {
356            case DUNGEON_HYRULE_CASTLE:
357            case DUNGEON_AGAHANIMS_TOWER:
358            case DUNGEON_SEWERS:
359                item->index = -1;
360                break;
361            default:
362                item->copies = 1;
363                break;
364            }
365            break;
366        /*
367        case DUNGEON_ITEM_REWARD:
368            switch(item->dungeon) {
369            case DUNGEON_HYRULE_CASTLE:
370            case DUNGEON_AGAHANIMS_TOWER:
371            case DUNGEON_SEWERS:
372                item->index = -1;
373                break;
374            default:
375                item->copies = 1;
376                break;
377            }
378            break;
379        */
380        default:
381            assert(0);
382        }
383
384        // Skip over the item
385        if (item->index == (size_t) -1) {
386            continue;
387        }
388
389        item_by_index[index] = item;
390        item->index = index++;
391        for (size_t c = 1; c < item->copies; c++) {
392            item_by_index[index] = item;
393            lb_init_equivalent(&item_lb, item->index, index++);
394        }
395
396        if (ITEM_IS_DUNGEON(item->type)) {
397            assert(item_by_dungeon_item[item->dungeon][item->type] == NULL);
398            item_by_dungeon_item[item->dungeon][item->type] = item;
399        } else {
400            LOG("%zu %p", index, item);
401            assert(item_by_type[item->type] == NULL);
402            item_by_type[item->type] = item;
403        }
404
405
406        LOG("%5zu %s * %zu", item->index, item->name, item->copies);
407    }
408    assert(index == N_ITEMS);
409
410    for (size_t i = 0; i < N_ITEMS; i++) {
411        item_locs[i].dungeon = (enum ap_dungeon) -1;
412    }
413
414    size_t l = 0;
415    for (enum area area = 0; area < _AREA_MAX; area++) {
416        assert_bp(area_loc_count[area] > 0);
417        assert_bp(l <= N_ITEMS);
418
419        area_loc_start[area] = l;
420        for (size_t dl = 0; dl < area_loc_count[area]; dl++) {
421            item_locs[l++].dungeon = area;
422        }
423    }
424    LOG("l = %zu", l);
425    assert_bp(l == N_ITEMS);
426
427    size_t real_dungeons = 0;
428    for (size_t i = 0; i < ARRAYLEN(items); i++) {
429        const struct ap_item * item = &items[i];
430        if (item->dungeon == (enum ap_dungeon) -1 || item->index == (size_t) -1) {
431            continue;
432        }
433        if (item->dungeon == DUNGEON_AGAHANIMS_TOWER) {
434            //continue;
435        }
436
437        // Dungeon items (e.g. keys, map) must be in the dungeon they are for
438        enum area a = item->dungeon;
439        LOG("x'ing %s*%zu %zu %zu", item->name, item->copies, area_loc_start[a], area_loc_count[a]);
440        for (size_t loc = 0; loc < N_ITEMS; loc++) {
441            if (loc < area_loc_start[a] || loc >= area_loc_start[a] + area_loc_count[a]) {
442                lb_mark_negative(&item_lb, item->index, loc);
443            }
444        }
445
446        if (item->type == DUNGEON_ITEM_COMPASS && item->dungeon != DUNGEON_GANONS_TOWER) {
447            // Dungeons with compass happen to have pendants/crystals; held in the last slot
448            LOG("placing reward for %s", item->name);
449            for (size_t j = 10; j < N_ITEMS; j++) {
450                lb_mark_negative(&item_lb, j, area_loc_start[a] + area_loc_count[a] - 1);
451            }
452            real_dungeons++;
453        }
454    }
455    assert(real_dungeons == 10);
456    //ap_item_update();
457    //ap_item_print_state();
458}
459
460struct ap_item_loc *
461ap_item_loc_add(struct ap_node * node) {
462    if (node->item_loc != NULL) {
463        return node->item_loc;
464    }
465    enum area a = node->screen->dungeon_id;
466    if (a >= (enum area) _DUNGEON_MAX) {
467        if (node->screen->dungeon_room == (uint16_t) -1) {
468            a = AREA_LIGHT_OVERWORLD; // XXX support dark world
469        } else {
470            a = AREA_INSIDE;
471        }
472    } else {
473        if (node->type == NODE_ITEM || node->type == NODE_SPRITE) {
474            // XXX items inside are constant drops, not accounted for
475            return NULL;
476        }
477    }
478    assert(a < _AREA_MAX);
479    for (size_t l = area_loc_start[a]; l < area_loc_start[a] + area_loc_count[a]; l++) {
480        if (item_locs[l].node == NULL) {
481            node->item_loc = &item_locs[l];
482            node->item_loc->index = l;
483            node->item_loc->node = node;
484            break;
485        }
486    }
487    if (node->item_loc == NULL) {
488        assert_bp(0);
489        return NULL;
490    }
491
492    if (node->screen->id == 0x0a68 && node->type == NODE_SPRITE) {
493        // Well Uncle
494        ap_item_loc_set_enum(node->item_loc, INVENTORY_SWORD);
495    }
496
497    item_lb.dirty = true;
498    ap_item_update();
499
500    return node->item_loc;
501}
502
503void
504ap_item_loc_set_raw(struct ap_item_loc * item_loc, uint8_t recv_item) {
505    assert(item_loc != NULL);
506    assert_bp(recv_item < ARRAYLEN(recv_item_map));
507    int item_type = recv_item_map[recv_item];
508    return ap_item_loc_set_enum(item_loc, item_type);
509}
510
511void
512ap_item_loc_set_enum(struct ap_item_loc * item_loc, int item_type) {
513    assert(item_loc != NULL);
514    LOGB("Set item: %zu %p %d", item_loc->index, item_loc, item_type);
515    assert(item_type > 0);
516
517    if (item_loc->item != NULL) {
518        assert(item_loc->item->type == item_type);
519        return;
520    }
521
522    const struct ap_item * item = NULL;
523    if (ITEM_IS_DUNGEON(item_type)) {
524        assert(item_loc->dungeon != (enum ap_dungeon) -1);
525        item = item_by_dungeon_item[item_loc->dungeon][item_type];
526    } else {
527        item = item_by_type[item_type];
528    }
529    assert(item != NULL);
530
531    lb_mark_positive(&item_lb, item->index, item_loc->index);
532    ap_item_update();
533}
534
535void
536ap_item_update() {
537    static bool initialized = false;
538    if (!initialized) {
539        ap_item_init();
540        initialized = true;
541    }
542    if (!item_lb.dirty) {
543        return;
544    }
545    lb_deduce(&item_lb);
546
547    double item_progression_value[N_ITEMS] = {0};
548    for (size_t i = 0; i < N_ITEMS; i++) {
549        if (!item_by_index[i]->is_progression) {
550            item_progression_value[i] = 0;
551            continue;
552        }
553
554        size_t possible_locations;
555        if (BM_ISSET(item_lb.halfs[0].paired, i)) {
556            possible_locations = 1;
557        } else {
558            possible_locations = N_ITEMS - BM_POPCOUNT(item_lb.halfs[0].bs[i]);
559            //LOG("%zu %s %zu", i, item_by_index[i]->name, possible_locations);
560        }
561        assert(possible_locations > 0 && possible_locations <= N_ITEMS);
562
563        item_progression_value[i] = 100. / possible_locations;
564    }
565
566    for (size_t l = 0; l < N_ITEMS; l++) {
567        struct ap_item_loc * item_loc = &item_locs[l];
568
569        if (BM_ISSET(item_lb.halfs[1].paired, l)) {
570            size_t item_index = item_lb.halfs[1].pair[l];
571            const struct ap_item * item = item_by_index[item_index];
572            assert(item_loc->item == NULL || item_loc->item == item);
573            assert(item_locs_by_item[item_index] == NULL || item_locs_by_item[item_index] == item_loc);
574            if (!item_loc->item) {
575                const char *loc_name = item_loc->node != NULL ? item_loc->node->name : "(no node)";
576                LOGB("Paired location %zu '%s' with item '%s'", l, loc_name, item->name);
577            }
578            item_locs_by_item[item_index] = item_loc;
579            item_loc->item = item;
580            item_loc->progression_value = item_progression_value[item_index];
581        } else {
582            item_loc->progression_value = 0.;
583            for (size_t i = 0; i < N_ITEMS; i++) {
584                if (!BM_ISSET(item_lb.halfs[1].bs[l], i)) {
585                    item_loc->progression_value += item_progression_value[i];
586                }
587            }
588        }
589    }
590
591    ap_item_print_state();
592}
593
594static void 
595lb_selftest_print(struct lb * lb) {
596    lb_deduce(lb);
597
598    printf("   %20s ", "");
599    for (size_t i = 0; i < lb->size; i++) {
600        printf("%zx", i >> 4);
601    }
602    printf("\n");
603
604    printf("   %20s ", "");
605    for (size_t i = 0; i < lb->size; i++) {
606        printf("%zx", i & 0xF);
607    }
608    printf("\n");
609
610    for (size_t i = 0; i < lb->size; i++) {
611        size_t h = 0;
612        printf("   %20s ", item_by_index[i]->name);
613        for (size_t j = 0; j < lb->size; j++) {
614            if (BM_ISSET(lb->halfs[h].bs[i], j)) {
615                printf("x");
616            } else if (BM_ISSET(lb->halfs[h].paired, i)) {
617                assert(lb->halfs[h].pair[i] == j);
618                printf("@");
619            } else {
620                printf(".");
621            }
622        }
623        printf("\n");
624    }
625    printf("\n");
626}
627
628void
629ap_item_print_state() {
630    lb_selftest_print(&item_lb);
631
632    for (size_t l = 0; l < N_ITEMS; l++) {
633        struct ap_item_loc * item_loc = &item_locs[l];
634        if (item_loc->node != NULL) {
635            LOG("Loc %#6zx, Value: %lf; %s %u %s %s", l, item_loc->progression_value,
636                (item_loc->item != NULL ? item_loc->item->name : "?"),
637                item_loc->dungeon, item_loc->node->name, item_loc->node->screen->name);
638        }
639    }
640}
641