]> git.localhorst.tv Git - alttp.git/commitdiff
merge manual and computed state
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 26 Mar 2024 12:07:37 +0000 (13:07 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 26 Mar 2024 12:07:37 +0000 (13:07 +0100)
resources/js/components/tracker/AutoTracking.js
resources/js/components/tracker/Equipment.js
resources/js/components/tracker/Map.js
resources/js/components/tracker/ToggleIcon.js
resources/js/components/tracker/index.js
resources/js/helpers/tracker.js
resources/js/hooks/tracker.js
resources/sass/tracker.scss

index 8528856bf430ef3901817fa53478092d3620d3da..dfa1972f6c020561c8e3fadc635b71f2348a4935 100644 (file)
@@ -11,7 +11,7 @@ import {
        WRAM_ADDR,
        buildPrizeMap,
 } from '../../helpers/alttp-ram';
-import { computeState, mergeStates } from '../../helpers/tracker';
+import { computeState } from '../../helpers/tracker';
 import { useSNES } from '../../hooks/snes';
 import { useTracker } from '../../hooks/tracker';
 
@@ -26,7 +26,7 @@ const AutoTracking = () => {
                sock,
                status,
        } = useSNES();
-       const { config, setState } = useTracker();
+       const { config, setAutoState } = useTracker();
        const { t } = useTranslation();
 
        const enable = React.useCallback(() => {
@@ -74,8 +74,8 @@ const AutoTracking = () => {
                        const saveStart = WRAM_ADDR.SAVE_DATA;
                        const saveSize = SRAM_ADDR.INV_END;
                        sock.current.readWRAM(saveStart, saveSize, (data) => {
-                               const computed = computeState(data, prizeMap);
-                               setState(s => mergeStates(config, s, computed));
+                               const computed = computeState(config, data, prizeMap);
+                               setAutoState(computed);
                        });
                };
                const fetchPrizes = () => {
@@ -118,7 +118,7 @@ const AutoTracking = () => {
                return 'tracking';
        }, [enabled, status]);
 
-       return <div>
+       return <div className="auto-tracking">
                {['disconnected', 'error', 'no-device'].includes(statusMsg) ?
                        <Icon.WARNING
                                className="me-2 text-warning"
index bfb9b20d3245ffcff2b0b4befc4fb73776737dc3..88778300acfdd102cf9f3345b7ad477afab0d831 100644 (file)
@@ -1,12 +1,8 @@
 import React from 'react';
 
-import CountDisplay from './CountDisplay';
 import ToggleIcon from './ToggleIcon';
-import { useTracker } from '../../hooks/tracker';
 
 const Equipment = () => {
-       const { state } = useTracker();
-
        return <div className="equipment">
                <div className="item">
                        <ToggleIcon controller={ToggleIcon.simpleController} icons={['boots']} />
index cfae29c4b4a4875e0c71e087b7f7456ee511d014..b77612ef65bd02ae9eb481df3c8e90b28daf4bb1 100644 (file)
@@ -686,7 +686,7 @@ const makeBackground = (src, level) => {
 };
 
 const Map = () => {
-       const { dungeons, setState, state } = useTracker();
+       const { dungeons, setManualState, state } = useTracker();
 
        const mapDungeon = React.useCallback(dungeon => {
                const definition = dungeons.find(d => d.id === dungeon.id);
@@ -701,51 +701,51 @@ const Map = () => {
                        remaining,
                        handlePrimary: () => {
                                if (getDungeonRemainingItems(state, definition)) {
-                                       setState(addDungeonCheck(definition));
+                                       setManualState(addDungeonCheck(definition));
                                } else if (
                                        !hasDungeonBoss(state, definition) || !hasDungeonPrize(state, definition)
                                ) {
                                        if (definition.boss) {
-                                               setState(setBossDefeated(definition, true));
+                                               setManualState(setBossDefeated(definition, true));
                                        }
                                        if (definition.prize) {
-                                               setState(setPrizeAcquired(definition, true));
+                                               setManualState(setPrizeAcquired(definition, true));
                                        }
                                } else {
-                                       setState(resetDungeonChecks(definition));
+                                       setManualState(resetDungeonChecks(definition));
                                        if (definition.boss) {
-                                               setState(setBossDefeated(definition, false));
+                                               setManualState(setBossDefeated(definition, false));
                                        }
                                        if (definition.prize) {
-                                               setState(setPrizeAcquired(definition, false));
+                                               setManualState(setPrizeAcquired(definition, false));
                                        }
                                }
                        },
                        handleSecondary: () => {
                                if (isDungeonCleared(state, definition)) {
                                        if (definition.items) {
-                                               setState(removeDungeonCheck(definition));
+                                               setManualState(removeDungeonCheck(definition));
                                        }
                                        if (definition.boss) {
-                                               setState(setBossDefeated(definition, false));
+                                               setManualState(setBossDefeated(definition, false));
                                        }
                                        if (definition.prize) {
-                                               setState(setPrizeAcquired(definition, false));
+                                               setManualState(setPrizeAcquired(definition, false));
                                        }
                                } else if (getDungeonClearedItems(state, definition)) {
-                                       setState(removeDungeonCheck(definition));
+                                       setManualState(removeDungeonCheck(definition));
                                } else {
-                                       setState(completeDungeonChecks(definition));
+                                       setManualState(completeDungeonChecks(definition));
                                        if (definition.boss) {
-                                               setState(setBossDefeated(definition, true));
+                                               setManualState(setBossDefeated(definition, true));
                                        }
                                        if (definition.prize) {
-                                               setState(setPrizeAcquired(definition, true));
+                                               setManualState(setPrizeAcquired(definition, true));
                                        }
                                }
                        },
                };
-       }, [dungeons, setState, state]);
+       }, [dungeons, setManualState, state]);
 
        const mapLocation = React.useCallback(loc => {
                const remaining = countRemainingLocations(state, loc.checks);
@@ -759,20 +759,20 @@ const Map = () => {
                        status,
                        handlePrimary: () => {
                                if (remaining) {
-                                       setState(clearAll(loc.checks));
+                                       setManualState(clearAll(loc.checks));
                                } else {
-                                       setState(unclearAll(loc.checks));
+                                       setManualState(unclearAll(loc.checks));
                                }
                        },
                        handleSecondary: () => {
                                if (remaining) {
-                                       setState(clearAll(loc.checks));
+                                       setManualState(clearAll(loc.checks));
                                } else {
-                                       setState(unclearAll(loc.checks));
+                                       setManualState(unclearAll(loc.checks));
                                }
                        },
                };
-       }, [setState, state]);
+       }, [setManualState, state]);
 
        const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]);
        const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]);
index f7cc0744b81f22619634fecfc047cf66451b098a..da1a18139e8f90bac57e1edb502b0f57e029caff 100644 (file)
@@ -19,7 +19,7 @@ import {
 import { useTracker } from '../../hooks/tracker';
 
 const ToggleIcon = ({ controller, className, icons }) => {
-       const { state, setState } = useTracker();
+       const { setManualState, state } = useTracker();
        const activeController = controller || ToggleIcon.nullController;
        const active = activeController.getActive(state, icons);
        const defaultIcon = activeController.getDefault(state, icons);
@@ -35,12 +35,12 @@ const ToggleIcon = ({ controller, className, icons }) => {
        return <span
                className={classNames.join(' ')}
                onClick={(e) => {
-                       activeController.handlePrimary(state, setState, icons);
+                       activeController.handlePrimary(state, setManualState, icons);
                        e.preventDefault();
                        e.stopPropagation();
                }}
                onContextMenu={(e) => {
-                       activeController.handleSecondary(state, setState, icons);
+                       activeController.handleSecondary(state, setManualState, icons);
                        e.preventDefault();
                        e.stopPropagation();
                }}
index 9cf88b497f3d998b9cc6bda37e888e92fa651301..ea09a22dad6b17fcabc91235981352157048ccc7 100644 (file)
@@ -11,8 +11,10 @@ const Tracker = () => {
                <Toolbar />
                <div className="d-flex">
                        <div>
-                               <Items />
-                               <Equipment />
+                               <div className="inventory">
+                                       <Items />
+                                       <Equipment />
+                               </div>
                                <Dungeons />
                        </div>
                        <div className="flex-fill">
index bc18847fcc10e47ad7f0a8c8405b64ce0323a37b..2bc477bff1b632813829f7d4a3ee944d62e6fb57 100644 (file)
@@ -1765,11 +1765,15 @@ const collectUnderworld = (state, data) => {
        });
 };
 
-export const computeState = (data, prizeMap) => {
+export const computeState = (config, data, prizeMap) => {
        const state = {};
        collectInventory(state, data.slice(SRAM_ADDR.INV_START), prizeMap);
        collectOverworld(state, data);
        collectUnderworld(state, data.slice(SRAM_ADDR.ROOM_DATA_START));
+       const amounts = getDungeonAmounts(config, state);
+       DUNGEONS.forEach(dungeon => {
+               state[`${dungeon.id}-checks`] = amounts[dungeon.id];
+       });
        return state;
 };
 
@@ -1804,12 +1808,54 @@ const getDungeonAmounts = (config, state) => {
        return amounts;
 };
 
-export const mergeStates = (config, cur, inc) => {
-       const next = { ...cur, ...inc };
-       const amounts = getDungeonAmounts(config, inc);
+export const mergeStates = (autoState, manualState) => {
+       const next = { ...autoState };
+       BOOLEAN_STATES.forEach(name => {
+               if (manualState[name]) {
+                       next[name] = true;
+               }
+       });
+       INTEGER_STATES.forEach(name => {
+               next[name] = Math.max(autoState[name] || 0, manualState[name] || 0);
+       });
        DUNGEONS.forEach(dungeon => {
-               next[`${dungeon.id}-checks`] = amounts[dungeon.id];
+               next[`${dungeon.id}-small-key`] += manualState[`${dungeon.id}-small-key`] || 0;
+               next[`${dungeon.id}-checks`] += manualState[`${dungeon.id}-checks`] || 0;
+               if (manualState[`${dungeon.id}-big-key`]) {
+                       next[`${dungeon.id}-big-key`] = true;
+               }
+               if (manualState[`${dungeon.id}-compass`]) {
+                       next[`${dungeon.id}-compass`] = true;
+               }
+               if (manualState[`${dungeon.id}-map`]) {
+                       next[`${dungeon.id}-map`] = true;
+               }
+               if (manualState[`${dungeon.id}-boss-defeated`]) {
+                       next[`${dungeon.id}-boss-defeated`] = true;
+               }
+               if (manualState[`${dungeon.id}-prize`] &&
+                       manualState[`${dungeon.id}-prize`] !== 'crystal'
+               ) {
+                       next[`${dungeon.id}-prize`] = manualState[`${dungeon.id}-prize`];
+               } else if (!next[`${dungeon.id}-prize`]) {
+                       next[`${dungeon.id}-prize`] = 'crystal';
+               }
+               if (manualState[`${dungeon.id}-prize-acquired`]) {
+                       next[`${dungeon.id}-prize-acquired`] = true;
+               }
+       });
+       OVERWORLD_LOCATIONS.forEach(loc => {
+               if (manualState[loc.id]) {
+                       next[loc.id] = true;
+               }
+       });
+       UNDERWORLD_LOCATIONS.forEach(loc => {
+               if (manualState[loc.id]) {
+                       next[loc.id] = true;
+               }
        });
+       next['mm-medallion'] = manualState['mm-medallion'];
+       next['tr-medallion'] = manualState['tr-medallion'];
        //console.log(next);
        return next;
 };
index be5a52f063604aad4eaeb7d88dcd650df2c4f87d..249a34bfe283bb8dfb000812c43d8d86cbaacfa8 100644 (file)
@@ -1,7 +1,7 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 
-import { CONFIG, DUNGEONS, makeEmptyState } from '../helpers/tracker';
+import { CONFIG, DUNGEONS, makeEmptyState, mergeStates } from '../helpers/tracker';
 
 const context = React.createContext({});
 
@@ -10,6 +10,8 @@ export const useTracker = () => React.useContext(context);
 export const TrackerProvider = ({ children }) => {
        const [config, setConfig] = React.useState(CONFIG);
        const [state, setState] = React.useState(makeEmptyState());
+       const [autoState, setAutoState] = React.useState(makeEmptyState());
+       const [manualState, setManualState] = React.useState(makeEmptyState());
        const [dungeons, setDungeons] = React.useState(DUNGEONS);
 
        React.useEffect(() => {
@@ -35,8 +37,12 @@ export const TrackerProvider = ({ children }) => {
                setDungeons(newDungeons);
        }, [config]);
 
+       React.useEffect(() => {
+               setState(mergeStates(autoState, manualState));
+       }, [autoState, manualState]);
+
        const value = React.useMemo(() => {
-               return { config, setConfig, dungeons, setState, state };
+               return { config, setConfig, dungeons, setAutoState, setManualState, state };
        }, [config, dungeons, state]);
 
        return <context.Provider value={value}>
index 5bc5a4696f870ea53e53157edcf8f8f27f6d2aaa..eae60c3586d0992746148feee5adababa5dc7cec 100644 (file)
@@ -1,4 +1,9 @@
 .tracker {
+       .auto-tracking {
+               .custom-toggle {
+                       vertical-align: middle;
+               }
+       }
        .count-display,
        .med-display {
                background: black;
@@ -39,6 +44,9 @@
                        }
                }
        }
+       .dungeons {
+               padding: 1ex;
+       }
        .dungeon-ep,
        .dungeon-pd {
                margin-top: 1ex;
@@ -49,6 +57,9 @@
                gap: 1ex;
                padding: 1ex;
        }
+       .inventory {
+               font-size: 110%;
+       }
        .items {
                display: grid;
                grid-template-columns: 3em 3em 3em 3em 3em;