]> git.localhorst.tv Git - alttp.git/commitdiff
tracker layout
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 4 Apr 2024 17:42:45 +0000 (19:42 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 4 Apr 2024 17:42:45 +0000 (19:42 +0200)
resources/js/components/tracker/Canvas.js [new file with mode: 0644]
resources/js/components/tracker/Dungeons.js
resources/js/components/tracker/Items.js
resources/js/components/tracker/Map/Overworld.js
resources/js/components/tracker/Map/index.js
resources/js/components/tracker/index.js
resources/js/helpers/tracker.js
resources/sass/tracker.scss

diff --git a/resources/js/components/tracker/Canvas.js b/resources/js/components/tracker/Canvas.js
new file mode 100644 (file)
index 0000000..392f2a7
--- /dev/null
@@ -0,0 +1,83 @@
+import React from 'react';
+
+import Dungeons from './Dungeons';
+import Items from './Items';
+import Map from './Map';
+import { shouldShowDungeonItem } from '../../helpers/tracker';
+import { useTracker } from '../../hooks/tracker';
+
+const LAYOUTS = {
+       defaultHorizontal: {
+               width: 100,
+               height: 60,
+               itemsTransform: 'translate(1 1) scale(22)',
+               dungeonColumns: 4,
+               dungeonsTransform: 'translate(1 39) scale(98)',
+               mapTransform: 'translate(24 0) scale(76)',
+       },
+       defaultVertical: {
+               width: 100,
+               height: 100,
+               itemsTransform: 'translate(10 1) scale(30)',
+               dungeonColumns: 2,
+               dungeonsTransform: 'translate(1 51) scale(48)',
+               mapTransform: 'translate(50 0) scale(50)',
+       },
+       manyDungeonItemsVertical: {
+               width: 80,
+               height: 100,
+               itemsTransform: 'translate(1 1) scale(27)',
+               dungeonColumns: 1,
+               dungeonsTransform: 'translate(1 48) scale(24)',
+               mapTransform: 'translate(30 0) scale(50)',
+       },
+};
+
+const Canvas = () => {
+       const { config } = useTracker();
+
+       const layout = React.useMemo(() => {
+               if (config.mapLayout === 'vertical') {
+                       let count = 0;
+                       if (shouldShowDungeonItem(config, 'Map')) {
+                               ++count;
+                       }
+                       if (shouldShowDungeonItem(config, 'Compass')) {
+                               ++count;
+                       }
+                       if (shouldShowDungeonItem(config, 'Small')) {
+                               ++count;
+                       }
+                       if (shouldShowDungeonItem(config, 'Big')) {
+                               ++count;
+                       }
+                       return count > 2 ? LAYOUTS.manyDungeonItemsVertical : LAYOUTS.defaultVertical;
+               } else {
+                       return LAYOUTS.defaultHorizontal;
+               }
+       }, [config]);
+
+       return <svg
+               xmlns="http://www.w3.org/2000/svg"
+               className="canvas"
+               width={layout.width}
+               height={layout.height}
+               viewBox={`0 0 ${layout.width} ${layout.height}`}
+               onContextMenu={(e) => {
+                       e.preventDefault();
+                       e.stopPropagation();
+               }}
+       >
+               <g className="items" transform={layout.itemsTransform}>
+                       <Items />
+               </g>
+               <g className="dungeons" transform={layout.dungeonsTransform}>
+                       <Dungeons columns={layout.dungeonColumns} />
+               </g>
+               <g className="tracker-map" transform={layout.mapTransform}>
+                       <Map />
+               </g>
+       </svg>;
+};
+
+export default Canvas;
index 4890bbbc6afbb433a38c282c0bf33b63cdc2d571..9c6e122f382f6792bae71f5e7fd99febe4da5900 100644 (file)
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
 import React from 'react';
 
 import CountDisplay from './CountDisplay';
@@ -10,7 +11,7 @@ import {
 } from '../../helpers/tracker';
 import { useTracker } from '../../hooks/tracker';
 
-const Dungeons = () => {
+const Dungeons = ({ columns }) => {
        const { config, dungeons, state } = useTracker();
 
        const layout = React.useMemo(() => {
@@ -21,65 +22,86 @@ const Dungeons = () => {
                const countX = shouldShowDungeonItem(config, 'Big') ? bigX + 1 : bigX;
                const bossX = countX + 1;
                const prizeX = bossX + 1;
-               const dungeonWidth = prizeX + 1;
-               const width = (4 * dungeonWidth) + 2;
+               const dungeonWidth = Math.max(5, prizeX + 1);
+               const width = (columns * dungeonWidth) + Math.max(0, columns - 1);
                const height = 4;
-               const viewBox = `0 0 ${width} ${height}`;
+
+               const transform = (col, row) =>
+                       `scale(${1 / width}) translate(${
+                               (col * dungeonWidth) + 0.5 + (col ? col - (columns > 3 ? 1 : 0) : 0)
+                       } ${row + 0.5})`;
+
+               const transforms = {
+                       tag: '',
+                       map: `translate(${mapX} 0) scale(0.9)`,
+                       compass: `translate(${compassX} 0) scale(0.9)`,
+                       small: `translate(${smallX} 0) scale(0.9)`,
+                       big: `translate(${bigX} 0) scale(0.9)`,
+                       checks: `translate(${countX} 0) scale(0.9)`,
+                       boss: `translate(${bossX} 0)`,
+                       prize: `translate(${prizeX} 0)`,
+                       hc: transform(0, 0),
+                       ct: transform(0, 1),
+                       gt: transform(0, 2),
+                       gtBoss1: `translate(${bossX - 2} 1)`,
+                       gtBoss2: `translate(${bossX - 1} 1)`,
+                       gtBoss3: `translate(${bossX} 1)`,
+               };
+
+               if (columns === 1) {
+                       transforms.ep = transform(0, 4);
+                       transforms.dp = transform(0, 5);
+                       transforms.th = transform(0, 6);
+                       transforms.pd = transform(0, 8);
+                       transforms.sp = transform(0, 9);
+                       transforms.sw = transform(0, 10);
+                       transforms.tt = transform(0, 11);
+                       transforms.ip = transform(0, 12);
+                       transforms.mm = transform(0, 13);
+                       transforms.tr = transform(0, 14);
+               } else if (columns === 2) {
+                       transforms.ep = transform(0, 4);
+                       transforms.dp = transform(0, 5);
+                       transforms.th = transform(0, 6);
+                       transforms.pd = transform(1, 0);
+                       transforms.sp = transform(1, 1);
+                       transforms.sw = transform(1, 2);
+                       transforms.tt = transform(1, 3);
+                       transforms.ip = transform(1, 4);
+                       transforms.mm = transform(1, 5);
+                       transforms.tr = transform(1, 6);
+               } else {
+                       transforms.ep = transform(1, 0);
+                       transforms.dp = transform(1, 1);
+                       transforms.th = transform(1, 2);
+                       transforms.pd = transform(2, 0);
+                       transforms.sp = transform(2, 1);
+                       transforms.sw = transform(2, 2);
+                       transforms.tt = transform(2, 3);
+                       transforms.ip = transform(3, 0);
+                       transforms.mm = transform(3, 1);
+                       transforms.tr = transform(3, 2);
+               }
+
                return {
                        width,
                        height,
-                       viewBox,
-                       transform: {
-                               tag: '',
-                               map: `translate(${mapX} 0) scale(0.9)`,
-                               compass: `translate(${compassX} 0) scale(0.9)`,
-                               small: `translate(${smallX} 0) scale(0.9)`,
-                               big: `translate(${bigX} 0) scale(0.9)`,
-                               checks: `translate(${countX} 0) scale(0.9)`,
-                               boss: `translate(${bossX} 0)`,
-                               prize: `translate(${prizeX} 0)`,
-                               hc: 'translate(0.5, 0.5)',
-                               ct: 'translate(0.5, 1.5)',
-                               gt: 'translate(0.5, 2.5)',
-                               gtBoss1: `translate(${bossX - 2} 1)`,
-                               gtBoss2: `translate(${bossX - 1} 1)`,
-                               gtBoss3: `translate(${bossX} 1)`,
-                               ep: `translate(${dungeonWidth + 0.5}, 0.5)`,
-                               dp: `translate(${dungeonWidth + 0.5}, 1.5)`,
-                               th: `translate(${dungeonWidth + 0.5}, 2.5)`,
-                               pd: `translate(${(2 * dungeonWidth) + 1.5}, 0.5)`,
-                               sp: `translate(${(2 * dungeonWidth) + 1.5}, 1.5)`,
-                               sw: `translate(${(2 * dungeonWidth) + 1.5}, 2.5)`,
-                               tt: `translate(${(2 * dungeonWidth) + 1.5}, 3.5)`,
-                               ip: `translate(${(3 * dungeonWidth) + 2.5}, 0.5)`,
-                               mm: `translate(${(3 * dungeonWidth) + 2.5}, 1.5)`,
-                               tr: `translate(${(3 * dungeonWidth) + 2.5}, 2.5)`,
-                       },
+                       transforms,
                };
        }, [config, dungeons]);
 
-       return <svg
-               xmlns="http://www.w3.org/2000/svg"
-               className="dungeons"
-               width={layout.width}
-               height={layout.height}
-               viewBox={layout.viewBox}
-               onContextMenu={(e) => {
-                       e.preventDefault();
-                       e.stopPropagation();
-               }}
-       >
+       return <>
                {dungeons.map(dungeon =>
                        <g
                                className={`dungeon dungeon-${dungeon.id}`}
                                key={dungeon.id}
-                               transform={layout.transform[dungeon.id]}
+                               transform={layout.transforms[dungeon.id]}
                        >
-                               <g transform={layout.transform.tag}>
+                               <g transform={layout.transforms.tag}>
                                        <text className="dungeon-tag">{dungeon.id.toUpperCase()}</text>
                                </g>
                                {shouldShowDungeonItem(config, 'Map') ?
-                                       <g transform={layout.transform.map}>
+                                       <g transform={layout.transforms.map}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonController(dungeon)}
                                                        icons={['map']}
@@ -88,7 +110,7 @@ const Dungeons = () => {
                                        </g>
                                : null}
                                {shouldShowDungeonItem(config, 'Compass') ?
-                                       <g transform={layout.transform.compass}>
+                                       <g transform={layout.transforms.compass}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonController(dungeon)}
                                                        icons={['compass']}
@@ -97,7 +119,7 @@ const Dungeons = () => {
                                        </g>
                                : null}
                                {shouldShowDungeonItem(config, 'Small') ?
-                                       <g transform={layout.transform.small}>
+                                       <g transform={layout.transforms.small}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonCountController(dungeon, dungeon.sk)}
                                                        icons={['small-key']}
@@ -110,7 +132,7 @@ const Dungeons = () => {
                                        </g>
                                : null}
                                {shouldShowDungeonItem(config, 'Big') ?
-                                       <g transform={layout.transform.big}>
+                                       <g transform={layout.transforms.big}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonController(dungeon)}
                                                        icons={['big-key']}
@@ -118,7 +140,7 @@ const Dungeons = () => {
                                                />
                                        </g>
                                : null}
-                               <g transform={layout.transform.checks}>
+                               <g transform={layout.transforms.checks}>
                                        <ToggleIcon
                                                controller={ToggleIcon.dungeonCheckController(dungeon)}
                                                icons={['open-chest', 'chest']}
@@ -127,7 +149,7 @@ const Dungeons = () => {
                                        <CountDisplay count={getDungeonRemainingItems(state, dungeon)} />
                                </g>
                                {dungeon.boss ?
-                                       <g transform={layout.transform.boss}>
+                                       <g transform={layout.transforms.boss}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonBossController(dungeon)}
                                                        icons={dungeon.bosses}
@@ -136,7 +158,7 @@ const Dungeons = () => {
                                        </g>
                                : null}
                                {dungeon.prize ?
-                                       <g transform={layout.transform.prize}>
+                                       <g transform={layout.transforms.prize}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.dungeonPrizeController(dungeon)}
                                                        icons={[
@@ -150,21 +172,21 @@ const Dungeons = () => {
                                        </g>
                                : null}
                                {dungeon.id === 'gt' && config.bossShuffle ? <>
-                                       <g transform={layout.transform.gtBoss1}>
+                                       <g transform={layout.transforms.gtBoss1}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.gtBossController('bot')}
                                                        icons={BOSSES}
                                                        svg
                                                />
                                        </g>
-                                       <g transform={layout.transform.gtBoss2}>
+                                       <g transform={layout.transforms.gtBoss2}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.gtBossController('mid')}
                                                        icons={BOSSES}
                                                        svg
                                                />
                                        </g>
-                                       <g transform={layout.transform.gtBoss3}>
+                                       <g transform={layout.transforms.gtBoss3}>
                                                <ToggleIcon
                                                        controller={ToggleIcon.gtBossController('top')}
                                                        icons={BOSSES}
@@ -174,7 +196,15 @@ const Dungeons = () => {
                                </> : null}
                        </g>
                )}
-       </svg>;
+       </>;
+};
+
+Dungeons.propTypes = {
+       columns: PropTypes.number,
+};
+
+Dungeons.defaultProps = {
+       columns: 4,
 };
 
 export default Dungeons;
index 9ec71e00223f6dbc5d670c357009144b1f32b78f..2a9c01a0affdc6620cc0615bc2eb57a49b0cf1a1 100644 (file)
@@ -4,22 +4,12 @@ import ToggleIcon from './ToggleIcon';
 import { BOTTLE_CONTENTS } from '../../helpers/tracker';
 import { useTracker } from '../../hooks/tracker';
 
-const transform = (x, y, s) => `translate(${x} ${y}) scale(${s || 0.85})`;
+const transform = (x, y, s) => `translate(${x * 0.2} ${y * 0.2}) scale(${(s || 0.85) * 0.2})`;
 
 const Items = () => {
        const { state } = useTracker();
 
-       return <svg
-               xmlns="http://www.w3.org/2000/svg"
-               className="items"
-               width="5"
-               height="8"
-               viewBox="0 0 5 8"
-               onContextMenu={(e) => {
-                       e.preventDefault();
-                       e.stopPropagation();
-               }}
-       >
+       return <>
                <g transform={transform(0.5, 0.5)}>
                        <ToggleIcon controller={ToggleIcon.simpleController} icons={['bow', 'silvers']} svg />
                </g>
@@ -186,7 +176,7 @@ const Items = () => {
                                svg
                        />
                </g>
-       </svg>;
+       </>;
 };
 
 export default Items;
index 66433e5e50770010bca2eeda03442c302b6584dd..f899593c1230278a0a6df5afa7cd058a85e6ebec 100644 (file)
@@ -836,34 +836,18 @@ const Overworld = () => {
        const layout = React.useMemo(() => {
                if (config.mapLayout === 'vertical') {
                        return {
-                               width: 1,
-                               height: 2,
-                               viewBox: '0 0 1 2',
                                lwTransform: '',
                                dwTransform: 'translate(0 1)',
                        };
                } else {
                        return {
-                               width: 2,
-                               height: 1,
-                               viewBox: '0 0 2 1',
-                               lwTransform: '',
-                               dwTransform: 'translate(1 0)',
+                               lwTransform: 'scale(0.5)',
+                               dwTransform: 'scale(0.5) translate(1 0)',
                        };
                }
        }, [config]);
 
-       return <svg
-               xmlns="http://www.w3.org/2000/svg"
-               className="canvas"
-               width={layout.width}
-               height={layout.height}
-               viewBox={layout.viewBox}
-               onContextMenu={(e) => {
-                       e.preventDefault();
-                       e.stopPropagation();
-               }}
-       >
+       return <>
                <g className="light-world" transform={layout.lwTransform}>
                        <g className="background">
                                {makeBackground('lw_files', 10)}
@@ -890,7 +874,7 @@ const Overworld = () => {
                                )}
                        </g>
                </g>
-       </svg>;
+       </>;
 };
 
 export default Overworld;
index 65dcbd34ead96bbe77efaa991932f0c0294e1c65..246403c1b22e2bbfe1d07d95b4840d4182b6793f 100644 (file)
@@ -3,9 +3,9 @@ import React from 'react';
 import Overworld from './Overworld';
 
 const Map = () => {
-       return <div className="tracker-map">
+       return <>
                <Overworld />
-       </div>;
+       </>;
 };
 
 export default Map;
index 7b16b4014a97801c02b73cfdc7417073888ae80b..68f79727dc0c3419c79079a01f43b34290868778 100644 (file)
@@ -1,27 +1,12 @@
 import React from 'react';
-import { Col, Container, Row } from 'react-bootstrap';
 
-import Dungeons from './Dungeons';
-import Items from './Items';
-import Map from './Map';
+import Canvas from './Canvas';
 import Toolbar from './Toolbar';
 
 const Tracker = () => {
        return <div className="tracker">
                <Toolbar />
-               <Container fluid>
-                       <Row>
-                               <Col sm={3}>
-                                       <Items />
-                               </Col>
-                               <Col sm={9}>
-                                       <Map />
-                               </Col>
-                       </Row>
-                       <div className="mt-2">
-                               <Dungeons />
-                       </div>
-               </Container>
+               <Canvas />
        </div>;
 };
 
index 06513cc8cbbde446bcac77f1da3650e603a8d086..c4b685c5b9ced45af45281b71e0728bae7aac022 100644 (file)
@@ -1619,7 +1619,7 @@ export const shouldShowDungeonItem = (config, which) => {
                case 'always':
                        return true;
                case 'situational':
-                       return wild;
+                       return wild || (which === 'Compass' && config.bossShuffle);
                case 'never':
                        return false;
        }
index ca20b6342d2893f47c77c694cf2a3ee7020c1df8..cf6cb6df77302212fcdb64902b93f2b04ac017f7 100644 (file)
@@ -1,12 +1,21 @@
 .tracker {
+       display: flex;
+       flex-direction: column;
+       height: 100vh;
+       align-items: stretch;
+       justify-content: flex-start;
+
        .auto-tracking {
                .custom-toggle {
                        vertical-align: middle;
                }
        }
-       .dungeons {
+       .canvas {
                width: 100%;
                height: auto;
+               max-height: 100%;
+       }
+       .dungeons {
                .count-display,
                .dungeon-tag {
                        font-size: 0.5px;
@@ -32,8 +41,6 @@
                }
        }
        .items {
-               width: 100%;
-               height: auto;
                .med-display {
                        font-size: 0.3px;
                        font-weight: bold;
                }
        }
        .tracker-map {
-               .canvas {
-                       width: 100%;
-                       height: auto;
-                       .location {
+               .location {
+                       .box {
+                               width: 0.04px;
+                               height: 0.04px;
+                               transform: translate(-0.02px, -0.02px);
+                               fill: #0d0;
+                               stroke: black;
+                               stroke-width: 1px;
+                               vector-effect: non-scaling-stroke;
+                       }
+                       .text {
+                               fill: black;
+                               font-size: 0.02px;
+                               font-weight: bold;
+                               text-anchor: middle;
+                               dominant-baseline: middle;
+                               pointer-events: none;
+                               user-select: none;
+                       }
+                       &.status-cleared {
                                .box {
-                                       width: 0.04px;
-                                       height: 0.04px;
-                                       transform: translate(-0.02px, -0.02px);
-                                       fill: #0d0;
-                                       stroke: black;
-                                       stroke-width: 1px;
-                                       vector-effect: non-scaling-stroke;
-                               }
-                               .text {
-                                       fill: black;
-                                       font-size: 0.02px;
-                                       font-weight: bold;
-                                       text-anchor: middle;
-                                       dominant-baseline: middle;
-                                       pointer-events: none;
-                                       user-select: none;
+                                       fill: grey;
+                                       opacity: 0.4;
                                }
-                               &.status-cleared {
-                                       .box {
-                                               fill: grey;
-                                               opacity: 0.4;
-                                       }
+                       }
+                       &.status-partial {
+                               .box {
+                                       fill: yellow;
                                }
-                               &.status-partial {
-                                       .box {
-                                               fill: yellow;
-                                       }
+                       }
+                       &.status-unavailable {
+                               .box {
+                                       fill: red;
                                }
-                               &.status-unavailable {
-                                       .box {
-                                               fill: red;
-                                       }
+                       }
+                       &.size-lg {
+                               .box {
+                                       width: 0.08px;
+                                       height: 0.08px;
+                                       transform: translate(-0.04px, -0.04px);
                                }
-                               &.size-lg {
-                                       .box {
-                                               width: 0.08px;
-                                               height: 0.08px;
-                                               transform: translate(-0.04px, -0.04px);
-                                       }
-                                       .text {
-                                               font-size: 0.04px;
-                                       }
+                               .text {
+                                       font-size: 0.04px;
                                }
-                               &.clickable {
-                                       .box:hover {
-                                               stroke: white;
-                                       }
+                       }
+                       &.clickable {
+                               .box:hover {
+                                       stroke: white;
                                }
                        }
                }