11 export const BOOLEAN_STATES = [
44 export const INTEGER_STATES = [
53 export const INITIAL = {
57 export const BOSSES = [
70 export const CONFIG = {
78 export const DUNGEONS = [
91 offset: DUNGEON_IDS.HC,
92 mask: DUNGEON_MASKS.HC,
115 offset: DUNGEON_IDS.CT,
116 mask: DUNGEON_MASKS.CT,
133 offset: DUNGEON_IDS.GT,
134 mask: DUNGEON_MASKS.GT,
178 offset: DUNGEON_IDS.EP,
179 mask: DUNGEON_MASKS.EP,
197 bosses: ['lanmolas'],
202 offset: DUNGEON_IDS.DP,
203 mask: DUNGEON_MASKS.DP,
226 offset: DUNGEON_IDS.TH,
227 mask: DUNGEON_MASKS.TH,
249 offset: DUNGEON_IDS.PD,
250 mask: DUNGEON_MASKS.PD,
253 'pd-stalfos-basement',
261 'pd-harmless-hellway',
280 offset: DUNGEON_IDS.SP,
281 mask: DUNGEON_MASKS.SP,
307 offset: DUNGEON_IDS.SW,
308 mask: DUNGEON_MASKS.SW,
332 offset: DUNGEON_IDS.TT,
333 mask: DUNGEON_MASKS.TT,
353 bosses: ['kholdstare'],
357 offset: DUNGEON_IDS.IP,
358 mask: DUNGEON_MASKS.IP,
378 bosses: ['vitreous'],
382 offset: DUNGEON_IDS.MM,
383 mask: DUNGEON_MASKS.MM,
407 offset: DUNGEON_IDS.TR,
408 mask: DUNGEON_MASKS.TR,
417 'tr-laser-bridge-top',
418 'tr-laser-bridge-left',
419 'tr-laser-bridge-right',
420 'tr-laser-bridge-bottom',
426 export const OVERWORLD_LOCATIONS = [
468 id: 'floating-island',
483 id: 'lake-hylia-island',
553 id: 'sunken-treasure',
574 export const UNDERWORLD_LOCATIONS = [
581 id: 'blinds-hut-top',
586 id: 'blinds-hut-left',
591 id: 'blinds-hut-right',
596 id: 'blinds-hut-far-left',
601 id: 'blinds-hut-far-right',
665 id: 'dp-big-key-chest',
671 id: 'dp-compass-chest',
695 id: 'ep-big-key-chest',
707 id: 'ep-compass-chest',
724 id: 'graveyard-ledge',
873 id: 'gt-helma-right',
879 id: 'gt-pre-moldorm',
885 id: 'gt-post-moldorm',
914 id: 'hookshot-cave-br',
919 id: 'hookshot-cave-tr',
924 id: 'hookshot-cave-tl',
929 id: 'hookshot-cave-bl',
939 id: 'hype-cave-left',
944 id: 'hype-cave-right',
949 id: 'hype-cave-bottom',
964 id: 'ip-compass-chest',
970 id: 'ip-big-key-chest',
982 id: 'ip-spike-chest',
988 id: 'ip-freezor-chest',
1011 id: 'kak-well-left',
1021 id: 'kak-well-right',
1026 id: 'kak-well-bottom',
1041 id: 'lost-woods-hideout',
1056 id: 'mini-moldorm-far-left',
1061 id: 'mini-moldorm-left',
1066 id: 'mini-moldorm-right',
1071 id: 'mini-moldorm-far-right',
1076 id: 'mini-moldorm-npc',
1081 id: 'mm-bridge-chest',
1086 id: 'mm-spike-chest',
1091 id: 'mm-lobby-chest',
1096 id: 'mm-compass-chest',
1101 id: 'mm-big-key-chest',
1116 id: 'mire-shed-left',
1121 id: 'mire-shed-right',
1126 id: 'paradox-lower-far-left',
1131 id: 'paradox-lower-left',
1136 id: 'paradox-lower-right',
1141 id: 'paradox-lower-far-right',
1146 id: 'paradox-lower-mid',
1151 id: 'paradox-upper-left',
1156 id: 'paradox-upper-right',
1161 id: 'pd-shooter-room',
1166 id: 'pd-stalfos-basement',
1171 id: 'pd-big-key-chest',
1176 id: 'pd-arena-bridge',
1181 id: 'pd-arena-ledge',
1196 id: 'pd-compass-chest',
1201 id: 'pd-harmless-hellway',
1211 id: 'pd-maze-bottom',
1216 id: 'pd-basement-left',
1221 id: 'pd-basement-right',
1226 id: 'pyramid-fairy-left',
1231 id: 'pyramid-fairy-right',
1257 id: 'secret-passage',
1298 id: 'sp-compass-chest',
1304 id: 'sp-west-chest',
1310 id: 'sp-big-key-chest',
1316 id: 'sp-flooded-left',
1322 id: 'sp-flooded-right',
1334 id: 'spec-rock-cave',
1349 id: 'super-bunny-top',
1354 id: 'super-bunny-bottom',
1371 id: 'sw-compass-chest',
1377 id: 'sw-big-key-chest',
1383 id: 'sw-pot-prison',
1389 id: 'sw-pinball-room',
1395 id: 'sw-bridge-chest',
1406 id: 'th-basement-cage',
1412 id: 'th-big-key-chest',
1430 id: 'th-compass-chest',
1436 id: 'tr-roller-left',
1442 id: 'tr-roller-right',
1448 id: 'tr-compass-chest',
1460 id: 'tr-big-key-chest',
1472 id: 'tr-crysta-roller',
1478 id: 'tr-laser-bridge-top',
1484 id: 'tr-laser-bridge-left',
1490 id: 'tr-laser-bridge-right',
1496 id: 'tr-laser-bridge-bottom',
1508 id: 'tt-big-key-chest',
1514 id: 'tt-ambush-chest',
1520 id: 'tt-compass-chest',
1544 id: 'waterfall-fairy-left',
1549 id: 'waterfall-fairy-right',
1555 export const toggleBoolean = name => state => ({
1557 [name]: !state[name],
1560 export const increment = (name, max, skipZero) => state => {
1561 let newValue = ((state[name] || 0) + 1) % (max + 1);
1562 if (skipZero && !newValue) {
1571 export const decrement = (name, max, skipZero) => state => {
1572 let newValue = ((state[name] || 0) + max) % (max + 1);
1573 if (skipZero && !newValue) {
1582 export const highestActive = (state, names) => {
1583 for (let i = names.length; i >= 0; --i) {
1584 if (state[names[i]]) {
1591 export const hasDungeonBoss = (state, dungeon) => !!state[`${dungeon.id}-boss-defeated`];
1593 export const getDungeonBoss = (state, dungeon) =>
1594 state[`${dungeon.id}-boss`] || dungeon.boss || null;
1596 export const hasDungeonPrize = (state, dungeon) => !!state[`${dungeon.id}-prize-acquired`];
1598 export const getDungeonPrize = (state, dungeon) => state[`${dungeon.id}-prize`] || null;
1600 export const makeEmptyState = () => {
1602 BOOLEAN_STATES.forEach(p => {
1603 state[p] = INITIAL[p] || false;
1605 INTEGER_STATES.forEach(p => {
1606 state[p] = INITIAL[p] || 0;
1608 DUNGEONS.forEach(dungeon => {
1609 state[`${dungeon.id}-map`] = false;
1610 state[`${dungeon.id}-compass`] = false;
1611 state[`${dungeon.id}-small-key`] = 0;
1612 state[`${dungeon.id}-big-key`] = false;
1613 state[`${dungeon.id}-checks`] = 0;
1615 state[`${dungeon.id}-boss`] = dungeon.boss;
1616 state[`${dungeon.id}-boss-defeated`] = false;
1618 if (dungeon.prize) {
1619 state[`${dungeon.id}-prize`] = 'crystal';
1620 state[`${dungeon.id}-prize-acquired`] = false;
1623 OVERWORLD_LOCATIONS.forEach(location => {
1624 state[location.id] = false;
1626 UNDERWORLD_LOCATIONS.forEach(location => {
1627 state[location.id] = false;
1629 state['mm-medallion'] = null;
1630 state['tr-medallion'] = null;
1634 const collectInventory = (state, data, prizeMap) => {
1635 state.bow = !!(data[INV_ADDR.RANDO_BOW] & 0x80);
1636 state.silvers = (data[INV_ADDR.RANDO_BOW] & 0xC0) == 0xC0;
1637 state['bowless-silvers'] = (data[INV_ADDR.RANDO_BOW] & 0xC0) == 0x40;
1638 state['blue-boomerang'] = !!(data[INV_ADDR.RANDO_BOOM] & 0x40);
1639 state['red-boomerang'] = !!(data[INV_ADDR.RANDO_BOOM] & 0x80);
1640 state.hookshot = !!data[INV_ADDR.HOOK];
1641 state.bomb = data[INV_ADDR.BOMB];
1642 state.mushroom = !!(data[INV_ADDR.RANDO_POWDER] & 0x20);
1643 state.powder = !!(data[INV_ADDR.RANDO_POWDER] & 0x10);
1644 state['fire-rod'] = !!data[INV_ADDR.FROD];
1645 state['ice-rod'] = !!data[INV_ADDR.IROD];
1646 state.bombos = !!data[INV_ADDR.BOMBOS];
1647 state.ether = !!data[INV_ADDR.ETHER];
1648 state.quake = !!data[INV_ADDR.QUAKE];
1649 state.lamp = !!data[INV_ADDR.LAMP];
1650 state.hammer = !!data[INV_ADDR.HAMMER];
1651 state.shovel = !!(data[INV_ADDR.RANDO_FLUTE] & 0x04);
1652 state.flute = !!(data[INV_ADDR.RANDO_FLUTE] & 0x03);
1653 state.duck = !!(data[INV_ADDR.RANDO_FLUTE] & 0x01);
1654 state.bugnet = !!data[INV_ADDR.BUGNET];
1655 state.book = !!data[INV_ADDR.BOOK];
1657 if (data[INV_ADDR.BOTTLE_1]) {
1660 if (data[INV_ADDR.BOTTLE_2]) {
1663 if (data[INV_ADDR.BOTTLE_3]) {
1666 if (data[INV_ADDR.BOTTLE_4]) {
1669 state.somaria = !!data[INV_ADDR.SOMARIA];
1670 state.byrna = !!data[INV_ADDR.BYRNA];
1671 state.cape = !!data[INV_ADDR.CAPE];
1672 state.mirror = !!data[INV_ADDR.MIRROR];
1673 state.lift = data[INV_ADDR.GLOVE];
1674 state.boots = !!data[INV_ADDR.BOOTS];
1675 state.flippers = !!data[INV_ADDR.FLIPPERS];
1676 state.moonpearl = !!data[INV_ADDR.MOONPEARL];
1677 state.sword = data[INV_ADDR.SWORD];
1678 state.shield = data[INV_ADDR.SHIELD];
1679 state.mail = data[INV_ADDR.ARMOR] + 1;
1680 state['heart-piece'] = data[INV_ADDR.HEART_PIECE];
1681 state['half-magic'] = data[INV_ADDR.MAGIC_USE] > 0;
1682 state['quarter-magic'] = data[INV_ADDR.MAGIC_USE] > 1;
1683 const map = getShort(data, INV_ADDR.MAP);
1684 const compass = getShort(data, INV_ADDR.COMPASS);
1685 const bigKey = getShort(data, INV_ADDR.BIG_KEY);
1686 DUNGEONS.forEach(dungeon => {
1687 state[`${dungeon.id}-map`] = !!(map & dungeon.mask);
1688 state[`${dungeon.id}-compass`] = !!(compass & dungeon.mask);
1689 state[`${dungeon.id}-small-key`] = data[INV_ADDR.RANDO_KEY_START + dungeon.offset];
1690 state[`${dungeon.id}-big-key`] = !!(bigKey & dungeon.mask);
1691 if (dungeon.prize) {
1692 const isCrystal = prizeMap[dungeon.offset].isCrystal;
1693 const prizeFlags = data[isCrystal ? INV_ADDR.CRYSTALS : INV_ADDR.PENDANTS];
1694 state[`${dungeon.id}-prize-acquired`] = !!(prizeFlags & prizeMap[dungeon.offset].mask);
1699 const collectOverworld = (state, data) => {
1700 OVERWORLD_LOCATIONS.forEach(location => {
1701 state[location.id] = !!(data[location.address] & location.mask);
1705 const collectUnderworld = (state, data) => {
1706 UNDERWORLD_LOCATIONS.forEach(location => {
1707 state[location.id] = isChestOpen(data, location.room, location.chest);
1709 DUNGEONS.forEach(dungeon => {
1710 state[`${dungeon.id}-boss-defeated`] = isBossDefeated(data, dungeon.bossRoom);
1714 export const computeState = (data, prizeMap) => {
1716 collectInventory(state, data.slice(SRAM_ADDR.INV_START), prizeMap);
1717 collectOverworld(state, data);
1718 collectUnderworld(state, data.slice(SRAM_ADDR.ROOM_DATA_START));
1722 const getDungeonAmounts = (config, state) => {
1724 DUNGEONS.forEach(dungeon => {
1726 let total = dungeon.checks.length;
1727 dungeon.checks.forEach(check => {
1732 if (!config.wildMap && state[`${dungeon.id}-map`]) {
1736 if (!config.wildCompass && state[`${dungeon.id}-compass`]) {
1740 if (!config.wildSmall) {
1741 amount -= Math.min(state[`${dungeon.id}-small-key`], dungeon.sk);
1742 total -= dungeon.sk;
1744 if (!config.wildBig && !dungeon.dropBk && state[`${dungeon.id}-big-key`]) {
1748 amounts[dungeon.id] = Math.min(total, amount);
1753 export const mergeStates = (config, cur, inc) => {
1754 const next = { ...cur, ...inc };
1755 const amounts = getDungeonAmounts(config, inc);
1756 DUNGEONS.forEach(dungeon => {
1757 next[`${dungeon.id}-checks`] = amounts[dungeon.id];
1759 //console.log(next);