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