import CountDisplay from './CountDisplay';
import ToggleIcon from './ToggleIcon';
+import {
+ getDungeonAcquiredSKs,
+ getDungeonRemainingItems,
+} from '../../helpers/tracker';
import { useTracker } from '../../hooks/tracker';
const Dungeons = () => {
controller={ToggleIcon.dungeonCountController(dungeon, dungeon.sk)}
icons={['small-key']}
/>
- <CountDisplay count={state[`${dungeon.id}-small-key`] || 0} />
+ <CountDisplay count={getDungeonAcquiredSKs(state, dungeon)} />
</span>
<ToggleIcon
controller={ToggleIcon.dungeonController(dungeon)}
/>
<span className="dungeon-checks">
<ToggleIcon
- controller={ToggleIcon.dungeonCheckController(dungeon, dungeon.items)}
+ controller={ToggleIcon.dungeonCheckController(dungeon)}
icons={['open-chest', 'chest']}
/>
- <CountDisplay count={dungeon.items - (state[`${dungeon.id}-checks`] || 0)} />
+ <CountDisplay count={getDungeonRemainingItems(state, dungeon)} />
</span>
{dungeon.boss ?
<ToggleIcon
import { useTranslation } from 'react-i18next';
import {
+ addDungeonCheck,
clearAll,
- decrement,
+ completeDungeonChecks,
+ countRemainingLocations,
+ getDungeonClearedItems,
+ getDungeonRemainingItems,
+ hasClearedLocations,
hasDungeonBoss,
- increment,
+ hasDungeonPrize,
isDungeonCleared,
- toggleBoolean,
+ removeDungeonCheck,
+ resetDungeonChecks,
+ setBossDefeated,
+ setPrizeAcquired,
unclearAll,
} from '../../helpers/tracker';
import { useTracker } from '../../hooks/tracker';
>
<title>{t(`tracker.location.${l.id}`)}</title>
<rect className="box" x="0" y="0" />
- {number && l.cleared < l.total ?
- <text className="text" x="0" y="0">{Math.max(0, l.total - l.cleared)}</text>
+ {number && l.remaining ?
+ <text className="text" x="0" y="0">{l.remaining}</text>
: null}
</g>;
};
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,
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;
+ const remaining = getDungeonRemainingItems(state, definition);
let status = 'available';
if (isDungeonCleared(state, definition)) {
status = 'cleared';
return {
...dungeon,
status,
- cleared,
- total,
+ remaining,
handlePrimary: () => {
- if (['ct', 'gt'].includes(dungeon.id) && cleared === total) {
- if (hasDungeonBoss(state, dungeon)) {
- // reset
- setState(s => ({
- ...s,
- [`${dungeon.id}-checks`]: 0,
- [`${dungeon.id}-boss-defeated`]: false,
- }));
- } else {
- setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
+ if (getDungeonRemainingItems(state, definition)) {
+ setState(addDungeonCheck(definition));
+ } else if (
+ !hasDungeonBoss(state, definition) || !hasDungeonPrize(state, definition)
+ ) {
+ if (definition.boss) {
+ setState(setBossDefeated(definition, true));
+ }
+ if (definition.prize) {
+ setState(setPrizeAcquired(definition, true));
}
} else {
- setState(increment(`${dungeon.id}-checks`, total));
+ setState(resetDungeonChecks(definition));
+ if (definition.boss) {
+ setState(setBossDefeated(definition, false));
+ }
+ if (definition.prize) {
+ setState(setPrizeAcquired(definition, false));
+ }
}
},
handleSecondary: () => {
- if (['ct', 'gt'].includes(dungeon.id) &&
- (hasDungeonBoss(state, dungeon) || !cleared)
- ) {
- if (hasDungeonBoss(state, dungeon)) {
- setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
- } else {
- setState(s => ({
- ...s,
- [`${dungeon.id}-checks`]: total,
- [`${dungeon.id}-boss-defeated`]: true,
- }));
+ if (isDungeonCleared(state, definition)) {
+ if (definition.items) {
+ setState(removeDungeonCheck(definition));
}
+ if (definition.boss) {
+ setState(setBossDefeated(definition, false));
+ }
+ if (definition.prize) {
+ setState(setPrizeAcquired(definition, false));
+ }
+ } else if (getDungeonClearedItems(state, definition)) {
+ setState(removeDungeonCheck(definition));
} else {
- setState(decrement(`${dungeon.id}-checks`, total));
+ setState(completeDungeonChecks(definition));
+ if (definition.boss) {
+ setState(setBossDefeated(definition, true));
+ }
+ if (definition.prize) {
+ setState(setPrizeAcquired(definition, true));
+ }
}
},
};
}, [dungeons, setState, state]);
const mapLocation = React.useCallback(loc => {
- const cleared = loc.checks.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0);
- const total = loc.checks.length;
+ const remaining = countRemainingLocations(state, loc.checks);
let status = 'available';
- if (cleared === total) {
+ if (hasClearedLocations(state, loc.checks)) {
status = 'cleared';
}
return {
...loc,
- cleared,
- total,
+ remaining,
status,
handlePrimary: () => {
- if (cleared < total) {
+ if (remaining) {
setState(clearAll(loc.checks));
} else {
setState(unclearAll(loc.checks));
}
},
handleSecondary: () => {
- if (cleared < total) {
+ if (remaining) {
setState(clearAll(loc.checks));
} else {
setState(unclearAll(loc.checks));
import ZeldaIcon from '../common/ZeldaIcon';
import {
+ addDungeonCheck,
decrement,
getDungeonBoss,
+ getDungeonRemainingItems,
getDungeonPrize,
hasDungeonBoss,
hasDungeonPrize,
highestActive,
increment,
+ removeDungeonCheck,
toggleBoolean,
+ toggleBossDefeated,
} from '../../helpers/tracker';
import { useTracker } from '../../hooks/tracker';
handlePrimary: dungeon.bosses.length > 1
? nextString(`${dungeon.id}-boss`)
: (state, setState) => {
- setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
+ setState(toggleBossDefeated(dungeon));
},
handleSecondary: dungeon.bosses.length > 1 ?
previousString(`${dungeon.id}-boss`)
: (state, setState) => {
- setState(toggleBoolean(`${dungeon.id}-boss-defeated`));
+ setState(toggleBossDefeated(dungeon));
},
});
-ToggleIcon.dungeonCheckController = (dungeon, max) => ({
- getActive: (state, icons) => state[`${dungeon.id}-checks`] < max ? icons[1] : null,
+ToggleIcon.dungeonCheckController = (dungeon) => ({
+ getActive: (state, icons) => getDungeonRemainingItems(state, dungeon) ? icons[1] : null,
getDefault: firstIcon,
handlePrimary: (state, setState) => {
- setState(increment(`${dungeon.id}-checks`, max));
+ setState(addDungeonCheck(dungeon));
},
handleSecondary: (state, setState) => {
- setState(decrement(`${dungeon.id}-checks`, max));
+ setState(removeDungeonCheck(dungeon));
},
});
return { ...state, ...changes };
};
-export const hasDungeonBoss = (state, dungeon) => !!state[`${dungeon.id}-boss-defeated`];
+export const countClearedLocations = (state, locations) =>
+ locations.reduce((acc, cur) => state[cur] ? acc + 1 : acc, 0);
+
+export const hasClearedLocations = (state, locations) =>
+ countClearedLocations(state, locations) === locations.length;
+
+export const countRemainingLocations = (state, locations) =>
+ locations.reduce((acc, cur) => state[cur] ? acc : acc + 1, 0);
+
+export const hasDungeonBoss = (state, dungeon) =>
+ !dungeon.boss || !!state[`${dungeon.id}-boss-defeated`];
export const getDungeonBoss = (state, dungeon) =>
state[`${dungeon.id}-boss`] || dungeon.boss || null;
-export const hasDungeonPrize = (state, dungeon) => !!state[`${dungeon.id}-prize-acquired`];
+export const hasDungeonPrize = (state, dungeon) =>
+ !dungeon.prize || !!state[`${dungeon.id}-prize-acquired`];
export const getDungeonPrize = (state, dungeon) => state[`${dungeon.id}-prize`] || null;
+export const getDungeonClearedItems = (state, dungeon) => state[`${dungeon.id}-checks`] || 0;
+
+export const getDungeonRemainingItems = (state, dungeon) =>
+ Math.max(0, dungeon.items - getDungeonClearedItems(state, dungeon));
+
+export const getDungeonAcquiredSKs = (state, dungeon) => state[`${dungeon.id}-small-key`] || 0;
+
export const isDungeonCleared = (state, dungeon) => {
- const cleared = state[`${dungeon.id}-checks`] || 0;
- const total = dungeon.items;
- const hasItems = cleared >= total;
- const hasBoss = !dungeon.boss || hasDungeonBoss(state, dungeon);
- const hasPrize = !dungeon.porize || hasDungeonPrize(state, dungeon);
+ const hasItems = !getDungeonRemainingItems(state, dungeon);
+ const hasBoss = hasDungeonBoss(state, dungeon);
+ const hasPrize = hasDungeonPrize(state, dungeon);
return hasItems && hasBoss && hasPrize;
};
+export const toggleBossDefeated = dungeon => toggleBoolean(`${dungeon.id}-boss-defeated`);
+
+export const setBossDefeated = (dungeon, defeated) =>
+ state => ({ ...state, [`${dungeon.id}-boss-defeated`]: !!defeated });
+
+export const togglePrizeAcquired = dungeon => toggleBoolean(`${dungeon.id}-prize-acquired`);
+
+export const setPrizeAcquired = (dungeon, acquired) =>
+ state => ({ ...state, [`${dungeon.id}-prize-acquired`]: !!acquired });
+
+export const addDungeonCheck = dungeon => increment(`${dungeon.id}-checks`, dungeon.items);
+
+export const removeDungeonCheck = dungeon => decrement(`${dungeon.id}-checks`, dungeon.items);
+
+export const resetDungeonChecks = dungeon => state => ({ ...state, [`${dungeon.id}-checks`]: 0 });
+
+export const completeDungeonChecks = dungeon =>
+ state => ({ ...state, [`${dungeon.id}-checks`]: dungeon.items });
+
export const makeEmptyState = () => {
const state = {};
BOOLEAN_STATES.forEach(p => {