]> git.localhorst.tv Git - alttp.git/blob - resources/js/components/tracker/Map.js
tracker descriptions
[alttp.git] / resources / js / components / tracker / Map.js
1 import PropTypes from 'prop-types';
2 import React from 'react';
3 import { useTranslation } from 'react-i18next';
4
5 import { useTracker } from '../../hooks/tracker';
6
7 const LW_DUNGEONS = [
8         {
9                 id: 'hc',
10                 x: 0.5,
11                 y: 0.5,
12         },
13         {
14                 id: 'ct',
15                 x: 0.5,
16                 y: 0.4,
17         },
18         {
19                 id: 'ep',
20                 x: 0.95,
21                 y: 0.42,
22         },
23         {
24                 id: 'dp',
25                 x: 0.075,
26                 y: 0.8,
27         },
28         {
29                 id: 'th',
30                 x: 0.56,
31                 y: 0.05,
32         },
33 ];
34
35 const LW_LOCATIONS = [
36         {
37                 id: 'aginah',
38                 checks: [
39                         'aginah',
40                 ],
41                 x: 0.2,
42                 y: 0.83,
43         },
44         {
45                 id: 'blinds-hut',
46                 checks: [
47                         'blinds-hut-top',
48                         'blinds-hut-left',
49                         'blinds-hut-right',
50                         'blinds-hut-far-left',
51                         'blinds-hut-far-right',
52                 ],
53                 x: 0.14,
54                 y: 0.42,
55         },
56         {
57                 id: 'bombos-tablet',
58                 checks: [
59                         'bombos-tablet',
60                 ],
61                 x: 0.225,
62                 y: 0.925,
63         },
64         {
65                 id: 'bonk-rocks',
66                 checks: [
67                         'bonk-rocks',
68                 ],
69                 x: 0.4,
70                 y: 0.3,
71         },
72         {
73                 id: 'bottle-vendor',
74                 checks: [
75                         'bottle-vendor',
76                 ],
77                 x: 0.1,
78                 y: 0.475,
79         },
80         {
81                 id: 'cave-45',
82                 checks: [
83                         'cave-45',
84                 ],
85                 x: 0.27,
86                 y: 0.83,
87         },
88         {
89                 id: 'checkerboard',
90                 checks: [
91                         'checkerboard',
92                 ],
93                 x: 0.18,
94                 y: 0.78,
95         },
96         {
97                 id: 'chicken-house',
98                 checks: [
99                         'chicken-house',
100                 ],
101                 x: 0.1,
102                 y: 0.53,
103         },
104         {
105                 id: 'dam',
106                 checks: [
107                         'flooded-chest',
108                         'sunken-treasure',
109                 ],
110                 x: 0.4675,
111                 y: 0.9375,
112         },
113         {
114                 id: 'desert-ledge',
115                 checks: [
116                         'desert-ledge',
117                 ],
118                 x: 0.025,
119                 y: 0.9,
120         },
121         {
122                 id: 'ether-tablet',
123                 checks: [
124                         'ether-tablet',
125                 ],
126                 x: 0.425,
127                 y: 0.025,
128         },
129         {
130                 id: 'floating-island',
131                 checks: [
132                         'floating-island',
133                 ],
134                 x: 0.8,
135                 y: 0.025,
136         },
137         {
138                 id: 'flute-spot',
139                 checks: [
140                         'flute-spot',
141                 ],
142                 x: 0.3,
143                 y: 0.675,
144         },
145         {
146                 id: 'graveyard-ledge',
147                 checks: [
148                         'graveyard-ledge',
149                 ],
150                 x: 0.57,
151                 y: 0.28,
152         },
153         {
154                 id: 'hobo',
155                 checks: [
156                         'hobo',
157                 ],
158                 x: 0.7,
159                 y: 0.7,
160         },
161         {
162                 id: 'ice-rod-cave',
163                 checks: [
164                         'ice-rod-cave',
165                 ],
166                 x: 0.9,
167                 y: 0.76,
168         },
169         {
170                 id: 'kak-well',
171                 checks: [
172                         'kak-well-top',
173                         'kak-well-left',
174                         'kak-well-mid',
175                         'kak-well-right',
176                         'kak-well-bottom',
177                 ],
178                 x: 0.04,
179                 y: 0.425,
180         },
181         {
182                 id: 'kings-tomb',
183                 checks: [
184                         'kings-tomb',
185                 ],
186                 x: 0.62,
187                 y: 0.3,
188         },
189         {
190                 id: 'lake-hylia-island',
191                 checks: [
192                         'lake-hylia-island',
193                 ],
194                 x: 0.725,
195                 y: 0.8375,
196         },
197         {
198                 id: 'library',
199                 checks: [
200                         'library',
201                 ],
202                 x: 0.15,
203                 y: 0.65,
204         },
205         {
206                 id: 'links-house',
207                 checks: [
208                         'links-house',
209                 ],
210                 x: 0.55,
211                 y: 0.6875,
212         },
213         {
214                 id: 'lost-woods-hideout',
215                 checks: [
216                         'lost-woods-hideout',
217                 ],
218                 x: 0.19,
219                 y: 0.14,
220         },
221         {
222                 id: 'lumberjack',
223                 checks: [
224                         'lumberjack',
225                 ],
226                 x: 0.3,
227                 y: 0.07,
228         },
229         {
230                 id: 'magic-bat',
231                 checks: [
232                         'magic-bat',
233                 ],
234                 x: 0.325,
235                 y: 0.55,
236         },
237         {
238                 id: 'mimic-cave',
239                 checks: [
240                         'mimic-cave',
241                 ],
242                 x: 0.85,
243                 y: 0.1,
244         },
245         {
246                 id: 'mini-moldorm-cave',
247                 checks: [
248                         'mini-moldorm-left',
249                         'mini-moldorm-right',
250                         'mini-moldorm-far-left',
251                         'mini-moldorm-far-right',
252                         'mini-moldorm-npc',
253                 ],
254                 x: 0.65,
255                 y: 0.95,
256         },
257         {
258                 id: 'mushroom-spot',
259                 checks: [
260                         'mushroom-spot',
261                 ],
262                 x: 0.125,
263                 y: 0.08,
264         },
265         {
266                 id: 'old-man',
267                 checks: [
268                         'old-man',
269                 ],
270                 x: 0.405,
271                 y: 0.195,
272         },
273         {
274                 id: 'paradox-cave',
275                 checks: [
276                         'paradox-lower-far-left',
277                         'paradox-lower-left',
278                         'paradox-lower-right',
279                         'paradox-lower-far-right',
280                         'paradox-lower-mid',
281                         'paradox-upper-left',
282                         'paradox-upper-right',
283                 ],
284                 x: 0.85,
285                 y: 0.2,
286         },
287         {
288                 id: 'pedestal',
289                 checks: [
290                         'pedestal',
291                 ],
292                 x: 0.03,
293                 y: 0.05,
294         },
295         {
296                 id: 'potion-shop',
297                 checks: [
298                         'potion-shop',
299                 ],
300                 x: 0.8,
301                 y: 0.325,
302         },
303         {
304                 id: 'race-game',
305                 checks: [
306                         'race-game',
307                 ],
308                 x: 0.025,
309                 y: 0.7,
310         },
311         {
312                 id: 'saha',
313                 checks: [
314                         'saha',
315                 ],
316                 x: 0.815,
317                 y: 0.465,
318         },
319         {
320                 id: 'saha-hut',
321                 checks: [
322                         'saha-left',
323                         'saha-mid',
324                         'saha-right',
325                 ],
326                 x: 0.815,
327                 y: 0.42,
328         },
329         {
330                 id: 'sick-kid',
331                 checks: [
332                         'sick-kid',
333                 ],
334                 x: 0.155,
335                 y: 0.525,
336         },
337         {
338                 id: 'uncle',
339                 checks: [
340                         'uncle',
341                         'secret-passage',
342                 ],
343                 x: 0.6,
344                 y: 0.4,
345         },
346         {
347                 id: 'spec-rock',
348                 checks: [
349                         'spec-rock',
350                 ],
351                 x: 0.48,
352                 y: 0.09,
353         },
354         {
355                 id: 'spec-rock-cave',
356                 checks: [
357                         'spec-rock-cave',
358                 ],
359                 x: 0.48,
360                 y: 0.14,
361         },
362         {
363                 id: 'spiral-cave',
364                 checks: [
365                         'spiral-cave',
366                 ],
367                 x: 0.8,
368                 y: 0.1,
369         },
370         {
371                 id: 'tavern',
372                 checks: [
373                         'tavern',
374                 ],
375                 x: 0.16,
376                 y: 0.58,
377         },
378         {
379                 id: 'waterfall-fairy',
380                 checks: [
381                         'waterfall-fairy-left',
382                         'waterfall-fairy-right',
383                 ],
384                 x: 0.9,
385                 y: 0.15,
386         },
387         {
388                 id: 'zora',
389                 checks: [
390                         'zora',
391                 ],
392                 x: 0.975,
393                 y: 0.12,
394         },
395         {
396                 id: 'zora-ledge',
397                 checks: [
398                         'zora-ledge',
399                 ],
400                 x: 0.975,
401                 y: 0.165,
402         },
403 ];
404
405 const DW_DUNGEONS = [
406         {
407                 id: 'pd',
408                 x: 0.95,
409                 y: 0.42,
410         },
411         {
412                 id: 'sp',
413                 x: 0.4675,
414                 y: 0.9375,
415         },
416         {
417                 id: 'sw',
418                 x: 0.05,
419                 y: 0.05,
420         },
421         {
422                 id: 'tt',
423                 x: 0.125,
424                 y: 0.475,
425         },
426         {
427                 id: 'ip',
428                 x: 0.7975,
429                 y: 0.86,
430         },
431         {
432                 id: 'mm',
433                 x: 0.12,
434                 y: 0.82,
435         },
436         {
437                 id: 'tr',
438                 x: 0.94,
439                 y: 0.06,
440         },
441         {
442                 id: 'gt',
443                 x: 0.56,
444                 y: 0.05,
445         },
446 ];
447
448 const DW_LOCATIONS = [
449         {
450                 id: 'blacksmith',
451                 checks: [
452                         'blacksmith',
453                 ],
454                 x: 0.15,
455                 y: 0.65,
456         },
457         {
458                 id: 'brewery',
459                 checks: [
460                         'brewery',
461                 ],
462                 x: 0.1,
463                 y: 0.6,
464         },
465         {
466                 id: 'bumper-cave',
467                 checks: [
468                         'bumper-cave',
469                 ],
470                 x: 0.325,
471                 y: 0.15,
472         },
473         {
474                 id: 'c-house',
475                 checks: [
476                         'c-house',
477                 ],
478                 x: 0.2,
479                 y: 0.5,
480         },
481         {
482                 id: 'catfish',
483                 checks: [
484                         'catfish',
485                 ],
486                 x: 0.9,
487                 y: 0.175,
488         },
489         {
490                 id: 'chest-game',
491                 checks: [
492                         'chest-game',
493                 ],
494                 x: 0.05,
495                 y: 0.45,
496         },
497         {
498                 id: 'digging-game',
499                 checks: [
500                         'digging-game',
501                 ],
502                 x: 0.05,
503                 y: 0.7,
504         },
505         {
506                 id: 'hammer-pegs',
507                 checks: [
508                         'hammer-pegs',
509                 ],
510                 x: 0.3125,
511                 y: 0.6,
512         },
513         {
514                 id: 'hookshot-cave',
515                 checks: [
516                         'hookshot-cave-tl',
517                         'hookshot-cave-tr',
518                         'hookshot-cave-bl',
519                 ],
520                 x: 0.85,
521                 y: 0.02,
522         },
523         {
524                 id: 'hookshot-cave-bonk',
525                 checks: [
526                         'hookshot-cave-br',
527                 ],
528                 x: 0.85,
529                 y: 0.065,
530         },
531         {
532                 id: 'hype-cave',
533                 checks: [
534                         'hype-cave-top',
535                         'hype-cave-left',
536                         'hype-cave-right',
537                         'hype-cave-bottom',
538                         'hype-cave-npc',
539                 ],
540                 x: 0.6,
541                 y: 0.75,
542         },
543         {
544                 id: 'mire-shed',
545                 checks: [
546                         'mire-shed-left',
547                         'mire-shed-right',
548                 ],
549                 x: 0.04,
550                 y: 0.8,
551         },
552         {
553                 id: 'purple-chest',
554                 checks: [
555                         'purple-chest',
556                 ],
557                 x: 0.3125,
558                 y: 0.525,
559         },
560         {
561                 id: 'pyramid',
562                 checks: [
563                         'pyramid',
564                 ],
565                 x: 0.575,
566                 y: 0.45,
567         },
568         {
569                 id: 'pyramid-fairy',
570                 checks: [
571                         'pyramid-fairy-left',
572                         'pyramid-fairy-right',
573                 ],
574                 x: 0.45,
575                 y: 0.5,
576         },
577         {
578                 id: 'spike-cave',
579                 checks: [
580                         'spike-cave',
581                 ],
582                 x: 0.575,
583                 y: 0.15,
584         },
585         {
586                 id: 'stumpy',
587                 checks: [
588                         'stumpy',
589                 ],
590                 x: 0.3125,
591                 y: 0.6875,
592         },
593         {
594                 id: 'super-bunny',
595                 checks: [
596                         'super-bunny-top',
597                         'super-bunny-bottom',
598                 ],
599                 x: 0.85,
600                 y: 0.15,
601         },
602 ];
603
604 const Location = ({ number, l, size }) => {
605         const { t } = useTranslation();
606
607         const classNames = ['location', `status-${l.status}`];
608         if (size) {
609                 classNames.push(`size-${size}`);
610         }
611
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>
617                 : null}
618         </g>;
619 };
620
621 Location.propTypes = {
622         number: PropTypes.bool,
623         l: PropTypes.shape({
624                 id: PropTypes.string,
625                 x: PropTypes.number,
626                 y: PropTypes.number,
627                 cleared: PropTypes.number,
628                 total: PropTypes.number,
629                 status: PropTypes.string,
630         }),
631         size: PropTypes.string,
632 };
633
634 const Map = () => {
635         const { dungeons, state } = useTracker();
636
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`]) {
645                                         status = 'cleared';
646                                 }
647                         } else {
648                                 status = 'cleared';
649                         }
650                 }
651                 return {
652                         ...dungeon,
653                         status,
654                         cleared,
655                         total,
656                 };
657         }, [dungeons, state]);
658
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) {
664                         status = 'cleared';
665                 }
666                 return {
667                         ...loc,
668                         cleared,
669                         total,
670                         status,
671                 };
672         }, [state]);
673
674         const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]);
675         const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]);
676
677         const dwDungeons = React.useMemo(() => DW_DUNGEONS.map(mapDungeon), [mapDungeon]);
678         const dwLocations = React.useMemo(() => DW_LOCATIONS.map(mapLocation), [mapLocation]);
679
680         return <div className="tracker-map">
681                 <svg
682                         xmlns="http://www.w3.org/2000/svg"
683                         className="canvas"
684                         width="2"
685                         height="1"
686                         viewBox="0 0 2 1"
687                 >
688                         <g className="light-world">
689                                 <g className="background">
690                                         <image
691                                                 x="0"
692                                                 y="0"
693                                                 width="0.5"
694                                                 height="0.5"
695                                                 href="/media/alttp/map/lw_files/9/0_0.png"
696                                         />
697                                         <image
698                                                 x="0.5"
699                                                 y="0"
700                                                 width="0.5"
701                                                 height="0.5"
702                                                 href="/media/alttp/map/lw_files/9/1_0.png"
703                                         />
704                                         <image
705                                                 x="0"
706                                                 y="0.5"
707                                                 width="0.5"
708                                                 height="0.5"
709                                                 href="/media/alttp/map/lw_files/9/0_1.png"
710                                         />
711                                         <image
712                                                 x="0.5"
713                                                 y="0.5"
714                                                 width="0.5"
715                                                 height="0.5"
716                                                 href="/media/alttp/map/lw_files/9/1_1.png"
717                                         />
718                                 </g>
719                                 <g className="locations">
720                                         {lwLocations.map(l =>
721                                                 <Location key={l.id} l={l} />
722                                         )}
723                                         {lwDungeons.map(l =>
724                                                 <Location key={l.id} number l={l} size="lg" />
725                                         )}
726                                 </g>
727                         </g>
728                         <g className="dark-world" transform="translate(1 0)">
729                                 <g className="background">
730                                         <image
731                                                 x="0"
732                                                 y="0"
733                                                 width="0.5"
734                                                 height="0.5"
735                                                 href="/media/alttp/map/dw_files/9/0_0.png"
736                                         />
737                                         <image
738                                                 x="0.5"
739                                                 y="0"
740                                                 width="0.5"
741                                                 height="0.5"
742                                                 href="/media/alttp/map/dw_files/9/1_0.png"
743                                         />
744                                         <image
745                                                 x="0"
746                                                 y="0.5"
747                                                 width="0.5"
748                                                 height="0.5"
749                                                 href="/media/alttp/map/dw_files/9/0_1.png"
750                                         />
751                                         <image
752                                                 x="0.5"
753                                                 y="0.5"
754                                                 width="0.5"
755                                                 height="0.5"
756                                                 href="/media/alttp/map/dw_files/9/1_1.png"
757                                         />
758                                 </g>
759                                 <g className="locations">
760                                         {dwLocations.map(l =>
761                                                 <Location key={l.id} l={l} />
762                                         )}
763                                         {dwDungeons.map(l =>
764                                                 <Location key={l.id} number l={l} size="lg" />
765                                         )}
766                                 </g>
767                         </g>
768                 </svg>
769         </div>;
770 };
771
772 export default Map;