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