]> git.localhorst.tv Git - alttp.git/commitdiff
UW grid overlay
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 13 Feb 2023 12:55:37 +0000 (13:55 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 13 Feb 2023 12:55:37 +0000 (13:55 +0100)
resources/js/components/map/Buttons.js
resources/js/components/map/Overlay.js
resources/js/components/map/UWSuperTiles.js [new file with mode: 0644]
resources/js/components/pages/Map.js
resources/js/i18n/de.js
resources/js/i18n/en.js
resources/sass/map.scss

index fdffe92cdbfd57b75f7daeb14f6d7e7fb79c765c..fa023927bd230aee191c9d1cebbfb29adafa10a4 100644 (file)
@@ -1,26 +1,48 @@
+import PropTypes from 'prop-types';
 import React from 'react';
-import { Button } from 'react-bootstrap';
+import { Button, Form } from 'react-bootstrap';
 import { useTranslation } from 'react-i18next';
 
 import { useOpenSeadragon } from './OpenSeadragon';
 
-const Buttons = () => {
+const Buttons = ({ setUWOverlay, uwOverlay }) => {
        const { activeMap, setActiveMap } = useOpenSeadragon();
        const { t } = useTranslation();
 
-       return <div className="button-bar">
-               {['lw', 'dw', 'sp', 'uw'].map(map =>
-                       <Button
-                               active={activeMap === map}
-                               key={map}
-                               onClick={() => setActiveMap(map)}
-                               title={t(`map.${map}Long`)}
-                               variant="outline-secondary"
-                       >
-                               {t(`map.${map}Short`)}
-                       </Button>
-               )}
+       return <div className="mt-5">
+               <div className="button-bar">
+                       {['lw', 'dw', 'sp', 'uw'].map(map =>
+                               <Button
+                                       active={activeMap === map}
+                                       key={map}
+                                       onClick={() => setActiveMap(map)}
+                                       title={t(`map.${map}Long`)}
+                                       variant="outline-secondary"
+                               >
+                                       {t(`map.${map}Short`)}
+                               </Button>
+                       )}
+               </div>
+               {activeMap === 'uw' ?
+                       <div className="mt-2">
+                               <Form.Check
+                                       checked={uwOverlay}
+                                       id="toggle-uw-overlay"
+                                       inline
+                                       onChange={e => setUWOverlay(e.target.checked)}
+                                       type="checkbox"
+                               />
+                               <Form.Label className="mt-0" htmlFor="toggle-uw-overlay">
+                                       {t('map.uwOverlay')}
+                               </Form.Label>
+                       </div>
+               : null}
        </div>;
 };
 
+Buttons.propTypes = {
+       setUWOverlay: PropTypes.func,
+       uwOverlay: PropTypes.bool,
+};
+
 export default Buttons;
index 4068bea92e5940b18abc9179357aa34ea06d2051..4ec503ac820bad275194eb7ac797128c54b49b7b 100644 (file)
@@ -5,31 +5,64 @@ import { createPortal } from 'react-dom';
 
 import { useOpenSeadragon } from './OpenSeadragon';
 
-const Overlay = ({ children, onClick, x, y }) => {
+const Overlay = ({ children, height, onClick, page, width, x, y }) => {
        const { viewer } = useOpenSeadragon();
        const [element] = React.useState(document.createElement('div'));
 
        React.useEffect(() => {
                if (!viewer) return;
-               viewer.addOverlay(
-                       element,
-                       new OpenSeadragon.Point(x, y),
-                       OpenSeadragon.Placement.CENTER,
-               );
-               if (onClick) {
-                       new OpenSeadragon.MouseTracker({
-                               element,
-                               clickHandler: onClick,
-                       });
+               const add = () => {
+                       if (width && height) {
+                               viewer.addOverlay(
+                                       element,
+                                       new OpenSeadragon.Rect(x, y, width, height),
+                               );
+                       } else {
+                               viewer.addOverlay(
+                                       element,
+                                       new OpenSeadragon.Point(x, y),
+                                       OpenSeadragon.Placement.CENTER,
+                               );
+                       }
+                       if (onClick) {
+                               new OpenSeadragon.MouseTracker({
+                                       element,
+                                       clickHandler: onClick,
+                               });
+                       }
+               };
+               const addPage = () => {
+                       if (viewer.currentPage() === page) {
+                               add();
+                       }
+               };
+               if (typeof page !== 'undefined') {
+                       viewer.addHandler('page', addPage);
+                       return () => {
+                               viewer.removeHandler('page', addPage);
+                               viewer.removeOverlay(element);
+                       };
                }
-       }, [onClick, viewer, x, y]);
+               if (viewer.isOpen()) {
+                       add();
+               } else {
+                       viewer.addHandler('open', add);
+                       return () => {
+                               viewer.removeHandler('open', add);
+                               viewer.removeOverlay(element);
+                       };
+               }
+       }, [onClick, height, page, viewer, width, x, y]);
 
        return createPortal(children, element);
 };
 
 Overlay.propTypes = {
        children: PropTypes.node,
+       height: PropTypes.number,
        onClick: PropTypes.func,
+       page: PropTypes.number,
+       width: PropTypes.number,
        x: PropTypes.number,
        y: PropTypes.number,
 };
diff --git a/resources/js/components/map/UWSuperTiles.js b/resources/js/components/map/UWSuperTiles.js
new file mode 100644 (file)
index 0000000..2d7afda
--- /dev/null
@@ -0,0 +1,504 @@
+import OpenSeadragon from 'openseadragon';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import Overlay from './Overlay';
+import { useOpenSeadragon } from './OpenSeadragon';
+
+const dropMap = {
+       '00': '10',
+       '03': '02',
+       '05': '05',
+       '06': '05',
+       '07': '17',
+       '08': '07',
+       '09': '4B',
+       '0A': '09',
+       '0B': '6A',
+       '0D': '0B',
+       '0F': '01',
+       '10': '01',
+       '12': '0D',
+       '13': '0D',
+       '14': '0D',
+       '17': '27',
+       '18': '12',
+       '19': '07',
+       '1E': '3E',
+       '20': '01',
+       '21': '0D',
+       '22': '0D',
+       '23': '0D',
+       '24': '08',
+       '27': '31',
+       '29': '07',
+       '2A': '07',
+       '2C': '12',
+       '2D': '06',
+       '2E': '06',
+       '2F': '02',
+       '31': '77',
+       '33': '08',
+       '35': '08',
+       '36': '08',
+       '37': '08',
+       '39': '29',
+       '3A': '0A',
+       '3C': '0E',
+       '3D': '96',
+       '43': '0A',
+       '44': '0A',
+       '46': '09',
+       '47': '07',
+       '48': '07',
+       '49': '07',
+       '4B': '09',
+       '4D': 'A6',
+       '4F': 'BE',
+       '54': '34',
+       '55': '09',
+       '56': '09',
+       '57': '09',
+       '58': '09',
+       '59': '07',
+       '5A': '0E',
+       '5B': '0E',
+       '5E': '7E',
+       '65': 'AC',
+       '67': '09',
+       '68': '07',
+       '6D': '0B',
+       '73': '05',
+       '74': '05',
+       '75': '08',
+       '77': 'A7',
+       '78': '9D',
+       '79': '9D',
+       '7A': '9D',
+       '7B': '9D',
+       '7C': '0E',
+       '7D': '9B',
+       '7E': '9E',
+       '81': '05',
+       '82': '05',
+       '83': '05',
+       '84': '05',
+       '85': '05',
+       '88': 'A9',
+       '89': 'A9',
+       '8A': '0E',
+       '8B': '0E',
+       '8C': '1C',
+       '8D': '0B',
+       '8F': '0C',
+       '90': '0C',
+       '92': '0C',
+       '94': '0E',
+       '95': '0E',
+       '97': 'D1',
+       '9A': '7D',
+       '9B': '7D',
+       '9C': '0E',
+       '9D': '7B',
+       '9E': 'BE',
+       '9F': '0C',
+       'A1': '0C',
+       'A3': '0C',
+       'A4': '0E',
+       'A7': '17',
+       'A8': '05',
+       'A9': '89',
+       'AA': '0A',
+       'AC': '0B',
+       'AF': '02',
+       'B1': 'B2',
+       'B2': '0C',
+       'B3': '0D',
+       'B7': '0D',
+       'B8': '05',
+       'B9': '05',
+       'BA': '0A',
+       'BB': '0A',
+       'BD': '4F',
+       'BE': '4F',
+       'BF': '02',
+       'C1': '0C',
+       'C2': '0C',
+       'C3': '0D',
+       'C5': '0D',
+       'C6': '0D',
+       'C7': '05',
+       'C8': '05',
+       'C9': '0A',
+       'CA': '0B',
+       'CB': '0B',
+       'CC': '0B',
+       'CD': 'DE',
+       'CE': 'DE',
+       'D1': 'B1',
+       'D3': '05',
+       'D4': '05',
+       'D5': '0D',
+       'D6': '0D',
+       'D7': '05',
+       'D8': '05',
+       'D9': '05',
+       'DB': '0B',
+       'DC': '0B',
+       'DD': '06',
+       'DE': '06',
+       'E1': '06',
+       'E2': '06',
+       'E3': '14',
+       'E4': '06',
+       'E5': '06',
+       'E6': '06',
+       'E7': '06',
+       'E8': 'F8',
+       'E9': 'FA',
+       'EA': 'FA',
+       'EB': 'FB',
+       'EC': 'FD',
+       'ED': 'FD',
+       'EE': 'FE',
+       'EF': 'FF',
+       'F0': '06',
+       'F1': '06',
+       'F4': '06',
+       'F5': '06',
+       'F9': '06',
+       'FE': '06',
+};
+
+const strongEG = [
+       '08',
+       '0C',
+       '15',
+       '2F',
+       '40',
+       '51',
+       '52',
+       '59',
+       '5B',
+       '60',
+       '62',
+       '66',
+       '71',
+       '72',
+       '81',
+       'A2',
+       'A8',
+       'A9',
+       'AA',
+       'B2',
+       'B3',
+       'B9',
+       'C2',
+       'C3',
+       'CB',
+       'CC',
+       'DB',
+       'DC',
+       'DF',
+       'E1',
+       'E3',
+       'FA',
+];
+
+const weakEG = [
+       '07',
+       '0A',
+       '16',
+       '28',
+       '2A',
+       '2B',
+       '34',
+       '35',
+       '36',
+       '37',
+       '3A',
+       '4D',
+       '55',
+       '61',
+       '76',
+       '99',
+       'A0',
+       'C9',
+       'E2',
+       'E4',
+       'F0',
+       'FD',
+       'FE',
+];
+
+const kick = [
+       '1B',
+       '29',
+       '3E',
+       '43',
+       '97',
+];
+
+const dark = [
+       '0B',
+       '19',
+       '21',
+       '22',
+       '32',
+       '41',
+       '42',
+       '69',
+       '6A',
+       '92',
+       '93',
+       'B5',
+       'BA',
+       'C0',
+       'D0',
+       'E5',
+       'E6',
+       'E7',
+       'F0',
+       'F1',
+];
+
+const camera = {
+       '00': ['x', 'y'],
+       '01': ['y'],
+       '02': ['y'],
+       '07': ['x', 'y'],
+       '0A': ['x', 'y'],
+       '0C': ['x', 'y'],
+       '0D': ['x', 'y'],
+       '0E': ['yu'],
+       '10': ['x'],
+       '11': ['x'],
+       '12': ['x', 'y'],
+       '13': ['x'],
+       '14': ['x', 'y'],
+       '15': ['x', 'y'],
+       '16': ['y'],
+       '17': ['x', 'y'],
+       '18': ['x'],
+       '19': ['xr'],
+       '1A': ['xl'],
+       '1B': ['yu'],
+       '1D': ['y'],
+       '1F': ['yu'],
+       '20': ['x', 'y'],
+       '21': ['y'],
+       '22': ['y'],
+       '26': ['yd'],
+       '27': ['x', 'y'],
+       '28': ['x', 'y'],
+       '2A': ['x', 'y'],
+       '2B': ['xl'],
+       '2F': ['yd'],
+       '31': ['yu'],
+       '32': ['x', 'y'],
+       '34': ['x', 'y'],
+       '35': ['y'],
+       '36': ['x', 'y'],
+       '37': ['y'],
+       '38': ['x'],
+       '39': ['yu'],
+       '3A': ['x', 'y'],
+       '3B': ['x'],
+       '3C': ['x', 'y'],
+       '3E': ['yd'],
+       '3F': ['yu'],
+       '40': ['xl'],
+       '41': ['x', 'y'],
+       '42': ['y'],
+       '43': ['yu'],
+       '44': ['xr'],
+       '45': ['xr'],
+       '46': ['y'],
+       '49': ['xr'],
+       '4A': ['y'],
+       '4B': ['yd'],
+       '4C': ['x'],
+       '4D': ['x', 'y'],
+       '4E': ['yd'],
+       '50': ['x'],
+       '51': ['x', 'y'],
+       '52': ['xl', 'yd'],
+       '53': ['xr'],
+       '54': ['x', 'y'],
+       '55': ['y'],
+       '56': ['xr'],
+       '58': ['xr'],
+       '59': ['x'],
+       '5B': ['x'],
+       '5C': ['yu'],
+       '60': ['x'],
+       '61': ['x', 'y'],
+       '62': ['x', 'y'],
+       '63': ['xr'],
+       '64': ['yu'],
+       '65': ['yu'],
+       '66': ['yd'],
+       '67': ['x'],
+       '68': ['x', 'y'],
+       '6A': ['x'],
+       '6B': ['yu'],
+       '6D': ['xr'],
+       '72': ['y'],
+       '74': ['y'],
+       '75': ['xr'],
+       '76': ['xl'],
+       '77': ['x', 'y'],
+       '7B': ['yu'],
+       '7C': ['x'],
+       '7D': ['yu'],
+       '7E': ['xr'],
+       '7F': ['xr'],
+       '80': ['y'],
+       '81': ['x', 'y'],
+       '82': ['x', 'y'],
+       '83': ['xr'],
+       '84': ['x', 'y'],
+       '85': ['xl'],
+       '89': ['y'],
+       '8B': ['xl'],
+       '8D': ['xr'],
+       '91': ['x'],
+       '92': ['xr'],
+       '93': ['yu'],
+       '95': ['x'],
+       '96': ['xl'],
+       '97': ['xr'],
+       '98': ['yd'],
+       '99': ['yd'],
+       '9B': ['yd'],
+       '9C': ['x', 'y'],
+       '9D': ['yd'],
+       'A0': ['y'],
+       'A1': ['xr', 'yu'],
+       'A2': ['x', 'y'],
+       'A3': ['x'],
+       'A5': ['yd'],
+       'A6': ['x', 'y'],
+       'A7': ['yd'],
+       'A8': ['xr'],
+       'A9': ['x', 'y'],
+       'AA': ['xl'],
+       'B1': ['xr'],
+       'B2': ['yu'],
+       'B3': ['x'],
+       'B4': ['x', 'y'],
+       'B5': ['x', 'y'],
+       'B7': ['x'],
+       'B8': ['x'],
+       'B9': ['x', 'y'],
+       'BB': ['xl'],
+       'BC': ['xr'],
+       'BE': ['xl'],
+       'C0': ['xl'],
+       'C2': ['x', 'y'],
+       'C3': ['x'],
+       'C4': ['x', 'y'],
+       'C5': ['x'],
+       'C6': ['x', 'y'],
+       'C7': ['x', 'y'],
+       'C9': ['y'],
+       'CB': ['x', 'y'],
+       'CC': ['x', 'y'],
+       'D0': ['xl'],
+       'D2': ['xr'],
+       'D5': ['x'],
+       'D6': ['x'],
+       'D8': ['xl'],
+       'D9': ['yu'],
+       'DA': ['yu'],
+       'DB': ['x', 'y'],
+       'DC': ['x', 'y'],
+       'DF': ['yd'],
+       'E1': ['x'],
+       'E2': ['x'],
+       'E4': ['x'],
+       'E5': ['x', 'y'],
+       'E6': ['xr', 'yd'],
+       'E7': ['xr', 'yu'],
+       'E8': ['x', 'yu'],
+       'EA': ['xl', 'yu'],
+       'EB': ['x'],
+       'ED': ['x', 'yu'],
+       'EE': ['x', 'y'],
+       'EF': ['yd'],
+       'F0': ['x', 'y'],
+       'F1': ['x', 'y'],
+       'F8': ['x', 'y'],
+       'F9': ['xr', 'yd'],
+       'FA': ['x', 'y'],
+       'FB': ['xr', 'yd'],
+       'FD': ['x', 'y'],
+       'FE': ['xr'],
+       'FF': ['yd'],
+};
+
+const getClassName = key => {
+       const classNames = [];
+       if (strongEG.includes(key)) {
+               classNames.push('strong-eg');
+       }
+       if (weakEG.includes(key)) {
+               classNames.push('weak-eg');
+       }
+       if (kick.includes(key)) {
+               classNames.push('kick');
+       }
+       if (dark.includes(key)) {
+               classNames.push('dark');
+       }
+       if (camera[key]) {
+               camera[key].forEach(c => {
+                       classNames.push(`cam-${c}`);
+               });
+       }
+       return classNames.join(' ');
+};
+
+const UWSuperTiles = ({ show }) => {
+       const { viewer } = useOpenSeadragon();
+
+       const onClick = React.useCallback(e => {
+               if (e.originalTarget.tagName !== 'A') return;
+               if (e.originalTarget.className !== 'cell-link') return;
+               const key = e.originalTarget.dataset.key;
+
+               const x = (parseInt(key[1], 16) + 0.5) / 16;
+               const y = (parseInt(key[0], 16) + 0.5) / 16;
+               if (viewer && viewer.viewport) {
+                       viewer.viewport.panTo(new OpenSeadragon.Point(x, y));
+                       viewer.viewport.zoomTo(4);
+                       viewer.element.scrollIntoView();
+               }
+       }, [viewer]);
+
+       return <Overlay onClick={onClick} page={3} x={0} y={0} width={1} height={1}>
+               <div className={`uw-super-tiles ${show ? '' : 'd-none'}`}>
+                       {[...Array(16).keys()].map(x =>
+                               [...Array(16).keys()].map(y => {
+                                       const key = `${x.toString(16).toUpperCase()}${y.toString(16).toUpperCase()}`;
+                                       return <div className={getClassName(key)} key={key}>
+                                               <p className="cell-id">{key}</p>
+                                               {dropMap[key] ?
+                                                       <p className="cell-drop">
+                                                               <a className="cell-link" data-key={dropMap[key]}>
+                                                                       {`▶ ${dropMap[key]}`}
+                                                               </a>
+                                                       </p>
+                                               : null}
+                                       </div>;
+                               })
+                       )}
+               </div>
+       </Overlay>;
+};
+
+UWSuperTiles.propTypes = {
+       show: PropTypes.bool,
+};
+
+export default UWSuperTiles;
index cbd3581d8d4a90c1266d2c74a4bff2451a1fcc95..b27560261194512b99000b6ac389c23c349983a1 100644 (file)
@@ -7,8 +7,11 @@ import Buttons from '../map/Buttons';
 import List from '../map/List';
 import OpenSeadragon from '../map/OpenSeadragon';
 import Pins from '../map/Pins';
+import UWSuperTiles from '../map/UWSuperTiles';
 
 const Map = () => {
+       const [uwOverlay, setUWOverlay] = React.useState(false);
+
        const container = React.useRef();
        const { t } = useTranslation();
 
@@ -18,12 +21,13 @@ const Map = () => {
                        <meta name="description" content={t('map.description')} />
                </Helmet>
                <OpenSeadragon ref={container}>
-                       <div className="d-flex align-items-center justify-content-between">
+                       <div className="d-flex align-items-start justify-content-between">
                                <h1>{t('map.heading')}</h1>
-                               <Buttons />
+                               <Buttons setUWOverlay={setUWOverlay} uwOverlay={uwOverlay} />
                        </div>
                        <div ref={container} style={{ height: '80vh' }} />
                        <Pins />
+                       <UWSuperTiles show={uwOverlay} />
                        <List />
                </OpenSeadragon>
        </Container>;
index fef88f452d8319fd3455bdb491f102813aeb36f8..6ec3c39d65faf9b948e254ef6b3f4c312e0cbcb5 100644 (file)
@@ -391,6 +391,7 @@ export default {
                        spLong: 'Spezielle Gebiete',
                        spShort: 'SP',
                        uwLong: 'Underworld',
+                       uwOverlay: 'Gitter zigen',
                        uwShort: 'UW',
                },
                menu: {
index 6820f30a3b08190e83f79a55fcbd2393cda20198..5e9668de0f5b0e0ca94ae18f083f6384bca188ff 100644 (file)
@@ -391,6 +391,7 @@ export default {
                        spLong: 'Special Areas',
                        spShort: 'SP',
                        uwLong: 'Underworld',
+                       uwOverlay: 'Show grid',
                        uwShort: 'UW',
                },
                menu: {
index 38376a1b4429b47363036a5a802e08dfde1a8e7d..3f8ae067d65ed629b4e6157ef051c07074ae4804 100644 (file)
                text-decoration: none;
        }
 }
+
+.uw-super-tiles {
+       display: grid;
+       height: 100%;
+       grid-template-columns: repeat(16, 1fr);
+
+       > div {
+               position: relative;
+               border: thin solid black;
+               padding: .25ex;
+
+               &.dark {
+                       border: medium solid black;
+               }
+               &.kick {
+                       border: medium solid yellow;
+               }
+               &.weak-eg {
+                       border: medium solid orange;
+               }
+               &.strong-eg {
+                       border: medium solid green;
+               }
+
+               &::before,
+               &::after {
+                       position: absolute;
+                       top: 0;
+                       left: 0;
+                       width: 100%;
+                       height: 100%;
+                       pointer-events: none;
+               }
+
+               &.cam-x::before {
+                       content: '';
+                       height: 50%;
+                       border-bottom: thin solid cyan;
+               }
+               &.cam-xl::before {
+                       content: '';
+                       position: absolute;
+                       width: 50%;
+                       height: 50%;
+                       border-bottom: thin solid cyan;
+               }
+               &.cam-xr::before {
+                       content: '';
+                       position: absolute;
+                       left: 50%;
+                       width: 50%;
+                       height: 50%;
+                       border-bottom: thin solid cyan;
+               }
+               &.cam-y::after {
+                       content: '';
+                       position: absolute;
+                       width: 50%;
+                       border-right: thin solid cyan;
+               }
+               &.cam-yu::after {
+                       content: '';
+                       position: absolute;
+                       width: 50%;
+                       height: 50%;
+                       border-right: thin solid cyan;
+               }
+               &.cam-yd::after {
+                       content: '';
+                       position: absolute;
+                       top: 50%;
+                       width: 50%;
+                       height: 50%;
+                       border-right: thin solid cyan;
+               }
+       }
+
+       .cell-id,
+       .cell-drop {
+               margin: 0;
+               line-height: 1;
+       }
+       .cell-drop {
+               font-size: 80%;
+       }
+       .cell-link {
+               color: inherit;
+               cursor: pointer;
+               text-decoration: none;
+       }
+}