From f145023c506cecad618e465a034933e1a5962637 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sun, 24 Mar 2024 19:26:18 +0100 Subject: [PATCH] add a map to the tracker --- resources/js/components/tracker/Map.js | 767 +++++++++++++++++++++++ resources/js/components/tracker/index.js | 14 +- resources/sass/tracker.scss | 23 + 3 files changed, 801 insertions(+), 3 deletions(-) create mode 100644 resources/js/components/tracker/Map.js diff --git a/resources/js/components/tracker/Map.js b/resources/js/components/tracker/Map.js new file mode 100644 index 0000000..d8e7636 --- /dev/null +++ b/resources/js/components/tracker/Map.js @@ -0,0 +1,767 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useTracker } from '../../hooks/tracker'; + +const LW_DUNGEONS = [ + { + id: 'hc', + x: 0.5, + y: 0.5, + }, + { + id: 'ct', + x: 0.5, + y: 0.4, + }, + { + id: 'ep', + x: 0.95, + y: 0.42, + }, + { + id: 'dp', + x: 0.075, + y: 0.8, + }, + { + id: 'th', + x: 0.56, + y: 0.05, + }, +]; + +const LW_LOCATIONS = [ + { + id: 'aginah', + checks: [ + 'aginah', + ], + x: 0.2, + y: 0.83, + }, + { + id: 'blinds-hut', + checks: [ + 'blinds-hut-top', + 'blinds-hut-left', + 'blinds-hut-right', + 'blinds-hut-far-left', + 'blinds-hut-far-right', + ], + x: 0.14, + y: 0.42, + }, + { + id: 'bombos-tablet', + checks: [ + 'bombos-tablet', + ], + x: 0.225, + y: 0.925, + }, + { + id: 'bonk-rocks', + checks: [ + 'bonk-rocks', + ], + x: 0.4, + y: 0.3, + }, + { + id: 'bottle-vendor', + checks: [ + 'bottle-vendor', + ], + x: 0.1, + y: 0.475, + }, + { + id: 'cave-45', + checks: [ + 'cave-45', + ], + x: 0.27, + y: 0.83, + }, + { + id: 'checkerboard', + checks: [ + 'checkerboard', + ], + x: 0.18, + y: 0.78, + }, + { + id: 'chicken-house', + checks: [ + 'chicken-house', + ], + x: 0.1, + y: 0.53, + }, + { + id: 'dam', + checks: [ + 'flooded-chest', + 'sunken-treasure', + ], + x: 0.4675, + y: 0.9375, + }, + { + id: 'desert-ledge', + checks: [ + 'desert-ledge', + ], + x: 0.025, + y: 0.9, + }, + { + id: 'ether-tablet', + checks: [ + 'ether-tablet', + ], + x: 0.425, + y: 0.025, + }, + { + id: 'floating-island', + checks: [ + 'floating-island', + ], + x: 0.8, + y: 0.025, + }, + { + id: 'flute-spot', + checks: [ + 'flute-spot', + ], + x: 0.3, + y: 0.675, + }, + { + id: 'graveyard-ledge', + checks: [ + 'graveyard-ledge', + ], + x: 0.57, + y: 0.28, + }, + { + id: 'hobo', + checks: [ + 'hobo', + ], + x: 0.7, + y: 0.7, + }, + { + id: 'ice-rod-cave', + checks: [ + 'ice-rod-cave', + ], + x: 0.9, + y: 0.76, + }, + { + id: 'kak-well', + checks: [ + 'kak-well-top', + 'kak-well-left', + 'kak-well-mid', + 'kak-well-right', + 'kak-well-bottom', + ], + x: 0.04, + y: 0.425, + }, + { + id: 'kings-tomb', + checks: [ + 'kings-tomb', + ], + x: 0.62, + y: 0.3, + }, + { + id: 'lake-hylia-island', + checks: [ + 'lake-hylia-island', + ], + x: 0.725, + y: 0.8375, + }, + { + id: 'library', + checks: [ + 'library', + ], + x: 0.15, + y: 0.65, + }, + { + id: 'links-house', + checks: [ + 'links-house', + ], + x: 0.55, + y: 0.6875, + }, + { + id: 'lost-woods-hideout', + checks: [ + 'lost-woods-hideout', + ], + x: 0.19, + y: 0.14, + }, + { + id: 'lumberjack', + checks: [ + 'lumberjack', + ], + x: 0.3, + y: 0.07, + }, + { + id: 'magic-bat', + checks: [ + 'magic-bat', + ], + x: 0.325, + y: 0.55, + }, + { + id: 'mimic-cave', + checks: [ + 'mimic-cave', + ], + x: 0.85, + y: 0.1, + }, + { + id: 'mini-moldorm-cave', + checks: [ + 'mini-moldorm-left', + 'mini-moldorm-right', + 'mini-moldorm-far-left', + 'mini-moldorm-far-right', + 'mini-moldorm-npc', + ], + x: 0.65, + y: 0.95, + }, + { + id: 'mushroom-spot', + checks: [ + 'mushroom-spot', + ], + x: 0.125, + y: 0.08, + }, + { + id: 'old-man', + checks: [ + 'old-man', + ], + x: 0.405, + y: 0.195, + }, + { + id: 'paradox-cave', + checks: [ + 'paradox-lower-far-left', + 'paradox-lower-left', + 'paradox-lower-right', + 'paradox-lower-far-right', + 'paradox-lower-mid', + 'paradox-upper-left', + 'paradox-upper-right', + ], + x: 0.85, + y: 0.2, + }, + { + id: 'pedestal', + checks: [ + 'pedestal', + ], + x: 0.03, + y: 0.05, + }, + { + id: 'potion-shop', + checks: [ + 'potion-shop', + ], + x: 0.8, + y: 0.325, + }, + { + id: 'race-game', + checks: [ + 'race-game', + ], + x: 0.025, + y: 0.7, + }, + { + id: 'saha', + checks: [ + 'saha', + ], + x: 0.815, + y: 0.42, + }, + { + id: 'saha-hut', + checks: [ + 'saha-left', + 'saha-mid', + 'saha-right', + ], + x: 0.815, + y: 0.465, + }, + { + id: 'sick-kid', + checks: [ + 'sick-kid', + ], + x: 0.155, + y: 0.525, + }, + { + id: 'uncle', + checks: [ + 'uncle', + 'secret-passage', + ], + x: 0.6, + y: 0.4, + }, + { + id: 'spec-rock', + checks: [ + 'spec-rock', + ], + x: 0.48, + y: 0.09, + }, + { + id: 'spec-rock-cave', + checks: [ + 'spec-rock-cave', + ], + x: 0.48, + y: 0.14, + }, + { + id: 'spiral-cave', + checks: [ + 'spiral-cave', + ], + x: 0.8, + y: 0.1, + }, + { + id: 'tavern', + checks: [ + 'tavern', + ], + x: 0.16, + y: 0.58, + }, + { + id: 'waterfall-fairy', + checks: [ + 'waterfall-fairy-left', + 'waterfall-fairy-right', + ], + x: 0.9, + y: 0.15, + }, + { + id: 'zora', + checks: [ + 'zora', + ], + x: 0.975, + y: 0.12, + }, + { + id: 'zora-ledge', + checks: [ + 'zora-ledge', + ], + x: 0.975, + y: 0.165, + }, +]; + +const DW_DUNGEONS = [ + { + id: 'pd', + x: 0.95, + y: 0.42, + }, + { + id: 'sp', + x: 0.4675, + y: 0.9375, + }, + { + id: 'sw', + x: 0.05, + y: 0.05, + }, + { + id: 'tt', + x: 0.125, + y: 0.475, + }, + { + id: 'ip', + x: 0.7975, + y: 0.86, + }, + { + id: 'mm', + x: 0.12, + y: 0.82, + }, + { + id: 'tr', + x: 0.94, + y: 0.06, + }, + { + id: 'gt', + x: 0.56, + y: 0.05, + }, +]; + +const DW_LOCATIONS = [ + { + id: 'blacksmith', + checks: [ + 'blacksmith', + ], + x: 0.15, + y: 0.65, + }, + { + id: 'brewery', + checks: [ + 'brewery', + ], + x: 0.1, + y: 0.6, + }, + { + id: 'bumper-cave', + checks: [ + 'bumper-cave', + ], + x: 0.325, + y: 0.15, + }, + { + id: 'c-house', + checks: [ + 'c-house', + ], + x: 0.2, + y: 0.5, + }, + { + id: 'catfish', + checks: [ + 'catfish', + ], + x: 0.9, + y: 0.175, + }, + { + id: 'chest-game', + checks: [ + 'chest-game', + ], + x: 0.05, + y: 0.45, + }, + { + id: 'digging-game', + checks: [ + 'digging-game', + ], + x: 0.05, + y: 0.7, + }, + { + id: 'hammer-pegs', + checks: [ + 'hammer-pegs', + ], + x: 0.3125, + y: 0.6, + }, + { + id: 'hookshot-cave', + checks: [ + 'hookshot-cave-tl', + 'hookshot-cave-tr', + 'hookshot-cave-bl', + ], + x: 0.85, + y: 0.02, + }, + { + id: 'hookshot-cave-bonk', + checks: [ + 'hookshot-cave-br', + ], + x: 0.85, + y: 0.065, + }, + { + id: 'hype-cave', + checks: [ + 'hype-cave-top', + 'hype-cave-left', + 'hype-cave-right', + 'hype-cave-bottom', + 'hype-cave-npc', + ], + x: 0.6, + y: 0.75, + }, + { + id: 'mire-shed', + checks: [ + 'mire-shed-left', + 'mire-shed-right', + ], + x: 0.04, + y: 0.8, + }, + { + id: 'purple-chest', + checks: [ + 'purple-chest', + ], + x: 0.3125, + y: 0.525, + }, + { + id: 'pyramid', + checks: [ + 'pyramid', + ], + x: 0.575, + y: 0.45, + }, + { + id: 'pyramid-fairy', + checks: [ + 'pyramid-fairy-left', + 'pyramid-fairy-right', + ], + x: 0.45, + y: 0.5, + }, + { + id: 'spike-cave', + checks: [ + 'spike-cave', + ], + x: 0.575, + y: 0.15, + }, + { + id: 'stumpy', + checks: [ + 'stumpy', + ], + x: 0.3125, + y: 0.6875, + }, + { + id: 'super-bunny', + checks: [ + 'super-bunny-top', + 'super-bunny-bottom', + ], + x: 0.85, + y: 0.15, + }, +]; + +const Location = ({ l, size }) => { + const { t } = useTranslation(); + + const classNames = ['location', `status-${l.status}`]; + if (size) { + classNames.push(`size-${size}`); + } + + return + {t(`tracker.location.${l.id}`)} + ; +}; + +Location.propTypes = { + l: PropTypes.shape({ + id: PropTypes.string, + x: PropTypes.number, + y: PropTypes.number, + cleared: PropTypes.number, + total: PropTypes.number, + status: PropTypes.string, + }), + size: PropTypes.string, +}; + +const Map = () => { + const { dungeons, state } = useTracker(); + + const mapDungeon = React.useCallback(dungeon => { + const definition = dungeons.find(d => d.id === dungeon.id); + const cleared = state[`${dungeon.id}-checks`] || 0; + const total = definition.items; + let status = 'available'; + if (cleared === total) { + if (['ct', 'gt'].includes(dungeon.id)) { + if (state[`${dungeon.id}-boss-defeated`]) { + status = 'cleared'; + } + } else { + status = 'cleared'; + } + } + return { + ...dungeon, + status, + cleared, + total, + }; + }, [dungeons, state]); + + const mapLocation = React.useCallback(loc => { + const cleared = loc.checks.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0); + const total = loc.checks.length; + let status = 'available'; + if (cleared === total) { + status = 'cleared'; + } + return { + ...loc, + cleared, + total, + status, + }; + }, [state]); + + const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]); + const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]); + + const dwDungeons = React.useMemo(() => DW_DUNGEONS.map(mapDungeon), [mapDungeon]); + const dwLocations = React.useMemo(() => DW_LOCATIONS.map(mapLocation), [mapLocation]); + + return
+ + + + + + + + + + {lwLocations.map(l => + + )} + {lwDungeons.map(l => + + )} + + + + + + + + + + + {dwLocations.map(l => + + )} + {dwDungeons.map(l => + + )} + + + +
; +}; + +export default Map; diff --git a/resources/js/components/tracker/index.js b/resources/js/components/tracker/index.js index 5a505b5..0dd7cc5 100644 --- a/resources/js/components/tracker/index.js +++ b/resources/js/components/tracker/index.js @@ -3,14 +3,22 @@ import React from 'react'; import Dungeons from './Dungeons'; import Equipment from './Equipment'; import Items from './Items'; +import Map from './Map'; import Toolbar from './Toolbar'; const Tracker = () => { return
- - - +
+
+ + + +
+
+ +
+
; }; diff --git a/resources/sass/tracker.scss b/resources/sass/tracker.scss index 2e75301..f1b745a 100644 --- a/resources/sass/tracker.scss +++ b/resources/sass/tracker.scss @@ -118,6 +118,29 @@ line-height: 1; } } + .tracker-map { + .canvas { + width: 50em; + height: auto; + .location { + width: 0.04px; + height: 0.04px; + fill: green; + stroke: black; + stroke-width: 0.003px; + transform: translate(-0.02px, -0.02px); + &.status-cleared { + fill: grey; + opacity: 0.4; + } + &.size-lg { + width: 0.08px; + height: 0.08px; + transform: translate(-0.04px, -0.04px); + } + } + } + } .toggle-icon { &.inactive { opacity: .5; -- 2.39.2