1 import PropTypes from 'prop-types';
2 import React from 'react';
3 import { useTranslation } from 'react-i18next';
5 import { useTracker } from '../../hooks/tracker';
35 const LW_LOCATIONS = [
50 'blinds-hut-far-left',
51 'blinds-hut-far-right',
130 id: 'floating-island',
146 id: 'graveyard-ledge',
190 id: 'lake-hylia-island',
214 id: 'lost-woods-hideout',
216 'lost-woods-hideout',
246 id: 'mini-moldorm-cave',
249 'mini-moldorm-right',
250 'mini-moldorm-far-left',
251 'mini-moldorm-far-right',
276 'paradox-lower-far-left',
277 'paradox-lower-left',
278 'paradox-lower-right',
279 'paradox-lower-far-right',
281 'paradox-upper-left',
282 'paradox-upper-right',
355 id: 'spec-rock-cave',
379 id: 'waterfall-fairy',
381 'waterfall-fairy-left',
382 'waterfall-fairy-right',
405 const DW_DUNGEONS = [
448 const DW_LOCATIONS = [
524 id: 'hookshot-cave-bonk',
571 'pyramid-fairy-left',
572 'pyramid-fairy-right',
597 'super-bunny-bottom',
604 const Location = ({ number, l, size }) => {
605 const { t } = useTranslation();
607 const classNames = ['location', `status-${l.status}`];
609 classNames.push(`size-${size}`);
612 return <g className={classNames.join(' ')} transform={`translate(${l.x} ${l.y})`}>
613 <title>{t(`tracker.location.${l.id}`)}</title>
614 <rect className="box" x="0" y="0" />
615 {number && l.cleared < l.total ?
616 <text className="text" x="0" y="0">{Math.max(0, l.total - l.cleared)}</text>
621 Location.propTypes = {
622 number: PropTypes.bool,
624 id: PropTypes.string,
627 cleared: PropTypes.number,
628 total: PropTypes.number,
629 status: PropTypes.string,
631 size: PropTypes.string,
635 const { dungeons, state } = useTracker();
637 const mapDungeon = React.useCallback(dungeon => {
638 const definition = dungeons.find(d => d.id === dungeon.id);
639 const cleared = state[`${dungeon.id}-checks`] || 0;
640 const total = definition.items;
641 let status = 'available';
642 if (cleared === total) {
643 if (['ct', 'gt'].includes(dungeon.id)) {
644 if (state[`${dungeon.id}-boss-defeated`]) {
657 }, [dungeons, state]);
659 const mapLocation = React.useCallback(loc => {
660 const cleared = loc.checks.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0);
661 const total = loc.checks.length;
662 let status = 'available';
663 if (cleared === total) {
674 const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]);
675 const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]);
677 const dwDungeons = React.useMemo(() => DW_DUNGEONS.map(mapDungeon), [mapDungeon]);
678 const dwLocations = React.useMemo(() => DW_LOCATIONS.map(mapLocation), [mapLocation]);
680 return <div className="tracker-map">
682 xmlns="http://www.w3.org/2000/svg"
688 <g className="light-world">
689 <g className="background">
695 href="/media/alttp/map/lw_files/9/0_0.png"
702 href="/media/alttp/map/lw_files/9/1_0.png"
709 href="/media/alttp/map/lw_files/9/0_1.png"
716 href="/media/alttp/map/lw_files/9/1_1.png"
719 <g className="locations">
720 {lwLocations.map(l =>
721 <Location key={l.id} l={l} />
724 <Location key={l.id} number l={l} size="lg" />
728 <g className="dark-world" transform="translate(1 0)">
729 <g className="background">
735 href="/media/alttp/map/dw_files/9/0_0.png"
742 href="/media/alttp/map/dw_files/9/1_0.png"
749 href="/media/alttp/map/dw_files/9/0_1.png"
756 href="/media/alttp/map/dw_files/9/1_1.png"
759 <g className="locations">
760 {dwLocations.map(l =>
761 <Location key={l.id} l={l} />
764 <Location key={l.id} number l={l} size="lg" />