+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 hasItems = !getDungeonRemainingItems(state, dungeon);
+ const hasBoss = hasDungeonBoss(state, dungeon);
+ const hasPrize = hasDungeonPrize(state, dungeon);
+ return hasItems && hasBoss && hasPrize;
+};
+
+export const aggregateDungeonStatus = (dungeon, logic, state) => {
+ if (isDungeonCleared(state, dungeon)) {
+ return 'cleared';
+ }
+ if (logic[dungeon.id] === 'unavailable') {
+ return 'unavailable';
+ }
+ const checks = [...dungeon.checks];
+ if (['ct', 'gt'].includes(dungeon.id)) {
+ checks.push(`${dungeon.id}-boss-killable`);
+ }
+ const statuses = checks.map(name => getLocationStatus(name, logic, state));
+ return getCombinedStatus(statuses);
+};
+
+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 });
+