]> git.localhorst.tv Git - alttp.git/blob - resources/js/helpers/tracker.js
compact keysanity tracker
[alttp.git] / resources / js / helpers / tracker.js
1 import {
2         DUNGEON_IDS,
3         DUNGEON_MASKS,
4         INV_ADDR,
5         SRAM_ADDR,
6         getShort,
7         isBossDefeated,
8         isChestOpen,
9 } from './alttp-ram';
10 import Logic from './logic';
11
12 export const BOOLEAN_STATES = [
13         'blue-boomerang',
14         'bomb',
15         'bombos',
16         'bow',
17         'bowless-silvers',
18         'book',
19         'boots',
20         'bugnet',
21         'byrna',
22         'cape',
23         'duck',
24         'ether',
25         'fire-rod',
26         'flippers',
27         'flute',
28         'half-magic',
29         'hammer',
30         'hookshot',
31         'ice-rod',
32         'lamp',
33         'mirror',
34         'moonpearl',
35         'mushroom',
36         'powder',
37         'quake',
38         'quarter-magic',
39         'red-boomerang',
40         'shovel',
41         'silvers',
42         'somaria',
43 ];
44
45 export const INTEGER_STATES = [
46         'bottle-1',
47         'bottle-2',
48         'bottle-3',
49         'bottle-4',
50         'heart-piece',
51         'lift',
52         'mail',
53         'shield',
54         'sword',
55 ];
56
57 export const INITIAL = {
58         mail: 1,
59 };
60
61 export const BOTTLE_CONTENTS = [
62         'mushroom',
63         'bottle',
64         'red-potion',
65         'green-potion',
66         'blue-potion',
67         'fairy',
68         'bottle-bee',
69         'bottle-good-bee',
70 ];
71
72 export const BOSSES = [
73         'armos',
74         'lanmolas',
75         'moldorm',
76         'helma',
77         'arrghus',
78         'mothula',
79         'blind',
80         'kholdstare',
81         'vitreous',
82         'trinexx',
83 ];
84
85 export const CONFIG = {
86         bossShuffle: false,
87         checkCalculation: 'room-data',
88         compactKeysanity: true,
89         glitches: 'none',
90         mapLayout: 'horizontal',
91         showMap: 'situational',
92         showCompass: 'situational',
93         showSmall: 'always',
94         showBig: 'always',
95         wildMap: false,
96         wildCompass: false,
97         wildSmall: false,
98         wildBig: false,
99         worldState: 'open',
100 };
101
102 export const DUNGEONS = [
103         {
104                 id: 'hc',
105                 map: true,
106                 compass: false,
107                 sk: 1,
108                 bk: true,
109                 dropBk: true,
110                 items: 6,
111                 boss: null,
112                 bosses: [],
113                 bossRoom: 0x80,
114                 prize: false,
115                 offset: DUNGEON_IDS.HC,
116                 mask: DUNGEON_MASKS.HC,
117                 checks: [
118                         'dark-cross',
119                         'hc-map-chest',
120                         'hc-boom',
121                         'hc-cell',
122                         'sanc',
123                         'sewers-left',
124                         'sewers-mid',
125                         'sewers-right',
126                 ],
127         },
128         {
129                 id: 'ct',
130                 map: false,
131                 compass: false,
132                 sk: 2,
133                 bk: false,
134                 items: 0,
135                 boss: 'aga',
136                 bosses: ['aga'],
137                 bossRoom: 0x20,
138                 prize: false,
139                 offset: DUNGEON_IDS.CT,
140                 mask: DUNGEON_MASKS.CT,
141                 checks: [
142                         'ct-1',
143                         'ct-2',
144                 ],
145         },
146         {
147                 id: 'gt',
148                 map: true,
149                 compass: true,
150                 sk: 4,
151                 bk: true,
152                 items: 20,
153                 boss: 'aga',
154                 bosses: ['aga'],
155                 bossRoom: 0x0D,
156                 prize: false,
157                 offset: DUNGEON_IDS.GT,
158                 mask: DUNGEON_MASKS.GT,
159                 checks: [
160                         'gt-hope-left',
161                         'gt-hope-right',
162                         'gt-tile-room',
163                         'gt-compass-tl',
164                         'gt-compass-tr',
165                         'gt-compass-bl',
166                         'gt-compass-br',
167                         'gt-torch',
168                         'gt-dm-tl',
169                         'gt-dm-tr',
170                         'gt-dm-bl',
171                         'gt-dm-br',
172                         'gt-map-chest',
173                         'gt-firesnake',
174                         'gt-rando-tl',
175                         'gt-rando-tr',
176                         'gt-rando-bl',
177                         'gt-rando-br',
178                         'gt-bobs-chest',
179                         'gt-ice-left',
180                         'gt-ice-mid',
181                         'gt-ice-right',
182                         'gt-big-chest',
183                         'gt-helma-left',
184                         'gt-helma-right',
185                         'gt-pre-moldorm',
186                         'gt-post-moldorm',
187                 ],
188         },
189         {
190                 id: 'ep',
191                 map: true,
192                 compass: true,
193                 sk: 0,
194                 bk: true,
195                 items: 3,
196                 boss: 'armos',
197                 bosses: ['armos'],
198                 bossRoom: 0xC8,
199                 prize: true,
200                 isPendant: true,
201                 prizeMask: 0x04,
202                 offset: DUNGEON_IDS.EP,
203                 mask: DUNGEON_MASKS.EP,
204                 checks: [
205                         'ep-cannonball',
206                         'ep-map-chest',
207                         'ep-compass-chest',
208                         'ep-big-chest',
209                         'ep-big-key-chest',
210                         'ep-boss-defeated',
211                 ],
212         },
213         {
214                 id: 'dp',
215                 map: true,
216                 compass: true,
217                 sk: 1,
218                 bk: true,
219                 items: 2,
220                 boss: 'lanmolas',
221                 bosses: ['lanmolas'],
222                 bossRoom: 0x33,
223                 prize: true,
224                 isPendant: true,
225                 prizeMask: 0x02,
226                 offset: DUNGEON_IDS.DP,
227                 mask: DUNGEON_MASKS.DP,
228                 checks: [
229                         'dp-torch',
230                         'dp-map-chest',
231                         'dp-big-chest',
232                         'dp-compass-chest',
233                         'dp-big-key-chest',
234                         'dp-boss-defeated',
235                 ],
236         },
237         {
238                 id: 'th',
239                 map: true,
240                 compass: true,
241                 sk: 1,
242                 bk: true,
243                 items: 2,
244                 boss: 'moldorm',
245                 bosses: ['moldorm'],
246                 bossRoom: 0x07,
247                 prize: true,
248                 isPendant: true,
249                 prizeMask: 0x01,
250                 offset: DUNGEON_IDS.TH,
251                 mask: DUNGEON_MASKS.TH,
252                 checks: [
253                         'th-basement-cage',
254                         'th-map-chest',
255                         'th-big-key-chest',
256                         'th-compass-chest',
257                         'th-big-chest',
258                         'th-boss-defeated',
259                 ],
260         },
261         {
262                 id: 'pd',
263                 map: true,
264                 compass: true,
265                 sk: 6,
266                 bk: true,
267                 items: 5,
268                 boss: 'helma',
269                 bosses: ['helma'],
270                 bossRoom: 0x5A,
271                 prize: true,
272                 prizeMask: 0x02,
273                 offset: DUNGEON_IDS.PD,
274                 mask: DUNGEON_MASKS.PD,
275                 checks: [
276                         'pd-shooter-room',
277                         'pd-stalfos-basement',
278                         'pd-big-key-chest',
279                         'pd-arena-bridge',
280                         'pd-arena-ledge',
281                         'pd-map-chest',
282                         'pd-compass-chest',
283                         'pd-basement-left',
284                         'pd-basement-right',
285                         'pd-harmless-hellway',
286                         'pd-maze-top',
287                         'pd-maze-bottom',
288                         'pd-big-chest',
289                         'pd-boss-defeated',
290                 ],
291         },
292         {
293                 id: 'sp',
294                 map: true,
295                 compass: true,
296                 sk: 1,
297                 bk: true,
298                 items: 6,
299                 boss: 'arrghus',
300                 bosses: ['arrghus'],
301                 bossRoom: 0x06,
302                 prize: true,
303                 prizeMask: 0x10,
304                 offset: DUNGEON_IDS.SP,
305                 mask: DUNGEON_MASKS.SP,
306                 checks: [
307                         'sp-lobby',
308                         'sp-map-chest',
309                         'sp-big-chest',
310                         'sp-compass-chest',
311                         'sp-west-chest',
312                         'sp-big-key-chest',
313                         'sp-flooded-left',
314                         'sp-flooded-right',
315                         'sp-waterfall',
316                         'sp-boss-defeated',
317                 ],
318         },
319         {
320                 id: 'sw',
321                 map: true,
322                 compass: true,
323                 sk: 3,
324                 bk: true,
325                 items: 2,
326                 boss: 'mothula',
327                 bosses: ['mothula'],
328                 bossRoom: 0x29,
329                 prize: true,
330                 prizeMask: 0x40,
331                 offset: DUNGEON_IDS.SW,
332                 mask: DUNGEON_MASKS.SW,
333                 checks: [
334                         'sw-big-chest',
335                         'sw-map-chest',
336                         'sw-pot-prison',
337                         'sw-compass-chest',
338                         'sw-pinball-room',
339                         'sw-big-key-chest',
340                         'sw-bridge-chest',
341                         'sw-boss-defeated',
342                 ],
343         },
344         {
345                 id: 'tt',
346                 map: true,
347                 compass: true,
348                 sk: 1,
349                 bk: true,
350                 items: 4,
351                 boss: 'blind',
352                 bosses: ['blind'],
353                 bossRoom: 0xAC,
354                 prize: true,
355                 prizeMask: 0x20,
356                 offset: DUNGEON_IDS.TT,
357                 mask: DUNGEON_MASKS.TT,
358                 checks: [
359                         'tt-map-chest',
360                         'tt-ambush-chest',
361                         'tt-compass-chest',
362                         'tt-big-key-chest',
363                         'tt-attic',
364                         'tt-cell',
365                         'tt-big-chest',
366                         'tt-boss-defeated',
367                 ],
368         },
369         {
370                 id: 'ip',
371                 map: true,
372                 compass: true,
373                 sk: 2,
374                 bk: true,
375                 items: 3,
376                 boss: 'kholdstare',
377                 bosses: ['kholdstare'],
378                 bossRoom: 0xDE,
379                 prize: true,
380                 prizeMask: 0x04,
381                 offset: DUNGEON_IDS.IP,
382                 mask: DUNGEON_MASKS.IP,
383                 checks: [
384                         'ip-compass-chest',
385                         'ip-big-key-chest',
386                         'ip-map-chest',
387                         'ip-spike-chest',
388                         'ip-freezor-chest',
389                         'ip-big-chest',
390                         'ip-ice-t',
391                         'ip-boss-defeated',
392                 ],
393         },
394         {
395                 id: 'mm',
396                 map: true,
397                 compass: true,
398                 sk: 3,
399                 bk: true,
400                 items: 2,
401                 boss: 'vitreous',
402                 bosses: ['vitreous'],
403                 bossRoom: 0x90,
404                 prize: true,
405                 prizeMask: 0x01,
406                 offset: DUNGEON_IDS.MM,
407                 mask: DUNGEON_MASKS.MM,
408                 checks: [
409                         'mm-bridge-chest',
410                         'mm-spike-chest',
411                         'mm-lobby-chest',
412                         'mm-compass-chest',
413                         'mm-big-key-chest',
414                         'mm-big-chest',
415                         'mm-map-chest',
416                         'mm-boss-defeated',
417                 ],
418         },
419         {
420                 id: 'tr',
421                 map: true,
422                 compass: true,
423                 sk: 4,
424                 bk: true,
425                 items: 5,
426                 boss: 'trinexx',
427                 bosses: ['trinexx'],
428                 bossRoom: 0xA4,
429                 prize: true,
430                 prizeMask: 0x08,
431                 offset: DUNGEON_IDS.TR,
432                 mask: DUNGEON_MASKS.TR,
433                 checks: [
434                         'tr-roller-left',
435                         'tr-roller-right',
436                         'tr-compass-chest',
437                         'tr-chomps',
438                         'tr-big-key-chest',
439                         'tr-big-chest',
440                         'tr-crysta-roller',
441                         'tr-laser-bridge-top',
442                         'tr-laser-bridge-left',
443                         'tr-laser-bridge-right',
444                         'tr-laser-bridge-bottom',
445                         'tr-boss-defeated',
446                 ],
447         },
448 ];
449
450 export const OVERWORLD_LOCATIONS = [
451         {
452                 id: 'blacksmith',
453                 address: 0x411,
454                 mask: 0x04,
455         },
456         {
457                 id: 'bombos-tablet',
458                 address: 0x411,
459                 mask: 0x02,
460         },
461         {
462                 id: 'bottle-vendor',
463                 address: 0x3C9,
464                 mask: 0x02,
465         },
466         {
467                 id: 'bumper-cave',
468                 address: 0x2CA,
469                 mask: 0x40,
470         },
471         {
472                 id: 'catfish',
473                 address: 0x410,
474                 mask: 0x20,
475         },
476         {
477                 id: 'desert-ledge',
478                 address: 0x2B0,
479                 mask: 0x40,
480         },
481         {
482                 id: 'digging-game',
483                 address: 0x2E8,
484                 mask: 0x40,
485         },
486         {
487                 id: 'ether-tablet',
488                 address: 0x411,
489                 mask: 0x01,
490         },
491         {
492                 id: 'floating-island',
493                 address: 0x285,
494                 mask: 0x40,
495         },
496         {
497                 id: 'flute-spot',
498                 address: 0x2AA,
499                 mask: 0x40,
500         },
501         {
502                 id: 'hobo',
503                 address: 0x3C9,
504                 mask: 0x01,
505         },
506         {
507                 id: 'lake-hylia-island',
508                 address: 0x2B5,
509                 mask: 0x40,
510         },
511         {
512                 id: 'library',
513                 address: 0x410,
514                 mask: 0x80,
515         },
516         {
517                 id: 'magic-bat',
518                 address: 0x411,
519                 mask: 0x80,
520         },
521         {
522                 id: 'mushroom-spot',
523                 address: 0x411,
524                 mask: 0x10,
525         },
526         {
527                 id: 'old-man',
528                 address: 0x410,
529                 mask: 0x01,
530         },
531         {
532                 id: 'pedestal',
533                 address: 0x300,
534                 mask: 0x40,
535         },
536         {
537                 id: 'potion-shop',
538                 address: 0x411,
539                 mask: 0x20,
540         },
541         {
542                 id: 'purple-chest',
543                 address: 0x3C9,
544                 mask: 0x10,
545         },
546         {
547                 id: 'pyramid',
548                 address: 0x2DB,
549                 mask: 0x40,
550         },
551         {
552                 id: 'race-game',
553                 address: 0x2A8,
554                 mask: 0x40,
555         },
556         {
557                 id: 'saha',
558                 address: 0x410,
559                 mask: 0x10,
560         },
561         {
562                 id: 'sick-kid',
563                 address: 0x410,
564                 mask: 0x04,
565         },
566         {
567                 id: 'spec-rock',
568                 address: 0x283,
569                 mask: 0x40,
570         },
571         {
572                 id: 'stumpy',
573                 address: 0x410,
574                 mask: 0x08,
575         },
576         {
577                 id: 'sunken-treasure',
578                 address: 0x2BB,
579                 mask: 0x40,
580         },
581         {
582                 id: 'uncle',
583                 address: 0x3C6,
584                 mask: 0x01,
585         },
586         {
587                 id: 'zora',
588                 address: 0x410,
589                 mask: 0x02,
590         },
591         {
592                 id: 'zora-ledge',
593                 address: 0x301,
594                 mask: 0x40,
595         },
596 ];
597
598 export const UNDERWORLD_LOCATIONS = [
599         {
600                 id: 'aginah',
601                 room: 0x10A,
602                 chest: 0,
603         },
604         {
605                 id: 'blinds-hut-top',
606                 room: 0x11D,
607                 chest: 0,
608         },
609         {
610                 id: 'blinds-hut-left',
611                 room: 0x11D,
612                 chest: 1,
613         },
614         {
615                 id: 'blinds-hut-right',
616                 room: 0x11D,
617                 chest: 2,
618         },
619         {
620                 id: 'blinds-hut-far-left',
621                 room: 0x11D,
622                 chest: 3,
623         },
624         {
625                 id: 'blinds-hut-far-right',
626                 room: 0x11D,
627                 chest: 4,
628         },
629         {
630                 id: 'bonk-rocks',
631                 room: 0x124,
632                 chest: 0,
633         },
634         {
635                 id: 'brewery',
636                 room: 0x106,
637                 chest: 0,
638         },
639         {
640                 id: 'c-house',
641                 room: 0x11C,
642                 chest: 0,
643         },
644         {
645                 id: 'cave-45',
646                 room: 0x11B,
647                 chest: 6,
648         },
649         {
650                 id: 'checkerboard',
651                 room: 0x126,
652                 chest: 5,
653         },
654         {
655                 id: 'chest-game',
656                 room: 0x106,
657                 chest: 6,
658         },
659         {
660                 id: 'chicken-house',
661                 room: 0x108,
662                 chest: 0,
663         },
664         {
665                 id: 'ct-1',
666                 area: 'ct',
667                 room: 0xE0,
668                 chest: 0,
669         },
670         {
671                 id: 'ct-2',
672                 area: 'ct',
673                 room: 0xD0,
674                 chest: 0,
675         },
676         {
677                 id: 'dark-cross',
678                 area: 'hc',
679                 room: 0x32,
680                 chest: 0,
681         },
682         {
683                 id: 'dp-big-chest',
684                 area: 'dp',
685                 room: 0x73,
686                 chest: 0,
687         },
688         {
689                 id: 'dp-big-key-chest',
690                 area: 'dp',
691                 room: 0x75,
692                 chest: 0,
693         },
694         {
695                 id: 'dp-compass-chest',
696                 area: 'dp',
697                 room: 0x85,
698                 chest: 0,
699         },
700         {
701                 id: 'dp-map-chest',
702                 area: 'dp',
703                 room: 0x74,
704                 chest: 0,
705         },
706         {
707                 id: 'dp-torch',
708                 area: 'dp',
709                 room: 0x73,
710                 chest: 6,
711         },
712         {
713                 id: 'ep-big-chest',
714                 area: 'ep',
715                 room: 0xA9,
716                 chest: 0,
717         },
718         {
719                 id: 'ep-big-key-chest',
720                 area: 'ep',
721                 room: 0xB8,
722                 chest: 0,
723         },
724         {
725                 id: 'ep-cannonball',
726                 area: 'ep',
727                 room: 0xB9,
728                 chest: 0,
729         },
730         {
731                 id: 'ep-compass-chest',
732                 area: 'ep',
733                 room: 0xA8,
734                 chest: 0,
735         },
736         {
737                 id: 'ep-map-chest',
738                 area: 'ep',
739                 room: 0xAA,
740                 chest: 0,
741         },
742         {
743                 id: 'flooded-chest',
744                 room: 0x10B,
745                 chest: 0,
746         },
747         {
748                 id: 'graveyard-ledge',
749                 room: 0x11B,
750                 chest: 5,
751         },
752         {
753                 id: 'gt-hope-left',
754                 area: 'gt',
755                 room: 0x8C,
756                 chest: 1,
757         },
758         {
759                 id: 'gt-hope-right',
760                 area: 'gt',
761                 room: 0x8C,
762                 chest: 2,
763         },
764         {
765                 id: 'gt-tile-room',
766                 area: 'gt',
767                 room: 0x8D,
768                 chest: 0,
769         },
770         {
771                 id: 'gt-compass-tl',
772                 area: 'gt',
773                 room: 0x9D,
774                 chest: 0,
775         },
776         {
777                 id: 'gt-compass-tr',
778                 area: 'gt',
779                 room: 0x9D,
780                 chest: 1,
781         },
782         {
783                 id: 'gt-compass-bl',
784                 area: 'gt',
785                 room: 0x9D,
786                 chest: 2,
787         },
788         {
789                 id: 'gt-compass-br',
790                 area: 'gt',
791                 room: 0x9D,
792                 chest: 3,
793         },
794         {
795                 id: 'gt-torch',
796                 area: 'gt',
797                 room: 0x8C,
798                 chest: 6,
799         },
800         {
801                 id: 'gt-dm-tl',
802                 area: 'gt',
803                 room: 0x7B,
804                 chest: 0,
805         },
806         {
807                 id: 'gt-dm-tr',
808                 area: 'gt',
809                 room: 0x7B,
810                 chest: 1,
811         },
812         {
813                 id: 'gt-dm-bl',
814                 area: 'gt',
815                 room: 0x7B,
816                 chest: 2,
817         },
818         {
819                 id: 'gt-dm-br',
820                 area: 'gt',
821                 room: 0x7B,
822                 chest: 3,
823         },
824         {
825                 id: 'gt-map-chest',
826                 area: 'gt',
827                 room: 0x8B,
828                 chest: 0,
829         },
830         {
831                 id: 'gt-firesnake',
832                 area: 'gt',
833                 room: 0x7D,
834                 chest: 0,
835         },
836         {
837                 id: 'gt-rando-tl',
838                 area: 'gt',
839                 room: 0x7C,
840                 chest: 0,
841         },
842         {
843                 id: 'gt-rando-tr',
844                 area: 'gt',
845                 room: 0x7C,
846                 chest: 1,
847         },
848         {
849                 id: 'gt-rando-bl',
850                 area: 'gt',
851                 room: 0x7C,
852                 chest: 2,
853         },
854         {
855                 id: 'gt-rando-br',
856                 area: 'gt',
857                 room: 0x7C,
858                 chest: 3,
859         },
860         {
861                 id: 'gt-bobs-chest',
862                 area: 'gt',
863                 room: 0x8C,
864                 chest: 3,
865         },
866         {
867                 id: 'gt-ice-left',
868                 area: 'gt',
869                 room: 0x1C,
870                 chest: 1,
871         },
872         {
873                 id: 'gt-ice-mid',
874                 area: 'gt',
875                 room: 0x1C,
876                 chest: 0,
877         },
878         {
879                 id: 'gt-ice-right',
880                 area: 'gt',
881                 room: 0x1C,
882                 chest: 2,
883         },
884         {
885                 id: 'gt-big-chest',
886                 area: 'gt',
887                 room: 0x8C,
888                 chest: 0,
889         },
890         {
891                 id: 'gt-helma-left',
892                 area: 'gt',
893                 room: 0x3D,
894                 chest: 0,
895         },
896         {
897                 id: 'gt-helma-right',
898                 area: 'gt',
899                 room: 0x3D,
900                 chest: 1,
901         },
902         {
903                 id: 'gt-pre-moldorm',
904                 area: 'gt',
905                 room: 0x3D,
906                 chest: 2,
907         },
908         {
909                 id: 'gt-post-moldorm',
910                 area: 'gt',
911                 room: 0x4D,
912                 chest: 0,
913         },
914         {
915                 id: 'hammer-pegs',
916                 room: 0x127,
917                 chest: 6,
918         },
919         {
920                 id: 'hc-boom',
921                 area: 'hc',
922                 room: 0x71,
923                 chest: 0,
924         },
925         {
926                 id: 'hc-cell',
927                 area: 'hc',
928                 room: 0x80,
929                 chest: 0,
930         },
931         {
932                 id: 'hc-map-chest',
933                 area: 'hc',
934                 room: 0x72,
935                 chest: 0,
936         },
937         {
938                 id: 'hookshot-cave-br',
939                 room: 0x3C,
940                 chest: 3,
941         },
942         {
943                 id: 'hookshot-cave-tr',
944                 room: 0x3C,
945                 chest: 0,
946         },
947         {
948                 id: 'hookshot-cave-tl',
949                 room: 0x3C,
950                 chest: 1,
951         },
952         {
953                 id: 'hookshot-cave-bl',
954                 room: 0x3C,
955                 chest: 2,
956         },
957         {
958                 id: 'hype-cave-top',
959                 room: 0x11E,
960                 chest: 0,
961         },
962         {
963                 id: 'hype-cave-left',
964                 room: 0x11E,
965                 chest: 1,
966         },
967         {
968                 id: 'hype-cave-right',
969                 room: 0x11E,
970                 chest: 2,
971         },
972         {
973                 id: 'hype-cave-bottom',
974                 room: 0x11E,
975                 chest: 3,
976         },
977         {
978                 id: 'hype-cave-npc',
979                 room: 0x11E,
980                 chest: 6,
981         },
982         {
983                 id: 'ice-rod-cave',
984                 room: 0x120,
985                 chest: 0,
986         },
987         {
988                 id: 'ip-compass-chest',
989                 area: 'ip',
990                 room: 0x2E,
991                 chest: 0,
992         },
993         {
994                 id: 'ip-big-key-chest',
995                 area: 'ip',
996                 room: 0x1F,
997                 chest: 0,
998         },
999         {
1000                 id: 'ip-map-chest',
1001                 area: 'ip',
1002                 room: 0x3F,
1003                 chest: 0,
1004         },
1005         {
1006                 id: 'ip-spike-chest',
1007                 area: 'ip',
1008                 room: 0x5F,
1009                 chest: 0,
1010         },
1011         {
1012                 id: 'ip-freezor-chest',
1013                 area: 'ip',
1014                 room: 0x7E,
1015                 chest: 0,
1016         },
1017         {
1018                 id: 'ip-big-chest',
1019                 area: 'ip',
1020                 room: 0x9E,
1021                 chest: 0,
1022         },
1023         {
1024                 id: 'ip-ice-t',
1025                 area: 'ip',
1026                 room: 0xAE,
1027                 chest: 0,
1028         },
1029         {
1030                 id: 'kak-well-top',
1031                 room: 0x2F,
1032                 chest: 0,
1033         },
1034         {
1035                 id: 'kak-well-left',
1036                 room: 0x2F,
1037                 chest: 1,
1038         },
1039         {
1040                 id: 'kak-well-mid',
1041                 room: 0x2F,
1042                 chest: 2,
1043         },
1044         {
1045                 id: 'kak-well-right',
1046                 room: 0x2F,
1047                 chest: 3,
1048         },
1049         {
1050                 id: 'kak-well-bottom',
1051                 room: 0x2F,
1052                 chest: 4,
1053         },
1054         {
1055                 id: 'kings-tomb',
1056                 room: 0x113,
1057                 chest: 0,
1058         },
1059         {
1060                 id: 'links-house',
1061                 room: 0x104,
1062                 chest: 0,
1063         },
1064         {
1065                 id: 'lost-woods-hideout',
1066                 room: 0xE1,
1067                 chest: 5,
1068         },
1069         {
1070                 id: 'lumberjack',
1071                 room: 0xE2,
1072                 chest: 5,
1073         },
1074         {
1075                 id: 'mimic-cave',
1076                 room: 0x10C,
1077                 chest: 0,
1078         },
1079         {
1080                 id: 'mini-moldorm-far-left',
1081                 room: 0x123,
1082                 chest: 0,
1083         },
1084         {
1085                 id: 'mini-moldorm-left',
1086                 room: 0x123,
1087                 chest: 1,
1088         },
1089         {
1090                 id: 'mini-moldorm-right',
1091                 room: 0x123,
1092                 chest: 2,
1093         },
1094         {
1095                 id: 'mini-moldorm-far-right',
1096                 room: 0x123,
1097                 chest: 3,
1098         },
1099         {
1100                 id: 'mini-moldorm-npc',
1101                 room: 0x123,
1102                 chest: 6,
1103         },
1104         {
1105                 id: 'mm-bridge-chest',
1106                 room: 0xA2,
1107                 chest: 0,
1108         },
1109         {
1110                 id: 'mm-spike-chest',
1111                 room: 0xB3,
1112                 chest: 0,
1113         },
1114         {
1115                 id: 'mm-lobby-chest',
1116                 room: 0xC2,
1117                 chest: 0,
1118         },
1119         {
1120                 id: 'mm-compass-chest',
1121                 room: 0xC1,
1122                 chest: 0,
1123         },
1124         {
1125                 id: 'mm-big-key-chest',
1126                 room: 0xD1,
1127                 chest: 0,
1128         },
1129         {
1130                 id: 'mm-big-chest',
1131                 room: 0xC3,
1132                 chest: 0,
1133         },
1134         {
1135                 id: 'mm-map-chest',
1136                 room: 0xC3,
1137                 chest: 1,
1138         },
1139         {
1140                 id: 'mire-shed-left',
1141                 room: 0x10D,
1142                 chest: 0,
1143         },
1144         {
1145                 id: 'mire-shed-right',
1146                 room: 0x10D,
1147                 chest: 1,
1148         },
1149         {
1150                 id: 'paradox-lower-far-left',
1151                 room: 0xEF,
1152                 chest: 0,
1153         },
1154         {
1155                 id: 'paradox-lower-left',
1156                 room: 0xEF,
1157                 chest: 1,
1158         },
1159         {
1160                 id: 'paradox-lower-right',
1161                 room: 0xEF,
1162                 chest: 2,
1163         },
1164         {
1165                 id: 'paradox-lower-far-right',
1166                 room: 0xEF,
1167                 chest: 3,
1168         },
1169         {
1170                 id: 'paradox-lower-mid',
1171                 room: 0xEF,
1172                 chest: 4,
1173         },
1174         {
1175                 id: 'paradox-upper-left',
1176                 room: 0xFF,
1177                 chest: 0,
1178         },
1179         {
1180                 id: 'paradox-upper-right',
1181                 room: 0xFF,
1182                 chest: 1,
1183         },
1184         {
1185                 id: 'pd-shooter-room',
1186                 room: 0x09,
1187                 chest: 0,
1188         },
1189         {
1190                 id: 'pd-stalfos-basement',
1191                 room: 0x0A,
1192                 chest: 0,
1193         },
1194         {
1195                 id: 'pd-big-key-chest',
1196                 room: 0x3A,
1197                 chest: 0,
1198         },
1199         {
1200                 id: 'pd-arena-bridge',
1201                 room: 0x2A,
1202                 chest: 1,
1203         },
1204         {
1205                 id: 'pd-arena-ledge',
1206                 room: 0x2A,
1207                 chest: 0,
1208         },
1209         {
1210                 id: 'pd-map-chest',
1211                 room: 0x2B,
1212                 chest: 0,
1213         },
1214         {
1215                 id: 'pd-big-chest',
1216                 room: 0x1A,
1217                 chest: 0,
1218         },
1219         {
1220                 id: 'pd-compass-chest',
1221                 room: 0x1A,
1222                 chest: 1,
1223         },
1224         {
1225                 id: 'pd-harmless-hellway',
1226                 room: 0x1A,
1227                 chest: 2,
1228         },
1229         {
1230                 id: 'pd-maze-top',
1231                 room: 0x19,
1232                 chest: 0,
1233         },
1234         {
1235                 id: 'pd-maze-bottom',
1236                 room: 0x19,
1237                 chest: 1,
1238         },
1239         {
1240                 id: 'pd-basement-left',
1241                 room: 0x6A,
1242                 chest: 0,
1243         },
1244         {
1245                 id: 'pd-basement-right',
1246                 room: 0x6A,
1247                 chest: 1,
1248         },
1249         {
1250                 id: 'pyramid-fairy-left',
1251                 room: 0x116,
1252                 chest: 0,
1253         },
1254         {
1255                 id: 'pyramid-fairy-right',
1256                 room: 0x116,
1257                 chest: 1,
1258         },
1259         {
1260                 id: 'saha-left',
1261                 room: 0x105,
1262                 chest: 0,
1263         },
1264         {
1265                 id: 'saha-mid',
1266                 room: 0x105,
1267                 chest: 1,
1268         },
1269         {
1270                 id: 'saha-right',
1271                 room: 0x105,
1272                 chest: 2,
1273         },
1274         {
1275                 id: 'sanc',
1276                 area: 'hc',
1277                 room: 0x12,
1278                 chest: 0,
1279         },
1280         {
1281                 id: 'secret-passage',
1282                 room: 0x55,
1283                 chest: 0,
1284         },
1285         {
1286                 id: 'sewers-left',
1287                 area: 'hc',
1288                 room: 0x11,
1289                 chest: 0,
1290         },
1291         {
1292                 id: 'sewers-mid',
1293                 area: 'hc',
1294                 room: 0x11,
1295                 chest: 1,
1296         },
1297         {
1298                 id: 'sewers-right',
1299                 area: 'hc',
1300                 room: 0x11,
1301                 chest: 2,
1302         },
1303         {
1304                 id: 'sp-lobby',
1305                 area: 'sp',
1306                 room: 0x28,
1307                 chest: 0,
1308         },
1309         {
1310                 id: 'sp-map-chest',
1311                 area: 'sp',
1312                 room: 0x37,
1313                 chest: 0,
1314         },
1315         {
1316                 id: 'sp-big-chest',
1317                 area: 'sp',
1318                 room: 0x36,
1319                 chest: 0,
1320         },
1321         {
1322                 id: 'sp-compass-chest',
1323                 area: 'sp',
1324                 room: 0x46,
1325                 chest: 0,
1326         },
1327         {
1328                 id: 'sp-west-chest',
1329                 area: 'sp',
1330                 room: 0x34,
1331                 chest: 0,
1332         },
1333         {
1334                 id: 'sp-big-key-chest',
1335                 area: 'sp',
1336                 room: 0x35,
1337                 chest: 0,
1338         },
1339         {
1340                 id: 'sp-flooded-left',
1341                 area: 'sp',
1342                 room: 0x76,
1343                 chest: 0,
1344         },
1345         {
1346                 id: 'sp-flooded-right',
1347                 area: 'sp',
1348                 room: 0x76,
1349                 chest: 1,
1350         },
1351         {
1352                 id: 'sp-waterfall',
1353                 area: 'sp',
1354                 room: 0x66,
1355                 chest: 0,
1356         },
1357         {
1358                 id: 'spec-rock-cave',
1359                 room: 0xEA,
1360                 chest: 6,
1361         },
1362         {
1363                 id: 'spike-cave',
1364                 room: 0x117,
1365                 chest: 0,
1366         },
1367         {
1368                 id: 'spiral-cave',
1369                 room: 0xFE,
1370                 chest: 0,
1371         },
1372         {
1373                 id: 'super-bunny-top',
1374                 room: 0xF8,
1375                 chest: 0,
1376         },
1377         {
1378                 id: 'super-bunny-bottom',
1379                 room: 0xF8,
1380                 chest: 0,
1381         },
1382         {
1383                 id: 'sw-big-chest',
1384                 area: 'sw',
1385                 room: 0x58,
1386                 chest: 0,
1387         },
1388         {
1389                 id: 'sw-map-chest',
1390                 area: 'sw',
1391                 room: 0x58,
1392                 chest: 1,
1393         },
1394         {
1395                 id: 'sw-compass-chest',
1396                 area: 'sw',
1397                 room: 0x67,
1398                 chest: 0,
1399         },
1400         {
1401                 id: 'sw-big-key-chest',
1402                 area: 'sw',
1403                 room: 0x57,
1404                 chest: 0,
1405         },
1406         {
1407                 id: 'sw-pot-prison',
1408                 area: 'sw',
1409                 room: 0x57,
1410                 chest: 1,
1411         },
1412         {
1413                 id: 'sw-pinball-room',
1414                 area: 'sw',
1415                 room: 0x68,
1416                 chest: 0,
1417         },
1418         {
1419                 id: 'sw-bridge-chest',
1420                 area: 'sw',
1421                 room: 0x59,
1422                 chest: 0,
1423         },
1424         {
1425                 id: 'tavern',
1426                 room: 0x103,
1427                 chest: 0,
1428         },
1429         {
1430                 id: 'th-basement-cage',
1431                 area: 'th',
1432                 room: 0x87,
1433                 chest: 6,
1434         },
1435         {
1436                 id: 'th-big-key-chest',
1437                 area: 'th',
1438                 room: 0x87,
1439                 chest: 0,
1440         },
1441         {
1442                 id: 'th-map-chest',
1443                 area: 'th',
1444                 room: 0x77,
1445                 chest: 0,
1446         },
1447         {
1448                 id: 'th-big-chest',
1449                 area: 'th',
1450                 room: 0x27,
1451                 chest: 0,
1452         },
1453         {
1454                 id: 'th-compass-chest',
1455                 area: 'th',
1456                 room: 0x27,
1457                 chest: 1,
1458         },
1459         {
1460                 id: 'tr-roller-left',
1461                 area: 'tr',
1462                 room: 0xB7,
1463                 chest: 0,
1464         },
1465         {
1466                 id: 'tr-roller-right',
1467                 area: 'tr',
1468                 room: 0xB7,
1469                 chest: 1,
1470         },
1471         {
1472                 id: 'tr-compass-chest',
1473                 area: 'tr',
1474                 room: 0xD6,
1475                 chest: 0,
1476         },
1477         {
1478                 id: 'tr-chomps',
1479                 area: 'tr',
1480                 room: 0xB6,
1481                 chest: 0,
1482         },
1483         {
1484                 id: 'tr-big-key-chest',
1485                 area: 'tr',
1486                 room: 0x14,
1487                 chest: 0,
1488         },
1489         {
1490                 id: 'tr-big-chest',
1491                 area: 'tr',
1492                 room: 0x24,
1493                 chest: 0,
1494         },
1495         {
1496                 id: 'tr-crysta-roller',
1497                 area: 'tr',
1498                 room: 0x04,
1499                 chest: 0,
1500         },
1501         {
1502                 id: 'tr-laser-bridge-top',
1503                 area: 'tr',
1504                 room: 0xD5,
1505                 chest: 0,
1506         },
1507         {
1508                 id: 'tr-laser-bridge-left',
1509                 area: 'tr',
1510                 room: 0xD5,
1511                 chest: 1,
1512         },
1513         {
1514                 id: 'tr-laser-bridge-right',
1515                 area: 'tr',
1516                 room: 0xD5,
1517                 chest: 2,
1518         },
1519         {
1520                 id: 'tr-laser-bridge-bottom',
1521                 area: 'tr',
1522                 room: 0xD5,
1523                 chest: 3,
1524         },
1525         {
1526                 id: 'tt-map-chest',
1527                 area: 'tt',
1528                 room: 0xDB,
1529                 chest: 0,
1530         },
1531         {
1532                 id: 'tt-big-key-chest',
1533                 area: 'tt',
1534                 room: 0xDB,
1535                 chest: 1,
1536         },
1537         {
1538                 id: 'tt-ambush-chest',
1539                 area: 'tt',
1540                 room: 0xCB,
1541                 chest: 0,
1542         },
1543         {
1544                 id: 'tt-compass-chest',
1545                 area: 'tt',
1546                 room: 0xDC,
1547                 chest: 0,
1548         },
1549         {
1550                 id: 'tt-attic',
1551                 area: 'tt',
1552                 room: 0x65,
1553                 chest: 0,
1554         },
1555         {
1556                 id: 'tt-cell',
1557                 area: 'tt',
1558                 room: 0x45,
1559                 chest: 0,
1560         },
1561         {
1562                 id: 'tt-big-chest',
1563                 area: 'tt',
1564                 room: 0x44,
1565                 chest: 0,
1566         },
1567         {
1568                 id: 'waterfall-fairy-left',
1569                 room: 0x114,
1570                 chest: 0,
1571         },
1572         {
1573                 id: 'waterfall-fairy-right',
1574                 room: 0x114,
1575                 chest: 1,
1576         },
1577 ];
1578
1579 export const getConfigValue = (config, name, fallback) =>
1580         Object.prototype.hasOwnProperty.call(config, name) ? config[name] : fallback;
1581
1582 export const configureDungeons = config => DUNGEONS.map(dungeon => {
1583         const newDungeon = JSON.parse(JSON.stringify(dungeon));
1584         if (config.wildMap && dungeon.map) {
1585                 ++newDungeon.items;
1586         }
1587         if (config.wildCompass && dungeon.compass) {
1588                 ++newDungeon.items;
1589         }
1590         if (config.wildSmall) {
1591                 newDungeon.items += dungeon.sk;
1592         }
1593         if (config.wildBig && dungeon.bk && !dungeon.dropBk) {
1594                 ++newDungeon.items;
1595         }
1596         if (dungeon.boss) {
1597                 newDungeon.bosses = config.bossShuffle ? BOSSES : [dungeon.boss];
1598         }
1599         return newDungeon;
1600 });
1601
1602 export const applyLogic = (config, dungeons, state) => {
1603         const logic = Logic[config.worldState];
1604         const map = {};
1605         for (const name in logic) {
1606                 try {
1607                         map[name] = logic[name](config, dungeons, state);
1608                 } catch (e) {
1609                         console.error('error evaluating', name, e);
1610                 }
1611         }
1612         return map;
1613 };
1614
1615 export const shouldShowDungeonItem = (config, which) => {
1616         const show = config[`show${which}`] || 'always';
1617         const wild = config[`wild${which}`] || false;
1618         switch (show) {
1619                 default:
1620                 case 'always':
1621                         return true;
1622                 case 'situational':
1623                         return wild || (which === 'Compass' && config.bossShuffle);
1624                 case 'never':
1625                         return false;
1626         }
1627 };
1628
1629 export const showsFullKeysanity = (config) =>
1630         shouldShowDungeonItem(config, 'Map') &&
1631         shouldShowDungeonItem(config, 'Compass') &&
1632         shouldShowDungeonItem(config, 'Small') &&
1633         shouldShowDungeonItem(config, 'Big');
1634
1635 export const shouldCompactKeysanity = (config) =>
1636         config.compactKeysanity && showsFullKeysanity(config);
1637
1638 export const toggleBoolean = name => state => ({
1639         ...state,
1640         [name]: !state[name],
1641 });
1642
1643 export const increment = (name, max, skipZero) => state => {
1644         let newValue = ((state[name] || 0) + 1) % (max + 1);
1645         if (skipZero && !newValue) {
1646                 newValue = 1;
1647         }
1648         return {
1649                 ...state,
1650                 [name]: newValue,
1651         };
1652 };
1653
1654 export const decrement = (name, max, skipZero) => state => {
1655         let newValue = ((state[name] || 0) + max) % (max + 1);
1656         if (skipZero && !newValue) {
1657                 newValue = max;
1658         }
1659         return {
1660                 ...state,
1661                 [name]: newValue,
1662         };
1663 };
1664
1665 export const highestActive = (state, names) => {
1666         for (let i = names.length; i >= 0; --i) {
1667                 if (state[names[i]]) {
1668                         return names[i];
1669                 }
1670         }
1671         return null;
1672 };
1673
1674 export const clearAll = names => state => {
1675         const changes = names.reduce((acc, cur) => ({ ...acc, [cur]: true }), {});
1676         return { ...state, ...changes };
1677 };
1678
1679 export const unclearAll = names => state => {
1680         const changes = names.reduce((acc, cur) => ({ ...acc, [cur]: false }), {});
1681         return { ...state, ...changes };
1682 };
1683
1684 export const countClearedLocations = (state, locations) =>
1685         locations.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0);
1686
1687 export const hasClearedLocations = (state, locations) =>
1688         countClearedLocations(state, locations) === locations.length;
1689
1690 export const getLocationStatus = (name, logic, state) => {
1691         if (state[name]) return 'cleared';
1692         if (logic[name]) return logic[name];
1693         return logic.fallback;
1694 };
1695
1696 export const getCombinedStatus = statuses => {
1697         if (statuses.filter(s => s === 'cleared').length === statuses.length) {
1698                 return 'cleared';
1699         }
1700         if (statuses.filter(s => ['available', 'cleared'].includes(s)).length === statuses.length) {
1701                 return 'available';
1702         }
1703         if (statuses.filter(s => ['unavailable', 'cleared'].includes(s)).length === statuses.length) {
1704                 return 'unavailable';
1705         }
1706         return 'partial';
1707 };
1708
1709 export const aggregateLocationStatus = (names, logic, state) => {
1710         const statuses = names.map(name => getLocationStatus(name, logic, state));
1711         return getCombinedStatus(statuses);
1712 };
1713
1714 export const countRemainingLocations = (state, locations) =>
1715         locations.reduce((acc, cur) => state[cur] ? acc : acc + 1, 0);
1716
1717 export const getGanonCrystals = (config) => getConfigValue(config, 'ganon-crystals', 7);
1718
1719 export const getGTCrystals = (config) => getConfigValue(config, 'gt-crystals', 7);
1720
1721 export const getGTBoss = (state, which) => state[`gt-${which}-boss`];
1722
1723 export const hasDungeonBoss = (state, dungeon) =>
1724         !dungeon.boss || !!state[`${dungeon.id}-boss-defeated`];
1725
1726 export const getDungeonBoss = (state, dungeon) =>
1727         dungeon.bosses.length > 1
1728                 ? state[`${dungeon.id}-boss`] || dungeon.boss || null
1729                 : dungeon.bosses[0];
1730
1731 export const hasDungeonPrize = (state, dungeon) =>
1732         !dungeon.prize || !!state[`${dungeon.id}-prize-acquired`];
1733
1734 export const getDungeonPrize = (state, dungeon) => state[`${dungeon.id}-prize`] || null;
1735
1736 export const getDungeonClearedItems = (state, dungeon) => state[`${dungeon.id}-checks`] || 0;
1737
1738 export const getDungeonRemainingItems = (state, dungeon) =>
1739         Math.max(0, dungeon.items - getDungeonClearedItems(state, dungeon));
1740
1741 export const getDungeonAcquiredSKs = (state, dungeon) => state[`${dungeon.id}-small-key`] || 0;
1742
1743 export const isDungeonCleared = (state, dungeon) => {
1744         const hasItems = !getDungeonRemainingItems(state, dungeon);
1745         const hasBoss = hasDungeonBoss(state, dungeon);
1746         const hasPrize = hasDungeonPrize(state, dungeon);
1747         return hasItems && hasBoss && hasPrize;
1748 };
1749
1750 export const aggregateDungeonStatus = (dungeon, logic, state) => {
1751         if (isDungeonCleared(state, dungeon)) {
1752                 return 'cleared';
1753         }
1754         if (logic[dungeon.id] === 'unavailable') {
1755                 return 'unavailable';
1756         }
1757         const checks = [...dungeon.checks];
1758         if (['ct', 'gt'].includes(dungeon.id)) {
1759                 checks.push(`${dungeon.id}-boss-killable`);
1760         }
1761         const statuses = checks.map(name => getLocationStatus(name, logic, state));
1762         return getCombinedStatus(statuses);
1763 };
1764
1765 export const toggleBossDefeated = dungeon => toggleBoolean(`${dungeon.id}-boss-defeated`);
1766
1767 export const setBossDefeated = (dungeon, defeated) =>
1768         state => ({ ...state, [`${dungeon.id}-boss-defeated`]: !!defeated });
1769
1770 export const togglePrizeAcquired = dungeon => toggleBoolean(`${dungeon.id}-prize-acquired`);
1771
1772 export const setPrizeAcquired = (dungeon, acquired) =>
1773         state => ({ ...state, [`${dungeon.id}-prize-acquired`]: !!acquired });
1774
1775 export const addDungeonCheck = dungeon => increment(`${dungeon.id}-checks`, dungeon.items);
1776
1777 export const removeDungeonCheck = dungeon => decrement(`${dungeon.id}-checks`, dungeon.items);
1778
1779 export const resetDungeonChecks = dungeon => state => ({ ...state, [`${dungeon.id}-checks`]: 0 });
1780
1781 export const completeDungeonChecks = dungeon =>
1782         state => ({ ...state, [`${dungeon.id}-checks`]: dungeon.items });
1783
1784 export const makeEmptyState = () => {
1785         const state = {};
1786         BOOLEAN_STATES.forEach(p => {
1787                 state[p] = INITIAL[p] || false;
1788         });
1789         INTEGER_STATES.forEach(p => {
1790                 state[p] = INITIAL[p] || 0;
1791         });
1792         DUNGEONS.forEach(dungeon => {
1793                 state[`${dungeon.id}-map`] = false;
1794                 state[`${dungeon.id}-compass`] = false;
1795                 state[`${dungeon.id}-small-key`] = 0;
1796                 state[`${dungeon.id}-big-key`] = false;
1797                 state[`${dungeon.id}-checks`] = 0;
1798                 if (dungeon.boss) {
1799                         state[`${dungeon.id}-boss`] = dungeon.boss;
1800                         state[`${dungeon.id}-boss-defeated`] = false;
1801                 }
1802                 if (dungeon.prize) {
1803                         state[`${dungeon.id}-prize`] = 'crystal';
1804                         state[`${dungeon.id}-prize-acquired`] = false;
1805                 }
1806                 if (dungeon.id === 'gt') {
1807                         state['gt-bot-boss'] = 'armos';
1808                         state['gt-mid-boss'] = 'lanmolas';
1809                         state['gt-top-boss'] = 'moldorm';
1810                 }
1811         });
1812         OVERWORLD_LOCATIONS.forEach(location => {
1813                 state[location.id] = false;
1814         });
1815         UNDERWORLD_LOCATIONS.forEach(location => {
1816                 state[location.id] = false;
1817         });
1818         state['mm-medallion'] = null;
1819         state['tr-medallion'] = null;
1820         return state;
1821 };
1822
1823 const collectInventory = (state, data, prizeMap) => {
1824         state.bow = !!(data[INV_ADDR.RANDO_BOW] & 0x80);
1825         state.silvers = (data[INV_ADDR.RANDO_BOW] & 0xC0) == 0xC0;
1826         state['bowless-silvers'] = (data[INV_ADDR.RANDO_BOW] & 0xC0) == 0x40;
1827         state['blue-boomerang'] = !!(data[INV_ADDR.RANDO_BOOM] & 0x40);
1828         state['red-boomerang'] = !!(data[INV_ADDR.RANDO_BOOM] & 0x80);
1829         state.hookshot = !!data[INV_ADDR.HOOK];
1830         state.bomb = data[INV_ADDR.BOMB];
1831         state.mushroom = !!(data[INV_ADDR.RANDO_POWDER] & 0x20);
1832         state.powder = !!(data[INV_ADDR.RANDO_POWDER] & 0x10);
1833         state['fire-rod'] = !!data[INV_ADDR.FROD];
1834         state['ice-rod'] = !!data[INV_ADDR.IROD];
1835         state.bombos = !!data[INV_ADDR.BOMBOS];
1836         state.ether = !!data[INV_ADDR.ETHER];
1837         state.quake = !!data[INV_ADDR.QUAKE];
1838         state.lamp = !!data[INV_ADDR.LAMP];
1839         state.hammer = !!data[INV_ADDR.HAMMER];
1840         state.shovel = !!(data[INV_ADDR.RANDO_FLUTE] & 0x04);
1841         state.flute = !!(data[INV_ADDR.RANDO_FLUTE] & 0x03);
1842         state.duck = !!(data[INV_ADDR.RANDO_FLUTE] & 0x01);
1843         state.bugnet = !!data[INV_ADDR.BUGNET];
1844         state.book = !!data[INV_ADDR.BOOK];
1845         state['bottle-1'] = data[INV_ADDR.BOTTLE_1];
1846         state['bottle-2'] = data[INV_ADDR.BOTTLE_2];
1847         state['bottle-3'] = data[INV_ADDR.BOTTLE_3];
1848         state['bottle-4'] = data[INV_ADDR.BOTTLE_4];
1849         state.somaria = !!data[INV_ADDR.SOMARIA];
1850         state.byrna = !!data[INV_ADDR.BYRNA];
1851         state.cape = !!data[INV_ADDR.CAPE];
1852         state.mirror = !!data[INV_ADDR.MIRROR];
1853         state.lift = data[INV_ADDR.GLOVE];
1854         state.boots = !!data[INV_ADDR.BOOTS];
1855         state.flippers = !!data[INV_ADDR.FLIPPERS];
1856         state.moonpearl = !!data[INV_ADDR.MOONPEARL];
1857         state.sword = data[INV_ADDR.SWORD];
1858         state.shield = data[INV_ADDR.SHIELD];
1859         state.mail = data[INV_ADDR.ARMOR] + 1;
1860         state['heart-piece'] = data[INV_ADDR.HEART_PIECE];
1861         state['half-magic'] = data[INV_ADDR.MAGIC_USE] > 0;
1862         state['quarter-magic'] = data[INV_ADDR.MAGIC_USE] > 1;
1863         const map = getShort(data, INV_ADDR.MAP);
1864         const compass = getShort(data, INV_ADDR.COMPASS);
1865         const bigKey = getShort(data, INV_ADDR.BIG_KEY);
1866         DUNGEONS.forEach(dungeon => {
1867                 state[`${dungeon.id}-map`] = !!(map & dungeon.mask);
1868                 state[`${dungeon.id}-compass`] = !!(compass & dungeon.mask);
1869                 state[`${dungeon.id}-small-key`] = data[INV_ADDR.RANDO_KEY_START + dungeon.offset];
1870                 state[`${dungeon.id}-big-key`] = !!(bigKey & dungeon.mask);
1871                 state[`${dungeon.id}-checks-collected`] =
1872                         data[INV_ADDR.RANDO_CHECKS_START + dungeon.offset];
1873                 if (dungeon.prize) {
1874                         const isCrystal = prizeMap[dungeon.offset].isCrystal;
1875                         const prizeFlags = data[isCrystal ? INV_ADDR.CRYSTALS : INV_ADDR.PENDANTS];
1876                         const prizeAcquired = !!(prizeFlags & prizeMap[dungeon.offset].mask);
1877                         state[`${dungeon.id}-prize-acquired`] = prizeAcquired;
1878                         if (prizeAcquired) {
1879                                 if (!isCrystal) {
1880                                         if (prizeMap[dungeon.offset].mask === 1) {
1881                                                 state[`${dungeon.id}-prize`] = 'red-pendant';
1882                                         } else if (prizeMap[dungeon.offset].mask === 2) {
1883                                                 state[`${dungeon.id}-prize`] = 'blue-pendant';
1884                                         } else if (prizeMap[dungeon.offset].mask === 4) {
1885                                                 state[`${dungeon.id}-prize`] = 'green-pendant';
1886                                         }
1887                                 } else {
1888                                         state[`${dungeon.id}-prize`] = 'crystal';
1889                                 }
1890                         }
1891                 }
1892         });
1893 };
1894
1895 const collectOverworld = (state, data) => {
1896         OVERWORLD_LOCATIONS.forEach(location => {
1897                 state[location.id] = !!(data[location.address] & location.mask);
1898         });
1899 };
1900
1901 const collectUnderworld = (state, data) => {
1902         UNDERWORLD_LOCATIONS.forEach(location => {
1903                 state[location.id] = isChestOpen(data, location.room, location.chest);
1904         });
1905         DUNGEONS.forEach(dungeon => {
1906                 state[`${dungeon.id}-boss-defeated`] = isBossDefeated(data, dungeon.bossRoom);
1907         });
1908 };
1909
1910 const getDungeonAmounts = (config, state) => {
1911         const amounts = {};
1912         DUNGEONS.forEach(dungeon => {
1913                 let amount = 0;
1914                 let total = dungeon.checks.length;
1915                 if (config.checkCalculation === 'inventory') {
1916                         amount = state[`${dungeon.id}-checks-collected`];
1917                 } else {
1918                         dungeon.checks.forEach(check => {
1919                                 if (state[check]) {
1920                                         ++amount;
1921                                 }
1922                         });
1923                 }
1924                 if (!config.wildMap && state[`${dungeon.id}-map`]) {
1925                         --amount;
1926                         --total;
1927                 }
1928                 if (!config.wildCompass && state[`${dungeon.id}-compass`]) {
1929                         --amount;
1930                         --total;
1931                 }
1932                 if (!config.wildSmall) {
1933                         amount -= Math.min(state[`${dungeon.id}-small-key`], dungeon.sk);
1934                         total -= dungeon.sk;
1935                 }
1936                 if (!config.wildBig && !dungeon.dropBk && state[`${dungeon.id}-big-key`]) {
1937                         --amount;
1938                         --total;
1939                 }
1940                 amounts[dungeon.id] = Math.min(total, amount);
1941         });
1942         return amounts;
1943 };
1944
1945 export const computeState = (config, data, prizeMap) => {
1946         const state = {};
1947         collectInventory(state, data.slice(SRAM_ADDR.INV_START), prizeMap);
1948         collectOverworld(state, data);
1949         collectUnderworld(state, data.slice(SRAM_ADDR.ROOM_DATA_START));
1950         const amounts = getDungeonAmounts(config, state);
1951         DUNGEONS.forEach(dungeon => {
1952                 state[`${dungeon.id}-checks`] = amounts[dungeon.id];
1953         });
1954         return state;
1955 };
1956
1957 export const mergeStates = (autoState, manualState) => {
1958         const next = { ...autoState };
1959         BOOLEAN_STATES.forEach(name => {
1960                 if (manualState[name]) {
1961                         next[name] = true;
1962                 }
1963         });
1964         INTEGER_STATES.forEach(name => {
1965                 next[name] = Math.max(autoState[name] || 0, manualState[name] || 0);
1966         });
1967         DUNGEONS.forEach(dungeon => {
1968                 next[`${dungeon.id}-small-key`] += manualState[`${dungeon.id}-small-key`] || 0;
1969                 next[`${dungeon.id}-checks`] += manualState[`${dungeon.id}-checks`] || 0;
1970                 if (manualState[`${dungeon.id}-big-key`]) {
1971                         next[`${dungeon.id}-big-key`] = true;
1972                 }
1973                 if (manualState[`${dungeon.id}-compass`]) {
1974                         next[`${dungeon.id}-compass`] = true;
1975                 }
1976                 if (manualState[`${dungeon.id}-map`]) {
1977                         next[`${dungeon.id}-map`] = true;
1978                 }
1979                 if (manualState[`${dungeon.id}-boss`]) {
1980                         next[`${dungeon.id}-boss`] = manualState[`${dungeon.id}-boss`];
1981                 }
1982                 if (manualState[`${dungeon.id}-boss-defeated`]) {
1983                         next[`${dungeon.id}-boss-defeated`] = true;
1984                 }
1985                 if (manualState[`${dungeon.id}-prize`] &&
1986                         manualState[`${dungeon.id}-prize`] !== 'crystal'
1987                 ) {
1988                         next[`${dungeon.id}-prize`] = manualState[`${dungeon.id}-prize`];
1989                 } else if (!next[`${dungeon.id}-prize`]) {
1990                         next[`${dungeon.id}-prize`] = 'crystal';
1991                 }
1992                 if (manualState[`${dungeon.id}-prize-acquired`]) {
1993                         next[`${dungeon.id}-prize-acquired`] = true;
1994                 }
1995         });
1996         OVERWORLD_LOCATIONS.forEach(loc => {
1997                 if (manualState[loc.id]) {
1998                         next[loc.id] = true;
1999                 }
2000         });
2001         UNDERWORLD_LOCATIONS.forEach(loc => {
2002                 if (manualState[loc.id]) {
2003                         next[loc.id] = true;
2004                 }
2005         });
2006         // prefer auto
2007         next['bottle-1'] = autoState['bottle-1'] || manualState['bottle-1'] || 0;
2008         next['bottle-2'] = autoState['bottle-2'] || manualState['bottle-2'] || 0;
2009         next['bottle-3'] = autoState['bottle-3'] || manualState['bottle-3'] || 0;
2010         next['bottle-4'] = autoState['bottle-4'] || manualState['bottle-4'] || 0;
2011         // force manual
2012         next['mm-medallion'] = manualState['mm-medallion'];
2013         next['tr-medallion'] = manualState['tr-medallion'];
2014         next['gt-crystals'] = manualState['gt-crystals'];
2015         next['ganon-crystals'] = manualState['ganon-crystals'];
2016         next['gt-bot-boss'] = manualState['gt-bot-boss'];
2017         next['gt-mid-boss'] = manualState['gt-mid-boss'];
2018         next['gt-top-boss'] = manualState['gt-top-boss'];
2019         //console.log(next);
2020         return next;
2021 };