Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tools/*
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"generate:options": "node ./tools/generateOptions.mjs",
"lint": "npm run lint:ts && npm run lint:eslint",
"lint:eslint": "eslint --max-warnings 0 \"src/**/*.ts\" \"src/**/*.js\" \"src/**/*.tsx\"",
"lint:ts": "tsc --noEmit"
Expand Down
18 changes: 7 additions & 11 deletions src/ImportExport.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChangeEvent } from 'react';
import { TrackerState, loadTracker } from './state/Tracker';
import Settings from './permalink/Settings';
import { defaultSettings } from './permalink/Settings';
import Logic from './logic/Logic';
import { AppDispatch, RootState } from './state/Store';
import { loadLogic } from './state/Logic';
import { useDispatch, useSelector } from 'react-redux';
import LogicLoader from './logic/LogicLoader';

export interface ExportState {
state: TrackerState;
Expand All @@ -16,22 +17,17 @@ async function importState(importedState: ExportState, dispatch: AppDispatch) {
const source = importedState.source
if (!source) {
alert('invalid source');
return;
}
const settings = new Settings();
if (importedState.state.settings) {
settings.loadFrom(importedState.state.settings);
} else {
await settings.init(source);
importedState.state.settings = settings;
}
const { rawLogic, options } = await LogicLoader.loadLogicFiles(source);

const logic = new Logic();
await logic.initialize(settings, source);
importedState.state.settings ??= defaultSettings(options);

const logic = new Logic(rawLogic, importedState.state.settings);
const state = importedState.state;

dispatch(loadTracker(state));
dispatch(loadLogic({ logic, options: settings.allOptions, source }));
dispatch(loadLogic({ logic, options, source }));
}

export default function ImportExport() {
Expand Down
157 changes: 65 additions & 92 deletions src/Options.tsx

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions src/TrackerContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
isLogicLoadedSelector,
loadingErrorSelector,
} from './selectors/LogicInput';
import Settings from './permalink/Settings';
import { decodePermalink } from './permalink/Settings';
import Logic from './logic/Logic';
import Tracker from './Tracker';
import { parseError } from './utils/Error';
import { reset } from './state/Tracker';
import LogicLoader from './logic/LogicLoader';

export default function TrackerContainer() {
const dispatch = useDispatch();
Expand All @@ -19,16 +20,14 @@ export default function TrackerContainer() {
async (source: string) => {
setLoadingSource(source);
try {
const settings = new Settings();
await settings.init(source);
const { rawLogic, options } = await LogicLoader.loadLogicFiles(source);
const path = new URLSearchParams(window.location.search);
const permalink = decodeURIComponent(path.get('options')!);
settings.updateFromPermalink(permalink);
const logic = new Logic();
await logic.initialize(settings, source);
const settings = decodePermalink(options, permalink);
const logic = new Logic(rawLogic, settings);
dispatch(reset({ settings }));
dispatch(
loadLogic({ logic, options: settings.allOptions, source }),
loadLogic({ logic, options, source }),
);
} catch (e) {
dispatch(setLoadingError({ error: parseError(e) }));
Expand Down
4 changes: 2 additions & 2 deletions src/itemTracker/DungeonTracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export default function DungeonTracker(props: DungeonTrackerProps) {
const settings = useSelector(settingsSelector);
const skyKeep = useSelector(skyKeepShownSelector);

const entranceRando = settings.getOption('Randomize Entrances');
const trialRando = settings.getOption('Randomize Silent Realms');
const entranceRando = settings['randomize-entrances'];
const trialRando = settings['randomize-trials'];

useResizeObserver(divElement, () => {
const elem = divElement.current;
Expand Down
45 changes: 22 additions & 23 deletions src/logic/Inventory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
import Settings from '../permalink/Settings';
import { TrackerState } from '../state/Tracker';
import { Settings } from '../permalink/SettingsTypes';

export const itemMaxes = {
'Progressive Sword': 6,
Expand Down Expand Up @@ -69,53 +69,52 @@ export function getInitialItems(settings: Settings): TrackerState['inventory'] {
startingItems[item]! += 1;
};
addItem('Sailcloth');
if (settings.getOption('Starting Tablet Count') === 3) {
if (settings['starting-tablet-count'] === 3) {
addItem('Emerald Tablet');
addItem('Ruby Tablet');
addItem('Amber Tablet');
}
for (
let crystalPacksAdded = 0;
crystalPacksAdded <
settings.getOption('Starting Gratitude Crystal Packs');
settings['starting-crystal-packs'];
crystalPacksAdded++
) {
addItem('Gratitude Crystal Pack');
}
for (
let tadtonesAdded = 0;
tadtonesAdded < settings.getOption('Starting Tadtone Count');
tadtonesAdded < settings['starting-tadtones'];
tadtonesAdded++
) {
addItem('Group of Tadtones');
}
for (
let bottlesAdded = 0;
bottlesAdded < settings.getOption('Starting Empty Bottles');
bottlesAdded < settings['starting-bottles'];
bottlesAdded++
) {
addItem('Empty Bottle');
}
const startingSword = settings.getOption('Starting Sword');
if (!(startingSword === 'Swordless')) {
const swordsToAdd: Record<string, number> = {
'Practice Sword': 1,
'Goddess Sword': 2,
'Goddess Longsword': 3,
'Goddess White Sword': 4,
'Master Sword': 5,
'True Master Sword': 6,
};
const startingSword = settings['starting-sword'];
const swordsToAdd: Record<Settings['starting-sword'], number> = {
'Swordless': 0,
'Practice Sword': 1,
'Goddess Sword': 2,
'Goddess Longsword': 3,
'Goddess White Sword': 4,
'Master Sword': 5,
'True Master Sword': 6,
};

for (
let swordsAdded = 0;
swordsAdded < swordsToAdd[startingSword];
swordsAdded++
) {
addItem('Progressive Sword');
}
for (
let swordsAdded = 0;
swordsAdded < swordsToAdd[startingSword];
swordsAdded++
) {
addItem('Progressive Sword');
}
_.forEach(settings.getOption('Starting Items'), (item) => {
_.forEach(settings['starting-items'], (item) => {
if (item.includes('Song of the Hero')) {
addItem('Song of the Hero');
} else if (item.includes('Triforce')) {
Expand Down
66 changes: 13 additions & 53 deletions src/logic/Locations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import _ from 'lodash';
import Settings from '../permalink/Settings';
import ItemLocation from './ItemLocation';
import potentialBannedLocations_ from '../data/potentialBannedLocations.json';
import { Settings } from '../permalink/SettingsTypes';

const potentialBannedLocations: {
[area: string]: { [locationName: string]: { requiredDungeon: string } };
Expand Down Expand Up @@ -43,22 +43,22 @@ export function createIsCheckBannedPredicate(
requiredDungeons: string[],
) {
return ({ id, area, name, rawType: loctype }: ItemLocation) => {
const bannedLocations = settings.getOption('Excluded Locations');
const bannedLocations = settings['excluded-locations'];
if (bannedLocations.includes(id)) {
return true;
}


if (settings.getOption('Empty Unrequired Dungeons')) {
if (settings['empty-unrequired-dungeons']) {
const potentialBanReason = potentialBannedLocations[area]?.[name];

if (potentialBanReason && !requiredDungeons.includes(potentialBanReason.requiredDungeon)) {
return true;
}
}

let maxRelics = settings.getOption('Trial Treasure Amount');
if (!settings.getOption('Treasuresanity in Silent Realms')) {
let maxRelics = settings['trial-treasure-amount'];
if (!settings['treasuresanity-in-silent-realms']) {
maxRelics = 0;
}
if (
Expand All @@ -68,9 +68,7 @@ export function createIsCheckBannedPredicate(
return true;
}

const emptyUnrequiredDungeons = settings.getOption(
'Empty Unrequired Dungeons',
);
const emptyUnrequiredDungeons = settings['empty-unrequired-dungeons'];
if (
emptyUnrequiredDungeons &&
(isDungeon(area)) &&
Expand All @@ -79,70 +77,32 @@ export function createIsCheckBannedPredicate(
return true;
}

// old 1.4.1 options
const shopMode = settings.getOption('Shop Mode');
const batMode = settings.getOption('Max Batreaux Reward');
if (loctype !== null) {
// have to specifically check Shopsanity being false, otherwise it being null on new versions disables Beedle
if (
(settings.getOption('Shopsanity') === false &&
(settings['shopsanity'] === false &&
loctype.includes("Beedle's Shop Purchases")) ||
(!settings.getOption('Rupeesanity') &&
(!settings['rupeesanity'] &&
loctype.includes('Rupees')) ||
(!settings.getOption('Tadtonesanity') &&
(!settings['tadtonesanity'] &&
loctype.includes('Tadtones') &&
name !== "Water Dragon's Reward")
) {
return true;
}
// 1.4.1 rupeesanity & shopsanity compatibility
if (
settings.getOption('Rupeesanity') === 'Vanilla' &&
loctype.includes('Rupees')
) {
return true;
}
if (
shopMode !== undefined &&
loctype.includes("Beedle's Shop Purchases")
) {
if (shopMode === 'Vanilla') {
return true;
}
if (
shopMode.includes('Cheap') &&
parseInt(name.replace(/^\D+/g, ''), 10) > 300
) {
return true;
}
if (
shopMode.includes('Medium') &&
parseInt(name.replace(/^\D+/g, ''), 10) > 1000
) {
return true;
}
}
// Post-shop split compatibility
// have to specifically check Beedle Shopsanity being false, otherwise it being null on old versions disables Beedle
if (
(settings.getOption('Beedle Shopsanity') === false &&
(settings['beedle-shopsanity'] === false &&
loctype.includes("Beedle's Shop")) ||
(!settings.getOption('Gear Shopsanity') &&
(!settings['rupin-shopsanity'] &&
loctype.includes('Gear Shop')) ||
(!settings.getOption('Potion Shopsanity') &&
loctype.includes('Potion Shop'))
(!settings['luv-shopsanity']) &&
loctype.includes('Potion Shop')
) {
return true;
}
}
// Must check this outside the loctype block because Batreaux checks have no type. 1.4.1 batreaux compatibility
if (
batMode !== undefined &&
area.includes('Batreaux') &&
parseInt(name.replace(/^\D+/g, ''), 10) > batMode
) {
return true;
}
};
}

Expand Down
13 changes: 4 additions & 9 deletions src/logic/Logic.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
import _ from 'lodash';
import LogicLoader from './LogicLoader';
import LogicHelper from './LogicHelper';
import Requirements from './Requirements';
import LogicTweaks from './LogicTweaks';
import goddessCubes from '../data/goddessCubes.json';
import ItemLocation from './ItemLocation';
import crystalLocations from '../data/crystals.json';
import logicFileNames from '../data/logicModeFiles.json';
import Settings from '../permalink/Settings';
import BooleanExpression from './BooleanExpression';
import { completionRequirementToDungeon, splitLocationName } from './Locations';
import { InventoryItem, isItem } from './Inventory';
import { Settings } from '../permalink/SettingsTypes';
import { RawLogic } from './LogicLoader';

class Logic {
// @ts-expect-error ts(2564)
settings: Settings;
// @ts-expect-error ts(2564)
requirements: Requirements;

locations: Record<string, Record<string, ItemLocation>> = {};
additionalLocations: Record<string, Record<string, ItemLocation>> = {};

cubeList: Record<string, ItemLocation> = {};
crystalList: Record<string, ItemLocation> = {};
async initialize(settings: Settings, source: string) {

constructor({ hints, locations, requirements }: RawLogic, settings: Settings) {
this.settings = settings;
console.log(settings.getOption('Logic Mode'));
const { requirements, locations, hints } = await LogicLoader.loadLogicFiles(_.get(logicFileNames, settings.getOption('Logic Mode')), source);
this.requirements = new Requirements(requirements);

_.forEach(locations, (data, id) => {
Expand Down
Loading