];
export const CONFIG = {
+ showMap: 'situational',
+ showCompass: 'situational',
+ showSmall: 'always',
+ showBig: 'always',
wildMap: false,
wildCompass: false,
wildSmall: false,
mask: DUNGEON_MASKS.HC,
checks: [
'dark-cross',
- 'hc-map',
+ 'hc-map-chest',
'hc-boom',
'hc-cell',
'sanc',
chest: 0,
},
{
- id: 'hc-map',
+ id: 'hc-map-chest',
area: 'hc',
room: 0x72,
chest: 0,
{
id: 'hype-cave-bottom',
room: 0x11E,
- chest: 4,
+ chest: 3,
},
{
id: 'hype-cave-npc',
{
id: 'paradox-lower-far-right',
room: 0xEF,
- chest: 4,
+ chest: 3,
},
{
id: 'paradox-lower-mid',
room: 0xEF,
- chest: 5,
+ chest: 4,
},
{
id: 'paradox-upper-left',
{
id: 'waterfall-fairy-left',
room: 0x114,
- chest: 4,
+ chest: 0,
},
{
id: 'waterfall-fairy-right',
room: 0x114,
- chest: 5,
+ chest: 1,
},
];
+export const shouldShowDungeonItem = (config, which) => {
+ const show = config[`show${which}`] || 'always';
+ const wild = config[`wild${which}`] || false;
+ switch (show) {
+ default:
+ case 'always':
+ return true;
+ case 'situational':
+ return wild;
+ case 'never':
+ return false;
+ }
+};
+
export const toggleBoolean = name => state => ({
...state,
[name]: !state[name],
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 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 => {
});
};
-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;
};
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;
};