]> git.localhorst.tv Git - alttp.git/blob - resources/js/helpers/logic.js
fix aga DW access
[alttp.git] / resources / js / helpers / logic.js
1 import {
2         getDungeonBoss,
3         getDungeonPrize,
4         getGTBoss,
5         getGTCrystals,
6         hasDungeonBoss,
7         hasDungeonPrize,
8 } from './tracker';
9
10 const and = (...predicates) => (...args) =>
11         predicates.reduce((acc, cur) => acc && cur(...args), true);
12
13 const or = (...predicates) => (...args) =>
14         predicates.reduce((acc, cur) => acc || cur(...args), false);
15
16 const when = (condition, then, otherwise) => (...args) =>
17         condition(...args) ? then(...args) : otherwise(...args);
18
19 const alwaysAvailable = () => true;
20
21 const neverAvailable = () => false;
22
23 const fromBool = b => (...args) => b(...args) ? 'available' : 'unavailable';
24
25 const isInverted = (config) => config.worldState === 'inverted';
26
27 const agaDead = (config, dungeons, state) =>
28         hasDungeonBoss(state, dungeons.find(d => d.id === 'ct'));
29
30 const countCrystals = (config, dungeons, state) => dungeons
31         .filter(dungeon =>
32                 (getDungeonPrize(state, dungeon) || 'crystal').endsWith('crystal') &&
33                 dungeon.prize &&
34                 hasDungeonPrize(state, dungeon)
35         ).length;
36
37 const countRedCrystals = (config, dungeons, state) => dungeons
38         .filter(dungeon =>
39                 getDungeonPrize(state, dungeon) === 'red-crystal' &&
40                 hasDungeonPrize(state, dungeon)
41         ).length;
42
43 const hasRedCrystals = n => (...args) => countRedCrystals(...args) >= n;
44
45 const hasGTCrystals = (config, dungeons, state) =>
46         countCrystals(config, dungeons, state) >= getGTCrystals(config);
47
48 const countPendants = (config, dungeons, state) => dungeons
49         .filter(dungeon =>
50                 (getDungeonPrize(state, dungeon) || '').endsWith('pendant') &&
51                 hasDungeonPrize(state, dungeon)
52         ).length;
53
54 const hasGreenPendant = (config, dungeons, state) => dungeons
55         .filter(dungeon =>
56                 getDungeonPrize(state, dungeon) === 'green-pendant' &&
57                 hasDungeonPrize(state, dungeon)
58         ).length >= 1;
59
60 const hasPendants = n => (...args) => countPendants(...args) >= n;
61
62 // Equipment
63
64 const countBottles = (config, dungeons, state) =>
65         ['bottle-1', 'bottle-2', 'bottle-3', 'bottle-4'].filter(b => !!state[b]).length;
66
67 const hasBig = dungeon => (config, dungeons, state) =>
68         !config.wildBig || !!state[`${dungeon}-big-key`];
69
70 const hasBird = (config, dungeons, state) => !!state['bird'];
71
72 const hasBombos = (config, dungeons, state) => !!state['bombos'];
73
74 const hasBook = (config, dungeons, state) => !!state['book'];
75
76 const hasBoom = (config, dungeons, state) => !!(state['blue-boomerang'] || state['red-boomerang']);
77
78 const hasBoots = (config, dungeons, state) => !!state['boots'];
79
80 const hasBottle = n => (...args) => countBottles(...args) >= (n || 1);
81
82 const hasBow = (config, dungeons, state) => !!state['bow'];
83
84 const hasBugnet = (config, dungeons, state) => !!state['bugnet'];
85
86 const hasByrna = (config, dungeons, state) => !!state['byrna'];
87
88 const hasCape = (config, dungeons, state) => !!state['cape'];
89
90 const hasFireRod = (config, dungeons, state) => !!state['fire-rod'];
91
92 const hasFlute = (config, dungeons, state) => !!state['flute'];
93
94 const hasHammer = (config, dungeons, state) => !!state['hammer'];
95
96 const hasHookshot = (config, dungeons, state) => !!state['hookshot'];
97
98 const hasIceRod = (config, dungeons, state) => !!state['ice-rod'];
99
100 const hasLamp = (config, dungeons, state) => !!state['lamp'];
101
102 const hasMagicBars = n => (config, dungeons, state) => {
103         let bars = 1 + (state['bottle'] || 0);
104         if (state['half-magic']) {
105                 bars *= 2;
106         }
107         if (state['quarter-magic']) {
108                 bars *= 2;
109         }
110         return bars >= (n || 1);
111 };
112
113 const hasMirror = (config, dungeons, state) => !!state['mirror'];
114
115 const hasMMMedallion = (config, dungeons, state) =>
116         !!state['mm-medallion'] && !!state[state['mm-medallion']];
117
118 const hasMoonpearl = (config, dungeons, state) => !!state['moonpearl'];
119
120 const hasMushroom = (config, dungeons, state) => !!state['mushroom'];
121
122 const hasPowder = (config, dungeons, state) => !!state['powder'];
123
124 const hasQuake = (config, dungeons, state) => !!state['quake'];
125
126 const hasShovel = (config, dungeons, state) => !!state['shovel'];
127
128 const hasSmall = (dungeon, amount) => (config, dungeons, state) =>
129         !config.wildSmall || state[`${dungeon}-small-key`] >= (amount || 1);
130
131 const hasSomaria = (config, dungeons, state) => !!state['somaria'];
132
133 const hasSword = n => (config, dungeons, state) => state['sword'] >= (n || 1);
134
135 const hasTRMedallion = (config, dungeons, state) =>
136         !!state['tr-medallion'] && !!state[state['tr-medallion']];
137
138 // Abilities
139
140 const canBomb = () => true;
141
142 const canBonk = hasBoots;
143
144 const canDarkRoom = hasLamp;
145
146 const canShootArrows = hasBow;
147
148 const canFlipSwitches = or(
149         canBomb,
150         hasBoom,
151         canShootArrows,
152         hasSword(),
153         hasHammer,
154         hasHookshot,
155         hasSomaria,
156         hasByrna,
157         hasFireRod,
158         hasIceRod,
159 );
160
161 const canGetGoodBee = and(hasBugnet, hasBottle(), or(canBonk, and(hasSword(), hasQuake)));
162
163 const canLift = (config, dungeons, state) => state['lift'] >= 1;
164
165 const canHeavyLift = (config, dungeons, state) => state['lift'] >= 2;
166
167 const canActivateFlute = when(isInverted,
168         and(
169                 hasMoonpearl,
170                 or(
171                         // partial copy of east light world
172                         agaDead,
173                         and(hasMoonpearl, or(and(canLift, hasHammer), canHeavyLift)),
174                 ),
175         ),
176         alwaysAvailable,
177 );
178
179 const canFly = or(hasBird, and(hasFlute, canActivateFlute));
180
181 const canKill = damage => damage && damage < 6
182         ? or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1), canBomb, hasByrna)
183         : or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1));
184
185 const canMedallion = hasSword();
186
187 const canMeltThings = or(hasFireRod, and(hasBombos, canMedallion));
188
189 const canPassCurtains = hasSword();
190
191 const canSwim = (config, dungeons, state) => !!state['flippers'];
192
193 const canTablet = and(hasBook, hasSword(2));
194
195 const canTorch = or(hasFireRod, hasLamp);
196
197 const canTorchDarkRoom = or(canDarkRoom, canTorch);
198
199 // Regions
200
201 const westDeathMountain = or(
202         canFly,
203         and(canLift, canDarkRoom),
204 );
205
206 const westDarkDeathMountain = when(isInverted,
207         or(canFly, and(canLift, canDarkRoom)),
208         westDeathMountain,
209 );
210
211 const eastDeathMountain = when(isInverted,
212         or(
213                 and(canHeavyLift, or(
214                         // copy of eastDarkDeathMountain, to avoid circular reference
215                         westDarkDeathMountain,
216                         and(westDeathMountain, hasMoonpearl, hasHookshot, hasMirror),
217                 )),
218                 and(westDeathMountain, hasMoonpearl, hasHookshot),
219         ),
220         and(
221                 westDeathMountain,
222                 or(
223                         hasHookshot,
224                         and(hasHammer, hasMirror),
225                 ),
226         ),
227 );
228
229 const northDeathMountain = when(isInverted,
230         and(eastDeathMountain, hasMoonpearl, hasHammer),
231         and(
232                 westDeathMountain,
233                 or(
234                         hasMirror,
235                         and(hasHammer, hasHookshot),
236                 ),
237         ),
238 );
239
240 const eastDarkDeathMountain = when(isInverted,
241         or(
242                 westDarkDeathMountain,
243                 and(westDeathMountain, hasMoonpearl, hasHookshot, hasMirror),
244         ),
245         and(
246                 eastDeathMountain,
247                 canHeavyLift,
248         ),
249 );
250
251 const eastLightWorld = when(isInverted,
252         or(
253                 agaDead,
254                 and(hasMoonpearl, or(and(canLift, hasHammer), canHeavyLift)),
255                 and(canFly, canHeavyLift),
256         ),
257         alwaysAvailable,
258 );
259
260 const westLightWorld = eastLightWorld;
261
262 const southLightWorld = eastLightWorld;
263
264 const eastDarkWorld = when(isInverted,
265         or(canFly, canSwim, hasHammer, and(hasMirror, eastLightWorld)),
266         or(
267                 agaDead,
268                 and(hasMoonpearl, canHeavyLift),
269                 and(hasMoonpearl, canLift, hasHammer),
270         ),
271 );
272
273 const westDarkWorld = when(isInverted,
274         alwaysAvailable,
275         and(
276                 hasMoonpearl,
277                 or(
278                         and(canLift, hasHammer),
279                         canHeavyLift,
280                         and(eastDarkWorld, hasHookshot, or(canSwim, canLift, hasHammer)),
281                 ),
282         ),
283 );
284
285 const southDarkWorld = when(isInverted,
286         alwaysAvailable,
287         or(
288                 westDarkWorld,
289                 and(eastDarkWorld, hasMoonpearl, hasHammer),
290         ),
291 );
292
293 const mireArea = when(isInverted,
294         or(canFly, and(hasMirror, southLightWorld)),
295         and(canFly, canHeavyLift),
296 );
297
298 // Bosses
299
300 const BOSS_RULES = {
301         aga: or(hasBugnet, hasHammer, hasSword()),
302         armos: or(
303                 hasSword(), hasHammer, canShootArrows, hasBoom,
304                 and(hasMagicBars(4), or(hasFireRod, hasIceRod)),
305                 and(hasMagicBars(2), or(hasByrna, hasSomaria)),
306         ),
307         arrghus: and(hasHookshot, or(
308                 hasSword(),
309                 hasHammer,
310                 and(or(hasMagicBars(2), canShootArrows), or(hasFireRod, hasIceRod)),
311         )),
312         blind: or(hasSword(), hasHammer, hasSomaria, hasByrna),
313         helma: and(or(canBomb, hasHammer), or(hasSword(), canShootArrows)),
314         kholdstare: and(canMeltThings, or(
315                 hasHammer,
316                 hasSword(),
317                 and(hasFireRod, hasMagicBars(3)),
318                 and(hasFireRod, hasBombos, hasMagicBars(2), canMedallion),
319         )),
320         lanmolas: or(
321                 hasSword(), hasHammer, canShootArrows, hasFireRod, hasIceRod, hasByrna, hasSomaria,
322         ),
323         moldorm: or(hasSword(), hasHammer),
324         mothula: or(
325                 hasSword(),
326                 hasHammer,
327                 and(hasMagicBars(2), or(hasFireRod, hasSomaria, hasByrna)),
328                 canGetGoodBee,
329         ),
330         trinexx: and(hasFireRod, hasIceRod, or(
331                 hasSword(3),
332                 hasHammer,
333                 and(hasMagicBars(2), hasSword(2)),
334                 and(hasMagicBars(4), hasSword(1)),
335         )),
336         vitreous: or(hasSword(), hasHammer, canShootArrows),
337 };
338
339 const canKillBoss = dungeon => (config, dungeons, state) => {
340         const boss = getDungeonBoss(state, dungeons.find(d => d.id === dungeon));
341         return BOSS_RULES[boss](config, dungeons, state);
342 };
343
344 const canKillGTBoss = which => (config, dungeons, state) => {
345         const boss = getGTBoss(state, which);
346         return BOSS_RULES[boss](config, dungeons, state);
347 };
348
349 // Dungeons
350
351 const canEnterHC = when(isInverted,
352         and(hasMoonpearl, eastLightWorld),
353         alwaysAvailable,
354 );
355
356 const canEnterCT = when(isInverted,
357         westDarkDeathMountain,
358         or(hasCape, hasSword(2)),
359 );
360
361 const canEnterGT = when(isInverted,
362         and(eastLightWorld, hasGTCrystals, hasMoonpearl),
363         and(eastDarkDeathMountain, hasGTCrystals, hasMoonpearl),
364 );
365
366 const canEnterEP = when(isInverted,
367         and(hasMoonpearl, eastLightWorld),
368         alwaysAvailable,
369 );
370
371 const canEnterDPFront = when(isInverted,
372         and(southLightWorld, hasBook),
373         or(hasBook, and(mireArea, hasMirror)),
374 );
375 const canEnterDPBack = when(isInverted,
376         and(canEnterDPFront, canLift),
377         or(and(canEnterDPFront, canLift), and(mireArea, hasMirror)),
378 );
379
380 const canEnterTH = northDeathMountain;
381
382 const canEnterPD = when(isInverted,
383         eastDarkWorld,
384         and(eastDarkWorld, hasMoonpearl),
385 );
386
387 const canEnterSP = when(isInverted,
388         and(southLightWorld, hasMirror, hasMoonpearl, canSwim),
389         and(southDarkWorld, hasMirror, hasMoonpearl, canSwim),
390 );
391
392 const canEnterSWFront = when(isInverted,
393         westDarkWorld,
394         and(westDarkWorld, hasMoonpearl),
395 );
396 const canEnterSWMid = canEnterSWFront;
397 const canEnterSWBack = and(canEnterSWMid, hasFireRod);
398
399 const canEnterTT = when(isInverted,
400         westDarkWorld,
401         and(westDarkWorld, hasMoonpearl),
402 );
403
404 const canEnterIP = when(isInverted,
405         and(canSwim, canMeltThings),
406         and(canSwim, canHeavyLift, hasMoonpearl, canMeltThings),
407 );
408 const rightSideIP = or(hasHookshot, hasSmall('ip'));
409
410 const canEnterMM = when(isInverted,
411         and(
412                 mireArea,
413                 hasMMMedallion,
414                 canMedallion,
415                 or(canBonk, hasHookshot),
416                 canKill(8),
417         ),
418         and(
419                 mireArea,
420                 hasMoonpearl,
421                 hasMMMedallion,
422                 canMedallion,
423                 or(canBonk, hasHookshot),
424                 canKill(8),
425         ),
426 );
427
428 const canEnterTRFront = when(isInverted,
429         and(
430                 eastDarkDeathMountain,
431                 canMedallion,
432                 hasTRMedallion,
433                 hasSomaria,
434         ),
435         and(
436                 eastDeathMountain,
437                 canHeavyLift,
438                 hasHammer,
439                 hasMoonpearl,
440                 canMedallion,
441                 hasTRMedallion,
442                 hasSomaria,
443         ),
444 );
445 const canEnterTRWest = when(isInverted,
446         and(eastDeathMountain, hasMirror),
447         neverAvailable,
448 );
449 const canEnterTREast = and(canEnterTRWest, or(hasHookshot, hasSomaria));
450 const canEnterTRBack = when(isInverted,
451         and(eastDeathMountain, hasMirror),
452         neverAvailable,
453 );
454 const laserBridge = or(
455         and(
456                 or(canEnterTRFront, canEnterTRWest, canEnterTREast),
457                 canDarkRoom,
458                 hasSomaria,
459                 hasBig('tr'),
460                 hasSmall('tr', 3),
461         ),
462         canEnterTRBack,
463 );
464
465 // Misc Macros
466
467 const paradoxLower = and(eastDeathMountain, or(canBomb, hasBoom, canShootArrows, hasSomaria));
468
469 const canBridgeRedBomb = or(
470         and(hasMoonpearl, hasHammer),
471         and(agaDead, hasMirror),
472 );
473
474 const canRescueSmith = and(westDarkWorld, hasMoonpearl, canHeavyLift);
475
476 const Logic = {};
477
478 Logic.dungeonInterior = {
479         'hc-boom': fromBool(and(hasSmall('hc'), canKill())),
480         'hc-cell': fromBool(and(hasSmall('hc'), canKill())),
481         'dark-cross': fromBool(canTorchDarkRoom),
482         'sewers-left': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
483         'sewers-mid': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
484         'sewers-right': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))),
485         'ct-1': fromBool(canKill()),
486         'ct-2': fromBool(and(canKill(), hasSmall('ct'), canDarkRoom)),
487         'ct-boss-killable': fromBool(and(
488                 canKill(), hasSmall('ct', 2), canDarkRoom, canPassCurtains, canKillBoss('ct'),
489         )),
490         'gt-tile-room': fromBool(hasSomaria),
491         'gt-compass-tl': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
492         'gt-compass-tr': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
493         'gt-compass-bl': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
494         'gt-compass-br': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))),
495         'gt-torch': fromBool(canBonk),
496         'gt-dm-tl': fromBool(and(hasHammer, hasHookshot)),
497         'gt-dm-tr': fromBool(and(hasHammer, hasHookshot)),
498         'gt-dm-bl': fromBool(and(hasHammer, hasHookshot)),
499         'gt-dm-br': fromBool(and(hasHammer, hasHookshot)),
500         'gt-map-chest': fromBool(and(hasHammer, or(hasHookshot, canBonk), hasSmall('gt', 4))),
501         'gt-firesnake': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 3))),
502         'gt-rando-tl': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
503         'gt-rando-tr': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
504         'gt-rando-bl': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
505         'gt-rando-br': fromBool(and(hasHammer, hasHookshot, hasSmall('gt', 4))),
506         'gt-bobs-chest': fromBool(and(
507                 or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
508                 hasSmall('gt', 3),
509         )),
510         'gt-ice-left': fromBool(and(
511                 or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
512                 hasSmall('gt', 3),
513                 canKillGTBoss('bot'),
514         )),
515         'gt-ice-mid': fromBool(and(
516                 or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
517                 hasSmall('gt', 3),
518                 canKillGTBoss('bot'),
519         )),
520         'gt-ice-right': fromBool(and(
521                 or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
522                 hasSmall('gt', 3),
523                 canKillGTBoss('bot'),
524         )),
525         'gt-big-chest': fromBool(and(
526                 or(and(hasHammer, hasHookshot), and(hasFireRod, hasSomaria)),
527                 hasSmall('gt', 3),
528                 hasBig('gt'),
529         )),
530         'gt-helma-left': fromBool(and(
531                 hasBig('gt'),
532                 hasSmall('gt', 3),
533                 canShootArrows,
534                 canTorch,
535                 canKillGTBoss('mid'),
536         )),
537         'gt-helma-right': fromBool(and(
538                 hasBig('gt'),
539                 hasSmall('gt', 3),
540                 canShootArrows,
541                 canTorch,
542                 canKillGTBoss('mid'),
543         )),
544         'gt-pre-moldorm': fromBool(and(
545                 hasBig('gt'),
546                 hasSmall('gt', 3),
547                 canShootArrows,
548                 canTorch,
549                 canKillGTBoss('mid'),
550         )),
551         'gt-post-moldorm': fromBool(and(
552                 hasBig('gt'),
553                 hasSmall('gt', 4),
554                 canShootArrows,
555                 canTorch,
556                 canKillGTBoss('mid'),
557                 canKillGTBoss('top'),
558                 hasHookshot,
559         )),
560         'gt-boss-killable': fromBool(and(
561                 hasBig('gt'),
562                 hasSmall('gt', 4),
563                 canShootArrows,
564                 canTorch,
565                 canKillGTBoss('mid'),
566                 canKillGTBoss('top'),
567                 hasHookshot,
568                 canKillBoss('ct'),
569         )),
570         'ep-big-chest': fromBool(hasBig('ep')),
571         'ep-big-key-chest': fromBool(canDarkRoom),
572         'ep-boss-defeated': fromBool(and(
573                 canShootArrows, canTorchDarkRoom, hasBig('ep'), canKillBoss('ep'),
574         )),
575         'dp-big-chest': fromBool(and(canEnterDPFront, hasBig('dp'))),
576         'dp-big-key-chest': fromBool(and(canEnterDPFront, hasSmall('dp'), canKill())),
577         'dp-compass-chest': fromBool(and(canEnterDPFront, hasSmall('dp'))),
578         'dp-map-chest': fromBool(canEnterDPFront),
579         'dp-torch': fromBool(and(canEnterDPFront, canBonk)),
580         'dp-boss-defeated': fromBool(and(
581                 canEnterDPBack,
582                 canTorch,
583                 hasBig('dp'),
584                 hasSmall('dp'),
585                 canKillBoss('dp'),
586         )),
587         'th-basement-cage': fromBool(canFlipSwitches),
588         'th-map-chest': fromBool(canFlipSwitches),
589         'th-big-key-chest': fromBool(and(canFlipSwitches, hasSmall('th'), canTorch)),
590         'th-compass-chest': fromBool(and(canFlipSwitches, hasBig('th'))),
591         'th-big-chest': fromBool(and(canFlipSwitches, hasBig('th'))),
592         'th-boss-defeated': fromBool(and(
593                 canFlipSwitches,
594                 hasBig('th'),
595                 canKillBoss('th'),
596         )),
597         'pd-stalfos-basement': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))),
598         'pd-big-key-chest': fromBool(hasSmall('pd', 6)),
599         'pd-arena-bridge': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))),
600         'pd-arena-ledge': fromBool(canShootArrows),
601         'pd-map-chest': fromBool(canShootArrows),
602         'pd-compass-chest': fromBool(hasSmall('pd', 4)),
603         'pd-basement-left': fromBool(and(canTorchDarkRoom, hasSmall('pd', 4))),
604         'pd-basement-right': fromBool(and(canTorchDarkRoom, hasSmall('pd', 4))),
605         'pd-harmless-hellway': fromBool(hasSmall('pd', 6)),
606         'pd-maze-top': fromBool(and(canDarkRoom, hasSmall('pd', 6))),
607         'pd-maze-bottom': fromBool(and(canDarkRoom, hasSmall('pd', 6))),
608         'pd-big-chest': fromBool(and(canDarkRoom, hasBig('pd'), hasSmall('pd', 6))),
609         'pd-boss-defeated': fromBool(and(
610                 canDarkRoom, hasBig('pd'), hasSmall('pd', 6), canShootArrows, hasHammer, canKillBoss('pd'),
611         )),
612         'sp-map-chest': fromBool(and(hasSmall('sp'), canBomb)),
613         'sp-big-chest': fromBool(and(hasSmall('sp'), hasHammer, hasBig('sp'))),
614         'sp-compass-chest': fromBool(and(hasSmall('sp'), hasHammer)),
615         'sp-west-chest': fromBool(and(hasSmall('sp'), hasHammer)),
616         'sp-big-key-chest': fromBool(and(hasSmall('sp'), hasHammer)),
617         'sp-flooded-left': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
618         'sp-flooded-right': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
619         'sp-waterfall': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)),
620         'sp-boss-defeated': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot, canKillBoss('sp'))),
621         'sw-big-chest': fromBool(and(canEnterSWFront, hasBig('sw'))),
622         'sw-bridge-chest': fromBool(canEnterSWBack),
623         'sw-boss-defeated': fromBool(and(
624                 canEnterSWBack, canPassCurtains, hasFireRod, hasSmall('sw', 3), canKillBoss('sw'),
625         )),
626         'tt-attic': fromBool(and(hasBig('tt'), hasSmall('tt'), canBomb)),
627         'tt-cell': fromBool(hasBig('tt')),
628         'tt-big-chest': fromBool(and(hasBig('tt'), hasSmall('tt'), hasHammer)),
629         'tt-boss-defeated': fromBool(and(hasBig('tt'), hasSmall('tt'), canKillBoss('tt'))),
630         'ip-big-key-chest': fromBool(and(rightSideIP, hasHammer, canLift)),
631         'ip-map-chest': fromBool(and(rightSideIP, hasHammer, canLift)),
632         'ip-spike-chest': fromBool(rightSideIP),
633         'ip-freezor-chest': fromBool(canMeltThings),
634         'ip-big-chest': fromBool(hasBig('ip')),
635         'ip-boss-defeated': fromBool(and(
636                 hasBig('ip'),
637                 hasHammer,
638                 canLift,
639                 or(hasSmall('ip', 2), and(hasSomaria, hasSmall('ip'))),
640                 canKillBoss('ip'),
641         )),
642         'mm-lobby-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))),
643         'mm-compass-chest': fromBool(and(canTorch, hasSmall('mm', 3))),
644         'mm-big-key-chest': fromBool(and(canTorch, hasSmall('mm', 3))),
645         'mm-big-chest': fromBool(hasBig('mm')),
646         'mm-map-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))),
647         'mm-boss-defeated': fromBool(and(hasBig('mm'), canDarkRoom, hasSomaria, canKillBoss('mm'))),
648         'tr-roller-left': fromBool(and(hasFireRod, hasSomaria, or(
649                 canEnterTRFront,
650                 and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
651                 and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
652         ))),
653         'tr-roller-right': fromBool(and(hasFireRod, hasSomaria, or(
654                 canEnterTRFront,
655                 and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
656                 and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
657         ))),
658         'tr-compass-chest': fromBool(and(hasSomaria, or(
659                 canEnterTRFront,
660                 and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)),
661                 and(canEnterTRBack, canDarkRoom, hasSmall('tr', 4)),
662         ))),
663         'tr-chomps': fromBool(or(
664                 and(canEnterTRFront, hasSmall('tr')),
665                 canEnterTRWest,
666                 canEnterTREast,
667                 and(canEnterTRBack, canDarkRoom, hasSomaria),
668         )),
669         'tr-big-key-chest': fromBool(and(hasSmall('tr', 4), or(
670                 and(canEnterTRFront, hasSomaria),
671                 canEnterTRWest,
672                 canEnterTREast,
673                 and(canEnterTRBack, canDarkRoom, hasSomaria),
674         ))),
675         'tr-big-chest': fromBool(canEnterTREast),
676         'tr-crysta-roller': fromBool(or(
677                 and(hasBig('tr'), or(
678                         and(canEnterTRFront, hasSomaria, hasSmall('tr', 2)),
679                         canEnterTRWest,
680                         canEnterTREast,
681                 )),
682                 and(canEnterTRBack, canDarkRoom, hasSomaria),
683         )),
684         'tr-laser-bridge-top': fromBool(laserBridge),
685         'tr-laser-bridge-left': fromBool(laserBridge),
686         'tr-laser-bridge-right': fromBool(laserBridge),
687         'tr-laser-bridge-bottom': fromBool(laserBridge),
688         'tr-boss-defeated': fromBool(and(
689                 laserBridge,
690                 hasSmall('tr', 4),
691                 hasBig('tr'),
692                 hasSomaria,
693                 canKillBoss('tr'),
694         )),
695 };
696
697 Logic.open = {
698         fallback: fromBool(alwaysAvailable),
699         aginah: fromBool(canBomb),
700         blacksmith: fromBool(canRescueSmith),
701         'blinds-hut-top': fromBool(canBomb),
702         'bombos-tablet': fromBool(and(southDarkWorld, hasMirror, canTablet)),
703         'bonk-rocks': fromBool(canBonk),
704         brewery: fromBool(and(westDarkWorld, canBomb, hasMoonpearl)),
705         'bumper-cave': fromBool(and(westDarkWorld, hasMoonpearl, canLift, hasCape)),
706         'c-house': fromBool(and(westDarkWorld, hasMoonpearl)),
707         catfish: fromBool(and(eastDarkWorld, hasMoonpearl, canLift)),
708         'cave-45': fromBool(and(southDarkWorld, hasMirror)),
709         checkerboard: fromBool(and(mireArea, hasMirror)),
710         'chest-game': fromBool(and(westDarkWorld, hasMoonpearl)),
711         'chicken-house': fromBool(canBomb),
712         'desert-ledge': fromBool(canEnterDPFront),
713         'digging-game': fromBool(and(southDarkWorld, hasMoonpearl)),
714         'ether-tablet': fromBool(and(northDeathMountain, canTablet)),
715         'floating-island': fromBool(
716                 and(eastDarkDeathMountain, hasMoonpearl, canLift, canBomb, hasMirror),
717         ),
718         'flute-spot': fromBool(hasShovel),
719         'graveyard-ledge': fromBool(and(westDarkWorld, hasMoonpearl, hasMirror)),
720         'hammer-pegs': fromBool(and(westDarkWorld, hasHammer, hasMoonpearl, canHeavyLift)),
721         hobo: fromBool(canSwim),
722         'hookshot-cave-tl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
723         'hookshot-cave-tr': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
724         'hookshot-cave-bl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)),
725         'hookshot-cave-br': fromBool(
726                 and(eastDarkDeathMountain, hasMoonpearl, canLift, or(hasHookshot, canBonk)),
727         ),
728         'hype-cave-npc': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
729         'hype-cave-top': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
730         'hype-cave-right': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
731         'hype-cave-left': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
732         'hype-cave-bottom': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)),
733         'ice-rod-cave': fromBool(canBomb),
734         'kak-well-top': fromBool(canBomb),
735         'kings-tomb': fromBool(and(canBonk, or(canHeavyLift, and(westDarkWorld, hasMirror)))),
736         'lake-hylia-island': fromBool(
737                 and(canSwim, hasMirror, hasMoonpearl, or(eastDarkWorld, southDarkWorld)),
738         ),
739         library: fromBool(canBonk),
740         lumberjack: fromBool(and(canBonk, agaDead)),
741         'magic-bat': fromBool(and(hasPowder,
742                 or(hasHammer, and(westDarkWorld, hasMoonpearl, canHeavyLift, hasMirror)),
743         )),
744         'mimic-cave': fromBool(and(canEnterTRFront, canBomb, hasSmall('tr', 2), hasMirror, hasHammer)),
745         'mini-moldorm-left': fromBool(canBomb),
746         'mini-moldorm-right': fromBool(canBomb),
747         'mini-moldorm-far-left': fromBool(canBomb),
748         'mini-moldorm-far-right': fromBool(canBomb),
749         'mini-moldorm-npc': fromBool(canBomb),
750         'mire-shed-left': fromBool(and(mireArea, hasMoonpearl)),
751         'mire-shed-right': fromBool(and(mireArea, hasMoonpearl)),
752         'old-man': fromBool(and(westDeathMountain, canDarkRoom)),
753         'paradox-lower-far-left': fromBool(paradoxLower),
754         'paradox-lower-left': fromBool(paradoxLower),
755         'paradox-lower-right': fromBool(paradoxLower),
756         'paradox-lower-far-right': fromBool(paradoxLower),
757         'paradox-lower-mid': fromBool(paradoxLower),
758         'paradox-upper-left': fromBool(and(eastDeathMountain, canBomb)),
759         'paradox-upper-right': fromBool(and(eastDeathMountain, canBomb)),
760         pedestal: fromBool(hasPendants(3)),
761         'potion-shop': fromBool(hasMushroom),
762         'purple-chest': fromBool(and(canRescueSmith, hasMoonpearl, canHeavyLift)),
763         pyramid: fromBool(eastDarkWorld),
764         'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)),
765         'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)),
766         'race-game': fromBool(or(canBomb, canBonk)),
767         saha: fromBool(hasGreenPendant),
768         'saha-left': fromBool(or(canBomb, canBonk)),
769         'saha-mid': fromBool(or(canBomb, canBonk)),
770         'saha-right': fromBool(or(canBomb, canBonk)),
771         'sick-kid': fromBool(hasBottle(1)),
772         'spec-rock': fromBool(and(westDeathMountain, hasMirror)),
773         'spec-rock-cave': fromBool(westDeathMountain),
774         'spike-cave': fromBool(and(
775                 westDarkDeathMountain,
776                 hasMoonpearl,
777                 hasHammer,
778                 canLift,
779                 or(hasByrna, and(hasCape, hasMagicBars(2))),
780         )),
781         'spiral-cave': fromBool(eastDeathMountain),
782         stumpy: fromBool(and(southDarkWorld, hasMoonpearl)),
783         'super-bunny-top': fromBool(and(eastDarkDeathMountain, hasMoonpearl)),
784         'super-bunny-bottom': fromBool(and(eastDarkDeathMountain, hasMoonpearl)),
785         'waterfall-fairy-left': fromBool(canSwim),
786         'waterfall-fairy-right': fromBool(canSwim),
787         zora: fromBool(or(canLift, canSwim)),
788         'zora-ledge': fromBool(canSwim),
789         ct: fromBool(canEnterCT),
790         gt: fromBool(canEnterGT),
791         dp: fromBool(or(canEnterDPFront, canEnterDPBack)),
792         th: fromBool(canEnterTH),
793         pd: fromBool(canEnterPD),
794         sp: fromBool(canEnterSP),
795         sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)),
796         tt: fromBool(canEnterTT),
797         ip: fromBool(canEnterIP),
798         mm: fromBool(canEnterMM),
799         tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)),
800         ...Logic.dungeonInterior,
801 };
802
803 Logic.inverted = {
804         fallback: fromBool(alwaysAvailable),
805         aginah: fromBool(and(southLightWorld, hasMoonpearl, canBomb)),
806         blacksmith: fromBool(and(or(canHeavyLift, hasMirror), westLightWorld)),
807         'blinds-hut-top': fromBool(and(westLightWorld, hasMoonpearl, canBomb)),
808         'blinds-hut-far-left': fromBool(and(westLightWorld, hasMoonpearl)),
809         'blinds-hut-left': fromBool(and(westLightWorld, hasMoonpearl)),
810         'blinds-hut-right': fromBool(and(westLightWorld, hasMoonpearl)),
811         'blinds-hut-far-right': fromBool(and(westLightWorld, hasMoonpearl)),
812         'bombos-tablet': fromBool(and(southLightWorld, canTablet)),
813         'bonk-rocks': fromBool(and(westLightWorld, hasMoonpearl, canBonk)),
814         'bottle-vendor': fromBool(westLightWorld),
815         brewery: fromBool(canBomb),
816         'bumper-cave': fromBool(and(canLift, hasCape, hasMoonpearl, hasMirror, westLightWorld)),
817         catfish: fromBool(or(
818                 and(eastDarkWorld, canLift),
819                 and(hasMirror, southLightWorld, hasMoonpearl, canSwim),
820         )),
821         'cave-45': fromBool(and(southLightWorld, hasMoonpearl)),
822         checkerboard: fromBool(and(southLightWorld, hasMoonpearl, canLift)),
823         'chicken-house': fromBool(and(westLightWorld, hasMoonpearl, canBomb)),
824         'desert-ledge': fromBool(and(hasMoonpearl, canEnterDPFront)),
825         'ether-tablet': fromBool(and(northDeathMountain, canTablet)),
826         'floating-island': fromBool(and(eastDeathMountain)),
827         'flooded-chest': fromBool(and(southLightWorld, hasMoonpearl)),
828         'flute-spot': fromBool(and(southLightWorld, hasMoonpearl, hasShovel)),
829         'graveyard-ledge': fromBool(and(westLightWorld, hasMoonpearl)),
830         'hammer-pegs': fromBool(and(hasHammer, or(canHeavyLift, and(westLightWorld, hasMirror)))),
831         hobo: fromBool(and(southLightWorld, hasMoonpearl, canSwim)),
832         'hookshot-cave-tl': fromBool(and(
833                 eastDarkDeathMountain,
834                 hasHookshot,
835                 or(canLift, and(canBomb, hasMirror, eastDeathMountain)),
836         )),
837         'hookshot-cave-tr': fromBool(and(
838                 eastDarkDeathMountain,
839                 hasHookshot,
840                 or(canLift, and(canBomb, hasMirror, eastDeathMountain)),
841         )),
842         'hookshot-cave-bl': fromBool(and(
843                 eastDarkDeathMountain,
844                 hasHookshot,
845                 or(canLift, and(canBomb, hasMirror, eastDeathMountain)),
846         )),
847         'hookshot-cave-br': fromBool(and(
848                 eastDarkDeathMountain,
849                 or(canBonk, hasHookshot),
850                 or(canLift, and(canBomb, hasMirror, eastDeathMountain)),
851         )),
852         'hype-cave-npc': fromBool(canBomb),
853         'hype-cave-top': fromBool(canBomb),
854         'hype-cave-right': fromBool(canBomb),
855         'hype-cave-left': fromBool(canBomb),
856         'hype-cave-bottom': fromBool(canBomb),
857         'ice-rod-cave': fromBool(and(southLightWorld, hasMoonpearl, canBomb)),
858         'kak-well-top': fromBool(and(westLightWorld, hasMoonpearl, canBomb)),
859         'kak-well-left': fromBool(and(westLightWorld, hasMoonpearl)),
860         'kak-well-mid': fromBool(and(westLightWorld, hasMoonpearl)),
861         'kak-well-right': fromBool(and(westLightWorld, hasMoonpearl)),
862         'kak-well-bottom': fromBool(and(westLightWorld, hasMoonpearl)),
863         'kings-tomb': fromBool(and(westLightWorld, hasMoonpearl, canBonk, canHeavyLift)),
864         'lake-hylia-island': fromBool(
865                 and(southLightWorld, hasMoonpearl, canSwim),
866         ),
867         library: fromBool(and(southLightWorld, hasMoonpearl, canBonk)),
868         lumberjack: fromBool(and(westLightWorld, hasMoonpearl, canBonk, agaDead)),
869         'lost-woods-hideout': fromBool(and(westLightWorld, hasMoonpearl)),
870         'magic-bat': fromBool(and(westLightWorld, hasMoonpearl, hasPowder, hasHammer)),
871         'maze-race': fromBool(and(southLightWorld, hasMoonpearl, or(canBomb, canBonk))),
872         'mimic-cave': fromBool(and(
873                 eastDeathMountain,
874                 hasMoonpearl,
875                 hasHammer,
876         )),
877         'mini-moldorm-far-left': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)),
878         'mini-moldorm-left': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)),
879         'mini-moldorm-right': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)),
880         'mini-moldorm-far-right': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)),
881         'mini-moldorm-npc': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)),
882         'mire-shed-left': fromBool(mireArea),
883         'mire-shed-right': fromBool(mireArea),
884         'mushroom-spot': fromBool(and(westLightWorld, hasMoonpearl)),
885         'old-man': fromBool(and(westDeathMountain, canDarkRoom)),
886         'paradox-lower-far-left': fromBool(and(hasMoonpearl, paradoxLower)),
887         'paradox-lower-left': fromBool(and(hasMoonpearl, paradoxLower)),
888         'paradox-lower-mid': fromBool(and(hasMoonpearl, paradoxLower)),
889         'paradox-lower-right': fromBool(and(hasMoonpearl, paradoxLower)),
890         'paradox-lower-far-right': fromBool(and(hasMoonpearl, paradoxLower)),
891         'paradox-upper-left': fromBool(and(eastDeathMountain, hasMoonpearl, canBomb)),
892         'paradox-upper-right': fromBool(and(eastDeathMountain, hasMoonpearl, canBomb)),
893         pedestal: fromBool(and(westLightWorld, hasPendants(3))),
894         'potion-shop': fromBool(and(eastLightWorld, hasMushroom, hasMoonpearl)),
895         'purple-chest': fromBool(and(or(canHeavyLift, hasMirror), westLightWorld, southLightWorld)),
896         pyramid: fromBool(eastDarkWorld),
897         'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), hasMirror)),
898         'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), hasMirror)),
899         'race-game': fromBool(and(westLightWorld, hasMoonpearl, or(canBomb, canBonk))),
900         saha: fromBool(and(eastLightWorld, hasGreenPendant)),
901         'saha-left': fromBool(and(eastLightWorld, hasMoonpearl, or(canBomb, canBonk))),
902         'saha-mid': fromBool(and(eastLightWorld, hasMoonpearl, or(canBomb, canBonk))),
903         'saha-right': fromBool(and(eastLightWorld, or(canBomb, canBonk))),
904         'secret-passage': fromBool(and(eastLightWorld, hasMoonpearl)),
905         'sick-kid': fromBool(and(westLightWorld, hasBottle(1))),
906         'spec-rock': fromBool(northDeathMountain),
907         'spec-rock-cave': fromBool(westDeathMountain),
908         'spike-cave': fromBool(and(
909                 westDarkDeathMountain,
910                 hasHammer,
911                 canLift,
912                 or(hasByrna, and(hasCape, hasMagicBars(2))),
913         )),
914         'spiral-cave': fromBool(and(
915                 eastDeathMountain,
916                 hasMoonpearl,
917         )),
918         'sunken-treasure': fromBool(and(southLightWorld, hasMoonpearl)),
919         'super-bunny-top': fromBool(eastDarkDeathMountain),
920         'super-bunny-bottom': fromBool(eastDarkDeathMountain),
921         tavern: fromBool(and(westLightWorld, hasMoonpearl)),
922         uncle: fromBool(and(eastLightWorld, hasMoonpearl)),
923         'waterfall-fairy-left': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)),
924         'waterfall-fairy-right': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)),
925         zora: fromBool(and(eastLightWorld, hasMoonpearl, or(canLift, canSwim))),
926         'zora-ledge': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)),
927         hc: fromBool(canEnterHC),
928         ct: fromBool(canEnterCT),
929         gt: fromBool(canEnterGT),
930         ep: fromBool(canEnterEP),
931         dp: fromBool(canEnterDPFront),
932         th: fromBool(canEnterTH),
933         pd: fromBool(canEnterPD),
934         sp: fromBool(canEnterSP),
935         sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)),
936         tt: fromBool(canEnterTT),
937         ip: fromBool(canEnterIP),
938         mm: fromBool(canEnterMM),
939         tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)),
940         ...Logic.dungeonInterior,
941 };
942
943 export default Logic;