From: Daniel Karbach Date: Sat, 30 Mar 2024 18:54:06 +0000 (+0100) Subject: tentative inverted logic X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=07c9bbbb2c6ddde460f48245699f27eaf66b8ac3;p=alttp.git tentative inverted logic --- diff --git a/icons.sh b/icons.sh index c9fe293..b402292 100755 --- a/icons.sh +++ b/icons.sh @@ -1,6 +1,6 @@ #!/bin/bash -gm montage -geometry '32x32>' -background transparent -gravity center -tile 8x100 public/item/*.png public/items-v1.png +gm montage -geometry '32x32>' -background transparent -gravity center -tile 8x100 public/item/*.png public/items-v2.png echo 'const ITEM_MAP = [' for i in public/item/*.png diff --git a/package.json b/package.json index 78f5453..1fdb362 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "tabWidth": 4 } ], + "no-use-before-define": "error", "no-extra-parens": [ "warn", "all", diff --git a/public/item/bottle-good-bee.png b/public/item/bottle-good-bee.png new file mode 100644 index 0000000..25fd630 Binary files /dev/null and b/public/item/bottle-good-bee.png differ diff --git a/public/item/bunny-head.png b/public/item/bunny-head.png new file mode 100644 index 0000000..c97d8f5 Binary files /dev/null and b/public/item/bunny-head.png differ diff --git a/public/item/crystal-switch-blue.png b/public/item/crystal-switch-blue.png new file mode 100644 index 0000000..17c5c42 Binary files /dev/null and b/public/item/crystal-switch-blue.png differ diff --git a/public/item/crystal-switch-red.png b/public/item/crystal-switch-red.png new file mode 100644 index 0000000..318ef3b Binary files /dev/null and b/public/item/crystal-switch-red.png differ diff --git a/public/item/crystal-switch.png b/public/item/crystal-switch.png new file mode 100644 index 0000000..f2d647d Binary files /dev/null and b/public/item/crystal-switch.png differ diff --git a/public/item/ganon.png b/public/item/ganon.png new file mode 100644 index 0000000..89fe005 Binary files /dev/null and b/public/item/ganon.png differ diff --git a/public/item/gt.png b/public/item/gt.png new file mode 100644 index 0000000..cc95d58 Binary files /dev/null and b/public/item/gt.png differ diff --git a/public/item/link-head.png b/public/item/link-head.png new file mode 100644 index 0000000..5c1d31b Binary files /dev/null and b/public/item/link-head.png differ diff --git a/public/item/triforce-piece.png b/public/item/triforce-piece.png new file mode 100644 index 0000000..a7356fe Binary files /dev/null and b/public/item/triforce-piece.png differ diff --git a/public/item/triforce.png b/public/item/triforce.png new file mode 100644 index 0000000..1b26293 Binary files /dev/null and b/public/item/triforce.png differ diff --git a/public/items-v2.png b/public/items-v2.png new file mode 100644 index 0000000..d479d84 Binary files /dev/null and b/public/items-v2.png differ diff --git a/resources/js/components/common/ZeldaIcon.js b/resources/js/components/common/ZeldaIcon.js index 230b14e..3504dcf 100644 --- a/resources/js/components/common/ZeldaIcon.js +++ b/resources/js/components/common/ZeldaIcon.js @@ -19,15 +19,20 @@ const ITEM_MAP = [ 'book', 'boots', 'bottle-bee', + 'bottle-good-bee', 'bottle', 'bowless-silvers', 'bow', 'bugnet', + 'bunny-head', 'byrna', 'cape', 'chest', 'compass', 'crystal', + 'crystal-switch-blue', + 'crystal-switch', + 'crystal-switch-red', 'duck', 'ether', 'fairy', @@ -37,11 +42,13 @@ const ITEM_MAP = [ 'fire-shield', 'flippers', 'flute', + 'ganon', 'glove', 'gold-sword', 'green-mail', 'green-pendant', 'green-potion', + 'gt', 'half-magic', 'hammer', 'heart-0', @@ -56,6 +63,7 @@ const ITEM_MAP = [ 'kholdstare', 'lamp', 'lanmolas', + 'link-head', 'map', 'master-sword', 'mirror', @@ -84,20 +92,32 @@ const ITEM_MAP = [ 'sword-3', 'sword-4', 'tempered-sword', + 'triforce-piece', + 'triforce', 'trinexx', 'vitreous', ]; +const ITEM_MAP_WIDTH = 8; + +const ITEM_MAP_HEIGHT = Math.ceil(ITEM_MAP.length / ITEM_MAP_WIDTH); + +const ITEM_MAP_URL = '/items-v2.png'; + const isOnItemMap = name => ITEM_MAP.includes(name); -const getItemMapX = name => ITEM_MAP.indexOf(name) % 8; +const getItemMapX = name => ITEM_MAP.indexOf(name) % ITEM_MAP_WIDTH; -const getItemMapY = name => Math.floor(ITEM_MAP.indexOf(name) / 8); +const getItemMapY = name => Math.floor(ITEM_MAP.indexOf(name) / ITEM_MAP_WIDTH); const getItemMapStyle = name => { const x = getItemMapX(name); const y = getItemMapY(name); - return { backgroundPosition: `-${x * 100}% -${y * 100}%` }; + return { + backgroundImage: `url(${ITEM_MAP_URL})`, + backgroundPosition: `-${x * 100}% -${y * 100}%`, + backgroundSize: `${ITEM_MAP_WIDTH * 100}% ${ITEM_MAP_HEIGHT * 100}%`, + }; }; const getIconURL = name => { @@ -116,10 +136,6 @@ const getIconURL = name => { case 'dungeon-tr': case 'dungeon-tt': return `/dungeon/${name.substr(8)}.png`; - case 'crystal-switch': - case 'crystal-switch-blue': - case 'crystal-switch-red': - return `/icon/${name}.png`; default: return ''; } @@ -138,9 +154,9 @@ const ZeldaIcon = ({ name, svg, title }) => { const clipX = getItemMapX(strippedName); const clipY = getItemMapY(strippedName); return + +

{t('tracker.config.logic')}

+ + {t('tracker.config.worldState')} + + {['open', 'inverted'].map(n => + + )} + + + + {t('tracker.config.glitches')} + + {['none', 'owg', 'hmg', 'mg', 'nl'].map(n => + + )} + + + + {t('tracker.config.bossShuffle')} + + + + +

{t('tracker.config.goal')}

+ + {t('tracker.config.gtCrystals')} + + {['?', 0, 1, 2, 3, 4, 5, 6, 7].map(n => + + )} + + + + {t('tracker.config.ganonCrystals')} + + {['?', 0, 1, 2, 3, 4, 5, 6, 7].map(n => + + )} + + + + {t('tracker.config.goal')} + + {['ganon', 'fast', 'ad', 'ped', 'trinity', 'thunt', 'ghunt'].map(n => + + )} + + + +
+

{t('tracker.config.wildItems')}

{ const { t } = useTranslation(); @@ -687,7 +733,7 @@ const makeBackground = (src, level) => { }; const Overworld = () => { - const { dungeons, logic, setManualState, state } = useTracker(); + const { config, dungeons, logic, setManualState, state } = useTracker(); const mapDungeon = React.useCallback(dungeon => { const definition = dungeons.find(d => d.id === dungeon.id); @@ -769,11 +815,23 @@ const Overworld = () => { }; }, [logic, setManualState, state]); - const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]); - const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]); + const lwDungeons = React.useMemo(() => + (config.worldState === 'inverted' ? INVERTED_LW_DUNGEONS : LW_DUNGEONS) + .map(mapDungeon) + , [mapDungeon]); + const lwLocations = React.useMemo(() => + (config.worldState === 'inverted' ? INVERTED_LW_LOCATIONS : LW_LOCATIONS) + .map(mapLocation) + , [mapLocation]); - const dwDungeons = React.useMemo(() => DW_DUNGEONS.map(mapDungeon), [mapDungeon]); - const dwLocations = React.useMemo(() => DW_LOCATIONS.map(mapLocation), [mapLocation]); + const dwDungeons = React.useMemo(() => + (config.worldState === 'inverted' ? INVERTED_DW_DUNGEONS : DW_DUNGEONS) + .map(mapDungeon) + , [mapDungeon]); + const dwLocations = React.useMemo(() => + (config.worldState === 'inverted' ? INVERTED_DW_LOCATIONS : DW_LOCATIONS) + .map(mapLocation) + , [mapLocation]); return { const [showConfigDialog, setShowConfigDialog] = React.useState(false); const { config, saveConfig } = useTracker(); + const { t } = useTranslation(); - const controller = React.useMemo(() => ({ + const handleConfigChange = React.useCallback(({ target: { name, value } }) => { + saveConfig({ [name]: value }); + }, [saveConfig]); + + const bossController = React.useMemo(() => ({ + getActive: (state, icons) => config.bossShuffle ? icons[0] : null, + getDefault: (state, icons) => icons[0], + handlePrimary: () => { + saveConfig({ bossShuffle: !config.bossShuffle}); + }, + handleSecondary: () => null, + }), [config, saveConfig]); + + const wildController = React.useMemo(() => ({ getActive: (state, icons) => config[mapWild[icons[0]]] ? icons[0] : null, getDefault: (state, icons) => icons[0], handlePrimary: (state, setState, icons) => { @@ -28,6 +45,15 @@ const Toolbar = () => { handleSecondary: () => null, }), [config, saveConfig]); + const worldController = React.useMemo(() => ({ + getActive: (state, icons) => config.worldState === 'inverted' ? icons[1] : icons[0], + getDefault: (state, icons) => icons[0], + handlePrimary: () => { + saveConfig({ worldState: config.worldState == 'inverted' ? 'open' : 'inverted' }); + }, + handleSecondary: () => null, + }), [config, saveConfig]); + return
@@ -38,12 +64,78 @@ const Toolbar = () => { > - - - - + + + + + + +
+
+ + + + + + {['?', 0, 1, 2, 3, 4, 5, 6, 7].map(n => + + )} + + + + + + + + {['?', 0, 1, 2, 3, 4, 5, 6, 7].map(n => + + )} + + + + + + + + {['ganon', 'fast', 'ad', 'ped', 'trinity', 'thunt', 'ghunt'].map(n => + + )} + + +
+
+
- setShowConfigDialog(false)} show={showConfigDialog} /> ; diff --git a/resources/js/helpers/logic.js b/resources/js/helpers/logic.js index 1754941..5225613 100644 --- a/resources/js/helpers/logic.js +++ b/resources/js/helpers/logic.js @@ -13,8 +13,17 @@ const and = (...predicates) => (...args) => const or = (...predicates) => (...args) => predicates.reduce((acc, cur) => acc || cur(...args), false); +const when = (condition, then, otherwise) => (...args) => + condition(...args) ? then(...args) : otherwise(...args); + +const alwaysAvailable = () => true; + +const neverAvailable = () => false; + const fromBool = b => (...args) => b(...args) ? 'available' : 'unavailable'; +const isInverted = (config) => config.worldState === 'inverted'; + const agaDead = (config, dungeons, state) => hasDungeonBoss(state, dungeons.find(d => d.id === 'ct')); @@ -34,7 +43,7 @@ const countRedCrystals = (config, dungeons, state) => dungeons const hasRedCrystals = n => (...args) => countRedCrystals(...args) >= n; const hasGTCrystals = (config, dungeons, state) => - countCrystals(config, dungeons, state) >= getGTCrystals(state); + countCrystals(config, dungeons, state) >= getGTCrystals(config); const countPendants = (config, dungeons, state) => dungeons .filter(dungeon => @@ -128,14 +137,14 @@ const hasTRMedallion = (config, dungeons, state) => // Abilities -const canActivateFlute = () => true; - const canBomb = () => true; const canBonk = hasBoots; const canDarkRoom = hasLamp; +const canShootArrows = hasBow; + const canFlipSwitches = or( canBomb, hasBoom, @@ -149,14 +158,26 @@ const canFlipSwitches = or( hasIceRod, ); -const canFly = or(hasBird, and(hasFlute, canActivateFlute)); - const canGetGoodBee = and(hasBugnet, hasBottle(), or(canBonk, and(hasSword(), hasQuake))); const canLift = (config, dungeons, state) => state['lift'] >= 1; const canHeavyLift = (config, dungeons, state) => state['lift'] >= 2; +const canActivateFlute = when(isInverted, + and( + hasMoonpearl, + or( + // partial copy of east light world + agaDead, + and(hasMoonpearl, or(and(canLift, hasHammer), canHeavyLift)), + ), + ), + alwaysAvailable, +); + +const canFly = or(hasBird, and(hasFlute, canActivateFlute)); + const canKill = damage => damage && damage < 6 ? or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1), canBomb, hasByrna) : or(hasBow, hasFireRod, hasHammer, hasSomaria, hasSword(1)); @@ -167,8 +188,6 @@ const canMeltThings = or(hasFireRod, and(hasBombos, canMedallion)); const canPassCurtains = hasSword(); -const canShootArrows = hasBow; - const canSwim = (config, dungeons, state) => !!state['flippers']; const canTablet = and(hasBook, hasSword(2)); @@ -184,53 +203,100 @@ const westDeathMountain = or( and(canLift, canDarkRoom), ); -const eastDeathMountain = and( +const westDarkDeathMountain = when(isInverted, + or(canFly, and(canLift, canDarkRoom)), westDeathMountain, - or( - hasHookshot, - and(hasHammer, hasMirror), - ), ); -const northDeathMountain = and( - westDeathMountain, +const eastDeathMountain = when(isInverted, or( - hasMirror, - and(hasHammer, hasHookshot), + and(canHeavyLift, or( + // copy of eastDarkDeathMountain, to avoid circular reference + westDarkDeathMountain, + and(westDeathMountain, hasMoonpearl, hasHookshot, hasMirror), + )), + and(westDeathMountain, hasMoonpearl, hasHookshot), + ), + and( + westDeathMountain, + or( + hasHookshot, + and(hasHammer, hasMirror), + ), ), ); -const eastDarkDeathMountain = and( - eastDeathMountain, - canHeavyLift, +const northDeathMountain = when(isInverted, + and(eastDeathMountain, hasMoonpearl, hasHammer), + and( + westDeathMountain, + or( + hasMirror, + and(hasHammer, hasHookshot), + ), + ), ); -const westDarkDeathMountain = westDeathMountain; - -const eastDarkWorld = and( - hasMoonpearl, +const eastDarkDeathMountain = when(isInverted, or( - agaDead, + westDarkDeathMountain, + and(westDeathMountain, hasMoonpearl, hasHookshot, hasMirror), + ), + and( + eastDeathMountain, canHeavyLift, - and(canLift, hasHammer), ), ); -const westDarkWorld = and( - hasMoonpearl, +const eastLightWorld = when(isInverted, or( - and(canLift, hasHammer), - canHeavyLift, - and(eastDarkWorld, hasHookshot, or(canSwim, canLift, hasHammer)), + agaDead, + and(hasMoonpearl, or(and(canLift, hasHammer), canHeavyLift)), + and(canFly, canHeavyLift), ), + alwaysAvailable, ); -const southDarkWorld = or( - westDarkWorld, - and(eastDarkWorld, hasMoonpearl, hasHammer), +const westLightWorld = eastLightWorld; + +const southLightWorld = eastLightWorld; + +const eastDarkWorld = when(isInverted, + or(canFly, canSwim, hasHammer, and(hasMoonpearl, eastLightWorld)), + and( + hasMoonpearl, + or( + agaDead, + canHeavyLift, + and(canLift, hasHammer), + ), + ), ); -const mireArea = and(canFly, canHeavyLift); +const westDarkWorld = when(isInverted, + alwaysAvailable, + and( + hasMoonpearl, + or( + and(canLift, hasHammer), + canHeavyLift, + and(eastDarkWorld, hasHookshot, or(canSwim, canLift, hasHammer)), + ), + ), +); + +const southDarkWorld = when(isInverted, + alwaysAvailable, + or( + westDarkWorld, + and(eastDarkWorld, hasMoonpearl, hasHammer), + ), +); + +const mireArea = when(isInverted, + or(canFly, and(hasMirror, southLightWorld)), + and(canFly, canHeavyLift), +); // Bosses @@ -285,58 +351,112 @@ const canKillGTBoss = which => (config, dungeons, state) => { // Dungeons -const canEnterCT = or(hasCape, hasSword(2)); +const canEnterHC = when(isInverted, + and(hasMoonpearl, eastLightWorld), + alwaysAvailable, +); + +const canEnterCT = when(isInverted, + westDarkDeathMountain, + or(hasCape, hasSword(2)), +); -const canEnterGT = and(eastDarkDeathMountain, hasGTCrystals, hasMoonpearl); +const canEnterGT = when(isInverted, + and(eastLightWorld, hasGTCrystals, hasMoonpearl), + and(eastDarkDeathMountain, hasGTCrystals, hasMoonpearl), +); + +const canEnterEP = when(isInverted, + and(hasMoonpearl, eastLightWorld), + alwaysAvailable, +); -const canEnterDPFront = or(hasBook, and(mireArea, hasMirror)); -const canEnterDPBack = or(and(canEnterDPFront, canLift), and(mireArea, hasMirror)); +const canEnterDPFront = when(isInverted, + and(southLightWorld, hasBook), + or(hasBook, and(mireArea, hasMirror)), +); +const canEnterDPBack = when(isInverted, + and(canEnterDPFront, canLift), + or(and(canEnterDPFront, canLift), and(mireArea, hasMirror)), +); const canEnterTH = northDeathMountain; -const canEnterPD = and(eastDarkWorld, hasMoonpearl); +const canEnterPD = when(isInverted, + eastDarkWorld, + and(eastDarkWorld, hasMoonpearl), +); -const canEnterSP = and(southDarkWorld, hasMirror, hasMoonpearl, canSwim); +const canEnterSP = when(isInverted, + and(southLightWorld, hasMirror, hasMoonpearl, canSwim), + and(southDarkWorld, hasMirror, hasMoonpearl, canSwim), +); -const canEnterSWFront = and(westDarkWorld, hasMoonpearl); -const canEnterSWMid = and(westDarkWorld, hasMoonpearl); -const canEnterSWBack = and(westDarkWorld, hasMoonpearl, hasFireRod); +const canEnterSWFront = when(isInverted, + westDarkWorld, + and(westDarkWorld, hasMoonpearl), +); +const canEnterSWMid = canEnterSWFront; +const canEnterSWBack = and(canEnterSWMid, hasFireRod); -const canEnterTT = and(westDarkWorld, hasMoonpearl); +const canEnterTT = when(isInverted, + westDarkWorld, + and(westDarkWorld, hasMoonpearl), +); -const canEnterIP = and(canSwim, canHeavyLift, hasMoonpearl, canMeltThings); +const canEnterIP = when(isInverted, + and(canSwim, canMeltThings), + and(canSwim, canHeavyLift, hasMoonpearl, canMeltThings), +); const rightSideIP = or(hasHookshot, hasSmall('ip')); -const canEnterMM = and( - mireArea, - hasMoonpearl, - hasMMMedallion, - canMedallion, - or(canBonk, hasHookshot), - canKill(8), +const canEnterMM = when(isInverted, + and( + mireArea, + hasMMMedallion, + canMedallion, + or(canBonk, hasHookshot), + canKill(8), + ), + and( + mireArea, + hasMoonpearl, + hasMMMedallion, + canMedallion, + or(canBonk, hasHookshot), + canKill(8), + ), ); -const canEnterTRFront = and( - eastDeathMountain, - canHeavyLift, - hasHammer, - hasMoonpearl, - canMedallion, - hasTRMedallion, +const canEnterTRFront = when(isInverted, + and( + eastDarkDeathMountain, + canMedallion, + hasTRMedallion, + hasSomaria, + ), + and( + eastDeathMountain, + canHeavyLift, + hasHammer, + hasMoonpearl, + canMedallion, + hasTRMedallion, + hasSomaria, + ), +); +const canEnterTRWest = when(isInverted, + and(eastDeathMountain, hasMirror), + neverAvailable, ); -const canEnterTRWest = and(canEnterTRFront, canBomb, hasSmall('tr', 2)); const canEnterTREast = and(canEnterTRWest, or(hasHookshot, hasSomaria)); -const canEnterTRBack = and( - or(canEnterTRWest, canEnterTREast), - or(canBomb, canBonk), - hasBig('tr'), - hasSmall('tr', 3), - hasSomaria, - canDarkRoom, +const canEnterTRBack = when(isInverted, + and(eastDeathMountain, hasMirror), + neverAvailable, ); const laserBridge = or( and( - or(canEnterDPFront, canEnterTRWest, canEnterTREast), + or(canEnterTRFront, canEnterTRWest, canEnterTREast), canDarkRoom, hasSomaria, hasBig('tr'), @@ -358,111 +478,18 @@ const canRescueSmith = and(westDarkWorld, hasMoonpearl, canHeavyLift); const Logic = {}; -Logic.open = { - fallback: () => 'available', - aginah: fromBool(canBomb), - blacksmith: fromBool(canRescueSmith), - 'blinds-hut-top': fromBool(canBomb), - 'bombos-tablet': fromBool(and(southDarkWorld, hasMirror, canTablet)), - 'bonk-rocks': fromBool(canBonk), - brewery: fromBool(and(westDarkWorld, canBomb, hasMoonpearl)), - 'bumper-cave': fromBool(and(westDarkWorld, hasMoonpearl, canLift, hasCape)), - 'c-house': fromBool(and(westDarkWorld, hasMoonpearl)), - catfish: fromBool(and(eastDarkWorld, hasMoonpearl)), - 'cave-45': fromBool(and(southDarkWorld, hasMirror)), - checkerboard: fromBool(and(mireArea, hasMirror)), - 'chest-game': fromBool(and(westDarkWorld, hasMoonpearl)), - 'chicken-house': fromBool(canBomb), - 'desert-ledge': fromBool(canEnterDPFront), - 'digging-game': fromBool(and(southDarkWorld, hasMoonpearl)), - 'ether-tablet': fromBool(and(northDeathMountain, canTablet)), - 'floating-island': fromBool( - and(eastDarkDeathMountain, hasMoonpearl, canLift, canBomb, hasMirror), - ), - 'flute-spot': fromBool(hasShovel), - 'graveyard-ledge': fromBool(and(westDarkWorld, hasMoonpearl, hasMirror)), - 'hammer-pegs': fromBool(and(westDarkWorld, hasHammer, hasMoonpearl, canHeavyLift)), - hobo: fromBool(canSwim), - 'hookshot-cave-tl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), - 'hookshot-cave-tr': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), - 'hookshot-cave-bl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), - 'hookshot-cave-br': fromBool( - and(eastDarkDeathMountain, hasMoonpearl, canLift, or(hasHookshot, canBonk)), - ), - 'hype-cave-npc': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), - 'hype-cave-top': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), - 'hype-cave-right': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), - 'hype-cave-left': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), - 'hype-cave-bottom': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), - 'ice-rod-cave': fromBool(canBomb), - 'kak-well-top': fromBool(canBomb), - 'kings-tomb': fromBool(and(canBonk, or(canHeavyLift, and(westDarkWorld, hasMirror)))), - 'lake-hylia-island': fromBool( - and(canSwim, hasMirror, hasMoonpearl, or(eastDarkWorld, southDarkWorld)), - ), - library: fromBool(canBonk), - lumberjack: fromBool(and(canBonk, agaDead)), - 'magic-bat': fromBool(and(hasPowder, - or(hasHammer, and(westDarkWorld, hasMoonpearl, canHeavyLift, hasMirror)), - )), - 'mimic-cave': fromBool(and(canEnterTREast, hasMirror, hasHammer)), - 'mini-moldorm-left': fromBool(canBomb), - 'mini-moldorm-right': fromBool(canBomb), - 'mini-moldorm-far-left': fromBool(canBomb), - 'mini-moldorm-far-right': fromBool(canBomb), - 'mini-moldorm-npc': fromBool(canBomb), - 'mire-shed-left': fromBool(and(mireArea, hasMoonpearl)), - 'mire-shed-right': fromBool(and(mireArea, hasMoonpearl)), - 'old-man': fromBool(and(westDeathMountain, canDarkRoom)), - 'paradox-lower-far-left': fromBool(paradoxLower), - 'paradox-lower-left': fromBool(paradoxLower), - 'paradox-lower-right': fromBool(paradoxLower), - 'paradox-lower-far-right': fromBool(paradoxLower), - 'paradox-lower-mid': fromBool(paradoxLower), - 'paradox-upper-left': fromBool(and(eastDeathMountain, canBomb)), - 'paradox-upper-right': fromBool(and(eastDeathMountain, canBomb)), - pedestal: fromBool(hasPendants(3)), - 'potion-shop': fromBool(hasMushroom), - 'purple-chest': fromBool(and(canRescueSmith, hasMoonpearl, canHeavyLift)), - pyramid: fromBool(eastDarkWorld), - 'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)), - 'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)), - 'race-game': fromBool(or(canBomb, canBonk)), - saha: fromBool(hasGreenPendant), - 'saha-left': fromBool(or(canBomb, canBonk)), - 'saha-mid': fromBool(or(canBomb, canBonk)), - 'saha-right': fromBool(or(canBomb, canBonk)), - 'sick-kid': fromBool(hasBottle(1)), - 'spec-rock': fromBool(and(westDeathMountain, hasMirror)), - 'spec-rock-cave': fromBool(westDeathMountain), - 'spike-cave': fromBool(and( - westDarkDeathMountain, - hasMoonpearl, - hasHammer, - canLift, - or(hasByrna, and(hasCape, hasMagicBars(2))), - )), - 'spiral-cave': fromBool(eastDeathMountain), - stumpy: fromBool(and(southDarkWorld, hasMoonpearl)), - 'super-bunny-top': fromBool(and(eastDarkDeathMountain, hasMoonpearl)), - 'super-bunny-bottom': fromBool(and(eastDarkDeathMountain, hasMoonpearl)), - 'waterfall-fairy-left': fromBool(canSwim), - 'waterfall-fairy-right': fromBool(canSwim), - zora: fromBool(or(canLift, canSwim)), - 'zora-ledge': fromBool(canSwim), +Logic.dungeonInterior = { 'hc-boom': fromBool(and(hasSmall('hc'), canKill())), 'hc-cell': fromBool(and(hasSmall('hc'), canKill())), 'dark-cross': fromBool(canTorchDarkRoom), 'sewers-left': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))), 'sewers-mid': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))), 'sewers-right': fromBool(or(canLift, and(canTorchDarkRoom, hasSmall('hc'), canKill()))), - ct: fromBool(canEnterCT), 'ct-1': fromBool(canKill()), 'ct-2': fromBool(and(canKill(), hasSmall('ct'), canDarkRoom)), 'ct-boss-killable': fromBool(and( canKill(), hasSmall('ct', 2), canDarkRoom, canPassCurtains, canKillBoss('ct'), )), - gt: fromBool(canEnterGT), 'gt-tile-room': fromBool(hasSomaria), 'gt-compass-tl': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))), 'gt-compass-tr': fromBool(and(hasSomaria, hasFireRod, hasSmall('gt', 4))), @@ -548,7 +575,6 @@ Logic.open = { 'ep-boss-defeated': fromBool(and( canShootArrows, canTorchDarkRoom, hasBig('ep'), canKillBoss('ep'), )), - dp: fromBool(or(canEnterDPFront, canEnterDPBack)), 'dp-big-chest': fromBool(and(canEnterDPFront, hasBig('dp'))), 'dp-big-key-chest': fromBool(and(canEnterDPFront, hasSmall('dp'), canKill())), 'dp-compass-chest': fromBool(and(canEnterDPFront, hasSmall('dp'))), @@ -561,7 +587,6 @@ Logic.open = { hasSmall('dp'), canKillBoss('dp'), )), - th: fromBool(canEnterTH), 'th-basement-cage': fromBool(canFlipSwitches), 'th-map-chest': fromBool(canFlipSwitches), 'th-big-key-chest': fromBool(and(canFlipSwitches, hasSmall('th'), canTorch)), @@ -572,7 +597,6 @@ Logic.open = { hasBig('th'), canKillBoss('th'), )), - pd: fromBool(canEnterPD), 'pd-stalfos-basement': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))), 'pd-big-key-chest': fromBool(hasSmall('pd', 6)), 'pd-arena-bridge': fromBool(or(hasSmall('pd', 1), and(canShootArrows, hasHammer))), @@ -588,7 +612,6 @@ Logic.open = { 'pd-boss-defeated': fromBool(and( canDarkRoom, hasBig('pd'), hasSmall('pd', 6), canShootArrows, hasHammer, canKillBoss('pd'), )), - sp: fromBool(canEnterSP), 'sp-map-chest': fromBool(and(hasSmall('sp'), canBomb)), 'sp-big-chest': fromBool(and(hasSmall('sp'), hasHammer, hasBig('sp'))), 'sp-compass-chest': fromBool(and(hasSmall('sp'), hasHammer)), @@ -598,18 +621,15 @@ Logic.open = { 'sp-flooded-right': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)), 'sp-waterfall': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot)), 'sp-boss-defeated': fromBool(and(hasSmall('sp'), hasHammer, hasHookshot, canKillBoss('sp'))), - sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)), 'sw-big-chest': fromBool(and(canEnterSWFront, hasBig('sw'))), 'sw-bridge-chest': fromBool(canEnterSWBack), 'sw-boss-defeated': fromBool(and( canEnterSWBack, canPassCurtains, hasFireRod, hasSmall('sw', 3), canKillBoss('sw'), )), - tt: fromBool(canEnterTT), 'tt-attic': fromBool(and(hasBig('tt'), hasSmall('tt'), canBomb)), 'tt-cell': fromBool(hasBig('tt')), 'tt-big-chest': fromBool(and(hasBig('tt'), hasSmall('tt'), hasHammer)), 'tt-boss-defeated': fromBool(and(hasBig('tt'), hasSmall('tt'), canKillBoss('tt'))), - ip: fromBool(canEnterIP), 'ip-big-key-chest': fromBool(and(rightSideIP, hasHammer, canLift)), 'ip-map-chest': fromBool(and(rightSideIP, hasHammer, canLift)), 'ip-spike-chest': fromBool(rightSideIP), @@ -622,14 +642,12 @@ Logic.open = { or(hasSmall('ip', 2), and(hasSomaria, hasSmall('ip'))), canKillBoss('ip'), )), - mm: fromBool(canEnterMM), 'mm-lobby-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))), 'mm-compass-chest': fromBool(and(canTorch, hasSmall('mm', 3))), 'mm-big-key-chest': fromBool(and(canTorch, hasSmall('mm', 3))), 'mm-big-chest': fromBool(hasBig('mm')), 'mm-map-chest': fromBool(or(hasBig('mm'), hasSmall('mm'))), 'mm-boss-defeated': fromBool(and(hasBig('mm'), canDarkRoom, hasSomaria, canKillBoss('mm'))), - tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)), 'tr-roller-left': fromBool(and(hasFireRod, hasSomaria, or( canEnterTRFront, and(or(canEnterTRWest, canEnterTREast), hasSmall('tr', 4)), @@ -679,4 +697,250 @@ Logic.open = { )), }; +Logic.open = { + fallback: fromBool(alwaysAvailable), + aginah: fromBool(canBomb), + blacksmith: fromBool(canRescueSmith), + 'blinds-hut-top': fromBool(canBomb), + 'bombos-tablet': fromBool(and(southDarkWorld, hasMirror, canTablet)), + 'bonk-rocks': fromBool(canBonk), + brewery: fromBool(and(westDarkWorld, canBomb, hasMoonpearl)), + 'bumper-cave': fromBool(and(westDarkWorld, hasMoonpearl, canLift, hasCape)), + 'c-house': fromBool(and(westDarkWorld, hasMoonpearl)), + catfish: fromBool(and(eastDarkWorld, hasMoonpearl)), + 'cave-45': fromBool(and(southDarkWorld, hasMirror)), + checkerboard: fromBool(and(mireArea, hasMirror)), + 'chest-game': fromBool(and(westDarkWorld, hasMoonpearl)), + 'chicken-house': fromBool(canBomb), + 'desert-ledge': fromBool(canEnterDPFront), + 'digging-game': fromBool(and(southDarkWorld, hasMoonpearl)), + 'ether-tablet': fromBool(and(northDeathMountain, canTablet)), + 'floating-island': fromBool( + and(eastDarkDeathMountain, hasMoonpearl, canLift, canBomb, hasMirror), + ), + 'flute-spot': fromBool(hasShovel), + 'graveyard-ledge': fromBool(and(westDarkWorld, hasMoonpearl, hasMirror)), + 'hammer-pegs': fromBool(and(westDarkWorld, hasHammer, hasMoonpearl, canHeavyLift)), + hobo: fromBool(canSwim), + 'hookshot-cave-tl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), + 'hookshot-cave-tr': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), + 'hookshot-cave-bl': fromBool(and(eastDarkDeathMountain, hasMoonpearl, canLift, hasHookshot)), + 'hookshot-cave-br': fromBool( + and(eastDarkDeathMountain, hasMoonpearl, canLift, or(hasHookshot, canBonk)), + ), + 'hype-cave-npc': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), + 'hype-cave-top': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), + 'hype-cave-right': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), + 'hype-cave-left': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), + 'hype-cave-bottom': fromBool(and(southDarkWorld, hasMoonpearl, canBomb)), + 'ice-rod-cave': fromBool(canBomb), + 'kak-well-top': fromBool(canBomb), + 'kings-tomb': fromBool(and(canBonk, or(canHeavyLift, and(westDarkWorld, hasMirror)))), + 'lake-hylia-island': fromBool( + and(canSwim, hasMirror, hasMoonpearl, or(eastDarkWorld, southDarkWorld)), + ), + library: fromBool(canBonk), + lumberjack: fromBool(and(canBonk, agaDead)), + 'magic-bat': fromBool(and(hasPowder, + or(hasHammer, and(westDarkWorld, hasMoonpearl, canHeavyLift, hasMirror)), + )), + 'mimic-cave': fromBool(and(canEnterTRFront, canBomb, hasSmall('tr', 2), hasMirror, hasHammer)), + 'mini-moldorm-left': fromBool(canBomb), + 'mini-moldorm-right': fromBool(canBomb), + 'mini-moldorm-far-left': fromBool(canBomb), + 'mini-moldorm-far-right': fromBool(canBomb), + 'mini-moldorm-npc': fromBool(canBomb), + 'mire-shed-left': fromBool(and(mireArea, hasMoonpearl)), + 'mire-shed-right': fromBool(and(mireArea, hasMoonpearl)), + 'old-man': fromBool(and(westDeathMountain, canDarkRoom)), + 'paradox-lower-far-left': fromBool(paradoxLower), + 'paradox-lower-left': fromBool(paradoxLower), + 'paradox-lower-right': fromBool(paradoxLower), + 'paradox-lower-far-right': fromBool(paradoxLower), + 'paradox-lower-mid': fromBool(paradoxLower), + 'paradox-upper-left': fromBool(and(eastDeathMountain, canBomb)), + 'paradox-upper-right': fromBool(and(eastDeathMountain, canBomb)), + pedestal: fromBool(hasPendants(3)), + 'potion-shop': fromBool(hasMushroom), + 'purple-chest': fromBool(and(canRescueSmith, hasMoonpearl, canHeavyLift)), + pyramid: fromBool(eastDarkWorld), + 'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)), + 'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), southDarkWorld, canBridgeRedBomb)), + 'race-game': fromBool(or(canBomb, canBonk)), + saha: fromBool(hasGreenPendant), + 'saha-left': fromBool(or(canBomb, canBonk)), + 'saha-mid': fromBool(or(canBomb, canBonk)), + 'saha-right': fromBool(or(canBomb, canBonk)), + 'sick-kid': fromBool(hasBottle(1)), + 'spec-rock': fromBool(and(westDeathMountain, hasMirror)), + 'spec-rock-cave': fromBool(westDeathMountain), + 'spike-cave': fromBool(and( + westDarkDeathMountain, + hasMoonpearl, + hasHammer, + canLift, + or(hasByrna, and(hasCape, hasMagicBars(2))), + )), + 'spiral-cave': fromBool(eastDeathMountain), + stumpy: fromBool(and(southDarkWorld, hasMoonpearl)), + 'super-bunny-top': fromBool(and(eastDarkDeathMountain, hasMoonpearl)), + 'super-bunny-bottom': fromBool(and(eastDarkDeathMountain, hasMoonpearl)), + 'waterfall-fairy-left': fromBool(canSwim), + 'waterfall-fairy-right': fromBool(canSwim), + zora: fromBool(or(canLift, canSwim)), + 'zora-ledge': fromBool(canSwim), + ct: fromBool(canEnterCT), + gt: fromBool(canEnterGT), + dp: fromBool(or(canEnterDPFront, canEnterDPBack)), + th: fromBool(canEnterTH), + pd: fromBool(canEnterPD), + sp: fromBool(canEnterSP), + sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)), + tt: fromBool(canEnterTT), + ip: fromBool(canEnterIP), + mm: fromBool(canEnterMM), + tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)), + ...Logic.dungeonInterior, +}; + +Logic.inverted = { + fallback: fromBool(alwaysAvailable), + aginah: fromBool(and(southLightWorld, hasMoonpearl, canBomb)), + blacksmith: fromBool(and(or(canHeavyLift, hasMirror), westLightWorld)), + 'blinds-hut-top': fromBool(and(westLightWorld, hasMoonpearl, canBomb)), + 'blinds-hut-far-left': fromBool(and(westLightWorld, hasMoonpearl)), + 'blinds-hut-left': fromBool(and(westLightWorld, hasMoonpearl)), + 'blinds-hut-right': fromBool(and(westLightWorld, hasMoonpearl)), + 'blinds-hut-far-right': fromBool(and(westLightWorld, hasMoonpearl)), + 'bombos-tablet': fromBool(and(southLightWorld, canTablet)), + 'bonk-rocks': fromBool(and(westLightWorld, hasMoonpearl, canBonk)), + 'bottle-vendor': fromBool(westLightWorld), + brewery: fromBool(canBomb), + 'bumper-cave': fromBool(and(canLift, hasCape, hasMoonpearl, hasMirror, westLightWorld)), + catfish: fromBool(or( + and(eastDarkWorld, canLift), + and(hasMirror, southLightWorld, hasMoonpearl, canSwim), + )), + 'cave-45': fromBool(and(southLightWorld, hasMoonpearl)), + checkerboard: fromBool(and(southLightWorld, hasMoonpearl, canLift)), + 'chicken-house': fromBool(and(westLightWorld, hasMoonpearl, canBomb)), + 'desert-ledge': fromBool(and(hasMoonpearl, canEnterDPFront)), + 'ether-tablet': fromBool(and(northDeathMountain, canTablet)), + 'floating-island': fromBool(and(eastDeathMountain)), + 'flooded-chest': fromBool(and(southLightWorld, hasMoonpearl)), + 'flute-spot': fromBool(and(southLightWorld, hasMoonpearl, hasShovel)), + 'graveyard-ledge': fromBool(and(westLightWorld, hasMoonpearl)), + 'hammer-pegs': fromBool(and(hasHammer, or(canHeavyLift, and(westLightWorld, hasMirror)))), + hobo: fromBool(and(southLightWorld, hasMoonpearl, canSwim)), + 'hookshot-cave-tl': fromBool(and( + eastDarkDeathMountain, + hasHookshot, + or(canLift, and(canBomb, hasMirror, eastDeathMountain)), + )), + 'hookshot-cave-tr': fromBool(and( + eastDarkDeathMountain, + hasHookshot, + or(canLift, and(canBomb, hasMirror, eastDeathMountain)), + )), + 'hookshot-cave-bl': fromBool(and( + eastDarkDeathMountain, + hasHookshot, + or(canLift, and(canBomb, hasMirror, eastDeathMountain)), + )), + 'hookshot-cave-br': fromBool(and( + eastDarkDeathMountain, + or(canBonk, hasHookshot), + or(canLift, and(canBomb, hasMirror, eastDeathMountain)), + )), + 'hype-cave-npc': fromBool(canBomb), + 'hype-cave-top': fromBool(canBomb), + 'hype-cave-right': fromBool(canBomb), + 'hype-cave-left': fromBool(canBomb), + 'hype-cave-bottom': fromBool(canBomb), + 'ice-rod-cave': fromBool(and(southLightWorld, hasMoonpearl, canBomb)), + 'kak-well-top': fromBool(and(westLightWorld, hasMoonpearl, canBomb)), + 'kak-well-left': fromBool(and(westLightWorld, hasMoonpearl)), + 'kak-well-mid': fromBool(and(westLightWorld, hasMoonpearl)), + 'kak-well-right': fromBool(and(westLightWorld, hasMoonpearl)), + 'kak-well-bottom': fromBool(and(westLightWorld, hasMoonpearl)), + 'kings-tomb': fromBool(and(westLightWorld, hasMoonpearl, canBonk, canHeavyLift)), + 'lake-hylia-island': fromBool( + and(southLightWorld, hasMoonpearl, canSwim), + ), + library: fromBool(and(southLightWorld, hasMoonpearl, canBonk)), + lumberjack: fromBool(and(westLightWorld, hasMoonpearl, canBonk, agaDead)), + 'lost-woods-hideout': fromBool(and(westLightWorld, hasMoonpearl)), + 'magic-bat': fromBool(and(westLightWorld, hasMoonpearl, hasPowder, hasHammer)), + 'maze-race': fromBool(and(southLightWorld, hasMoonpearl, or(canBomb, canBonk))), + 'mimic-cave': fromBool(and( + eastDeathMountain, + hasMoonpearl, + hasHammer, + )), + 'mini-moldorm-far-left': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)), + 'mini-moldorm-left': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)), + 'mini-moldorm-right': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)), + 'mini-moldorm-far-right': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)), + 'mini-moldorm-npc': fromBool(and(southLightWorld, hasMoonpearl, canBomb, canKill)), + 'mire-shed-left': fromBool(mireArea), + 'mire-shed-right': fromBool(mireArea), + 'mushroom-spot': fromBool(and(westLightWorld, hasMoonpearl)), + 'old-man': fromBool(and(westDeathMountain, canDarkRoom)), + 'paradox-lower-far-left': fromBool(and(hasMoonpearl, paradoxLower)), + 'paradox-lower-left': fromBool(and(hasMoonpearl, paradoxLower)), + 'paradox-lower-mid': fromBool(and(hasMoonpearl, paradoxLower)), + 'paradox-lower-right': fromBool(and(hasMoonpearl, paradoxLower)), + 'paradox-lower-far-right': fromBool(and(hasMoonpearl, paradoxLower)), + 'paradox-upper-left': fromBool(and(eastDeathMountain, hasMoonpearl, canBomb)), + 'paradox-upper-right': fromBool(and(eastDeathMountain, hasMoonpearl, canBomb)), + pedestal: fromBool(and(westLightWorld, hasPendants(3))), + 'potion-shop': fromBool(and(eastLightWorld, hasMushroom, hasMoonpearl)), + 'purple-chest': fromBool(and(or(canHeavyLift, hasMirror), westLightWorld, southLightWorld)), + pyramid: fromBool(eastDarkWorld), + 'pyramid-fairy-left': fromBool(and(hasRedCrystals(2), hasMirror)), + 'pyramid-fairy-right': fromBool(and(hasRedCrystals(2), hasMirror)), + 'race-game': fromBool(and(westLightWorld, hasMoonpearl, or(canBomb, canBonk))), + saha: fromBool(and(eastLightWorld, hasGreenPendant)), + 'saha-left': fromBool(and(eastLightWorld, hasMoonpearl, or(canBomb, canBonk))), + 'saha-mid': fromBool(and(eastLightWorld, hasMoonpearl, or(canBomb, canBonk))), + 'saha-right': fromBool(and(eastLightWorld, or(canBomb, canBonk))), + 'secret-passage': fromBool(and(eastLightWorld, hasMoonpearl)), + 'sick-kid': fromBool(and(westLightWorld, hasBottle(1))), + 'spec-rock': fromBool(northDeathMountain), + 'spec-rock-cave': fromBool(westDeathMountain), + 'spike-cave': fromBool(and( + westDarkDeathMountain, + hasHammer, + canLift, + or(hasByrna, and(hasCape, hasMagicBars(2))), + )), + 'spiral-cave': fromBool(and( + eastDeathMountain, + hasMoonpearl, + )), + 'sunken-treasure': fromBool(and(southLightWorld, hasMoonpearl)), + 'super-bunny-top': fromBool(eastDarkDeathMountain), + 'super-bunny-bottom': fromBool(eastDarkDeathMountain), + tavern: fromBool(and(westLightWorld, hasMoonpearl)), + uncle: fromBool(and(eastLightWorld, hasMoonpearl)), + 'waterfall-fairy-left': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)), + 'waterfall-fairy-right': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)), + zora: fromBool(and(eastLightWorld, hasMoonpearl, or(canLift, canSwim))), + 'zora-ledge': fromBool(and(eastLightWorld, hasMoonpearl, canSwim)), + hc: fromBool(canEnterHC), + ct: fromBool(canEnterCT), + gt: fromBool(canEnterGT), + ep: fromBool(canEnterEP), + dp: fromBool(canEnterDPFront), + th: fromBool(canEnterTH), + pd: fromBool(canEnterPD), + sp: fromBool(canEnterSP), + sw: fromBool(or(canEnterSWFront, canEnterSWMid, canEnterSWBack)), + tt: fromBool(canEnterTT), + ip: fromBool(canEnterIP), + mm: fromBool(canEnterMM), + tr: fromBool(or(canEnterTRFront, canEnterTRWest, canEnterTREast, canEnterTRBack)), + ...Logic.dungeonInterior, +}; + export default Logic; diff --git a/resources/js/helpers/tracker.js b/resources/js/helpers/tracker.js index 8b210d2..2089433 100644 --- a/resources/js/helpers/tracker.js +++ b/resources/js/helpers/tracker.js @@ -1573,11 +1573,18 @@ export const UNDERWORLD_LOCATIONS = [ }, ]; +export const getConfigValue = (config, name, fallback) => + Object.prototype.hasOwnProperty.call(config, name) ? config[name] : fallback; + export const applyLogic = (config, dungeons, state) => { const logic = Logic[config.worldState]; const map = {}; for (const name in logic) { - map[name] = logic[name](config, dungeons, state); + try { + map[name] = logic[name](config, dungeons, state); + } catch (e) { + console.error('error evaluating', name, e); + } } return map; }; @@ -1675,9 +1682,9 @@ export const aggregateLocationStatus = (names, logic, state) => { export const countRemainingLocations = (state, locations) => locations.reduce((acc, cur) => state[cur] ? acc : acc + 1, 0); -export const getGanonCrystals = (state) => state['ganon-crystals']; +export const getGanonCrystals = (config) => getConfigValue(config, 'ganon-crystals', 7); -export const getGTCrystals = (state) => state['gt-crystals']; +export const getGTCrystals = (config) => getConfigValue(config, 'gt-crystals', 7); export const getGTBoss = (state, which) => state[`gt-${which}-boss`]; @@ -1685,7 +1692,9 @@ export const hasDungeonBoss = (state, dungeon) => !dungeon.boss || !!state[`${dungeon.id}-boss-defeated`]; export const getDungeonBoss = (state, dungeon) => - state[`${dungeon.id}-boss`] || dungeon.boss || null; + dungeon.bosses.length > 1 + ? state[`${dungeon.id}-boss`] || dungeon.boss || null + : dungeon.bosses[0]; export const hasDungeonPrize = (state, dungeon) => !dungeon.prize || !!state[`${dungeon.id}-prize-acquired`]; @@ -1776,8 +1785,6 @@ export const makeEmptyState = () => { }); state['mm-medallion'] = null; state['tr-medallion'] = null; - state['gt-crystals'] = 7; - state['ganon-crystals'] = 7; return state; }; @@ -1917,6 +1924,9 @@ export const mergeStates = (autoState, manualState) => { if (manualState[`${dungeon.id}-map`]) { next[`${dungeon.id}-map`] = true; } + if (manualState[`${dungeon.id}-boss`]) { + next[`${dungeon.id}-boss`] = manualState[`${dungeon.id}-boss`]; + } if (manualState[`${dungeon.id}-boss-defeated`]) { next[`${dungeon.id}-boss-defeated`] = true; } diff --git a/resources/js/hooks/tracker.js b/resources/js/hooks/tracker.js index 4004152..ec1e8ce 100644 --- a/resources/js/hooks/tracker.js +++ b/resources/js/hooks/tracker.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { + BOSSES, CONFIG, DUNGEONS, applyLogic, @@ -51,8 +52,8 @@ export const TrackerProvider = ({ children }) => { if (config.wildBig && dungeon.bk && !dungeon.dropBk) { ++newDungeon.items; } - if (!config.bossShuffle && dungeon.boss) { - newDungeon.bosses = [dungeon.boss]; + if (dungeon.boss) { + newDungeon.bosses = config.bossShuffle ? BOSSES : [dungeon.boss]; } return newDungeon; }); diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 87b6198..9647da5 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -502,6 +502,28 @@ export default { }, tracker: { config: { + bossShuffle: 'Boss Shuffle', + ganonCrystals: 'Ganon Crystals', + glitches: 'Glitches', + glitchRules: { + hmg: 'HMG', + mg: 'Major', + nl: 'No Logic', + none: 'Keine', + owg: 'OWG', + }, + goal: 'Ziel', + goals: { + ad: 'All Dungeons', + fast: 'Fast Ganon', + ganon: 'Ganon', + ghunt: 'Ganon Hunt', + ped: 'Pedestal', + thunt: 'Triforce Hunt', + trinity: 'Trinity', + }, + gtCrystals: 'GT Crystals', + logic: 'Logik', showBig: 'Big Keys', showCompass: 'Kompanden', showItemOptions: { @@ -518,6 +540,11 @@ export default { wildItems: 'Wild Dungeon Items', wildMap: 'Maps', wildSmall: 'Small Keys', + worldState: 'World State', + worldStates: { + inverted: 'Inverted', + open: 'Open', + }, }, location: { aginah: 'Aginah', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index 9ca7a6b..7554f6e 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -502,6 +502,28 @@ export default { }, tracker: { config: { + bossShuffle: 'Boss Shuffle', + ganonCrystals: 'Ganon Crystals', + glitches: 'Glitches', + glitchRules: { + hmg: 'HMG', + mg: 'Major', + nl: 'No Logic', + none: 'None', + owg: 'OWG', + }, + goal: 'Goal', + goals: { + ad: 'All Dungeons', + fast: 'Fast Ganon', + ganon: 'Ganon', + ghunt: 'Ganon Hunt', + ped: 'Pedestal', + thunt: 'Triforce Hunt', + trinity: 'Trinity', + }, + gtCrystals: 'GT Crystals', + logic: 'Logic', showBig: 'Big Keys', showCompass: 'Compasses', showItemOptions: { @@ -518,6 +540,11 @@ export default { wildItems: 'Wild Dungeon Items', wildMap: 'Maps', wildSmall: 'Small Keys', + worldState: 'World State', + worldStates: { + inverted: 'Inverted', + open: 'Open', + }, }, location: { aginah: 'Aginah', diff --git a/resources/sass/common.scss b/resources/sass/common.scss index 8fe76bb..0d61af3 100644 --- a/resources/sass/common.scss +++ b/resources/sass/common.scss @@ -271,8 +271,6 @@ h1 { display: inline-block; width: 100%; height: 100%; - background: url(/items-v1.png); - background-size: 800% 1100%; } .strike { position: absolute; diff --git a/resources/sass/tracker.scss b/resources/sass/tracker.scss index c4541e2..27bd55e 100644 --- a/resources/sass/tracker.scss +++ b/resources/sass/tracker.scss @@ -195,11 +195,17 @@ } } .tracker-toolbar { + label .zelda-icon, .toggle-icon { display: inline-block; width: 2em; height: 2em; } + .form-select { + background-image: none; + padding-right: 0.75rem; + border: none; + } } .zelda-icon { width: 100%;