X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=resources%2Fjs%2Fcomponents%2Ftracker%2FMap.js;h=3ee4b2f81458ab4eeb6190273623467406788bc1;hb=1b1f6865fb04dcec9e5c55b876cc9ff25706de23;hp=d8e76364fb864a0f09def8cd0e1423a24ad30f83;hpb=f145023c506cecad618e465a034933e1a5962637;p=alttp.git diff --git a/resources/js/components/tracker/Map.js b/resources/js/components/tracker/Map.js index d8e7636..3ee4b2f 100644 --- a/resources/js/components/tracker/Map.js +++ b/resources/js/components/tracker/Map.js @@ -2,6 +2,24 @@ import PropTypes from 'prop-types'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { + addDungeonCheck, + aggregateDungeonStatus, + aggregateLocationStatus, + clearAll, + completeDungeonChecks, + countRemainingLocations, + getDungeonClearedItems, + getDungeonRemainingItems, + hasDungeonBoss, + hasDungeonPrize, + isDungeonCleared, + removeDungeonCheck, + resetDungeonChecks, + setBossDefeated, + setPrizeAcquired, + unclearAll, +} from '../../helpers/tracker'; import { useTracker } from '../../hooks/tracker'; const LW_DUNGEONS = [ @@ -314,7 +332,7 @@ const LW_LOCATIONS = [ 'saha', ], x: 0.815, - y: 0.42, + y: 0.465, }, { id: 'saha-hut', @@ -324,7 +342,7 @@ const LW_LOCATIONS = [ 'saha-right', ], x: 0.815, - y: 0.465, + y: 0.42, }, { id: 'sick-kid', @@ -601,70 +619,155 @@ const DW_LOCATIONS = [ }, ]; -const Location = ({ l, size }) => { +const Location = ({ number, l, size }) => { const { t } = useTranslation(); const classNames = ['location', `status-${l.status}`]; if (size) { classNames.push(`size-${size}`); } + if (l.handlePrimary) { + classNames.push('clickable'); + } - return + return { + l.handlePrimary(); + e.preventDefault(); + e.stopPropagation(); + }} + onContextMenu={(e) => { + l.handleSecondary(); + e.preventDefault(); + e.stopPropagation(); + }} + transform={`translate(${l.x} ${l.y})`} + > {t(`tracker.location.${l.id}`)} - ; + + {number && l.remaining ? + {l.remaining} + : null} + ; }; Location.propTypes = { + number: PropTypes.bool, l: PropTypes.shape({ id: PropTypes.string, x: PropTypes.number, y: PropTypes.number, - cleared: PropTypes.number, - total: PropTypes.number, + number: PropTypes.number, + remaining: PropTypes.number, status: PropTypes.string, + handlePrimary: PropTypes.func, + handleSecondary: PropTypes.func, }), size: PropTypes.string, }; +const makeBackground = (src, level) => { + const amount = Math.pow(2, Math.max(0, level - 8)); + const size = 1 / amount; + const tiles = []; + for (let y = 0; y < amount; ++y) { + for (let x = 0; x < amount; ++x) { + tiles.push(); + } + } + return tiles; +}; + const Map = () => { - const { dungeons, state } = useTracker(); + const { dungeons, logic, setManualState, state } = useTracker(); const mapDungeon = React.useCallback(dungeon => { const definition = dungeons.find(d => d.id === dungeon.id); - const cleared = state[`${dungeon.id}-checks`] || 0; - const total = definition.items; - let status = 'available'; - if (cleared === total) { - if (['ct', 'gt'].includes(dungeon.id)) { - if (state[`${dungeon.id}-boss-defeated`]) { - status = 'cleared'; - } - } else { - status = 'cleared'; - } - } + const remaining = getDungeonRemainingItems(state, definition); + const status = aggregateDungeonStatus(definition, logic, state); return { ...dungeon, status, - cleared, - total, + remaining, + handlePrimary: () => { + if (getDungeonRemainingItems(state, definition)) { + setManualState(addDungeonCheck(definition)); + } else if ( + !hasDungeonBoss(state, definition) || !hasDungeonPrize(state, definition) + ) { + if (definition.boss) { + setManualState(setBossDefeated(definition, true)); + } + if (definition.prize) { + setManualState(setPrizeAcquired(definition, true)); + } + } else { + setManualState(resetDungeonChecks(definition)); + if (definition.boss) { + setManualState(setBossDefeated(definition, false)); + } + if (definition.prize) { + setManualState(setPrizeAcquired(definition, false)); + } + } + }, + handleSecondary: () => { + if (isDungeonCleared(state, definition)) { + if (definition.items) { + setManualState(removeDungeonCheck(definition)); + } + if (definition.boss) { + setManualState(setBossDefeated(definition, false)); + } + if (definition.prize) { + setManualState(setPrizeAcquired(definition, false)); + } + } else if (getDungeonClearedItems(state, definition)) { + setManualState(removeDungeonCheck(definition)); + } else { + setManualState(completeDungeonChecks(definition)); + if (definition.boss) { + setManualState(setBossDefeated(definition, true)); + } + if (definition.prize) { + setManualState(setPrizeAcquired(definition, true)); + } + } + }, }; - }, [dungeons, state]); + }, [dungeons, logic, setManualState, state]); const mapLocation = React.useCallback(loc => { - const cleared = loc.checks.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0); - const total = loc.checks.length; - let status = 'available'; - if (cleared === total) { - status = 'cleared'; - } + const remaining = countRemainingLocations(state, loc.checks); + const status = aggregateLocationStatus(loc.checks, logic, state); return { ...loc, - cleared, - total, + remaining, status, + handlePrimary: () => { + if (remaining) { + setManualState(clearAll(loc.checks)); + } else { + setManualState(unclearAll(loc.checks)); + } + }, + handleSecondary: () => { + if (remaining) { + setManualState(clearAll(loc.checks)); + } else { + setManualState(unclearAll(loc.checks)); + } + }, }; - }, [state]); + }, [logic, setManualState, state]); const lwDungeons = React.useMemo(() => LW_DUNGEONS.map(mapDungeon), [mapDungeon]); const lwLocations = React.useMemo(() => LW_LOCATIONS.map(mapLocation), [mapLocation]); @@ -679,84 +782,34 @@ const Map = () => { width="2" height="1" viewBox="0 0 2 1" + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + }} > - - - - + {makeBackground('lw_files', 10)} {lwLocations.map(l => )} {lwDungeons.map(l => - + )} - - - - + {makeBackground('dw_files', 10)} {dwLocations.map(l => )} {dwDungeons.map(l => - + )}