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