Source: logic-tweaks.js

import _ from 'lodash';

import CHARTS from '../data/charts.json';
import HAS_ACCESSED_LOCATION_TWEAKS from '../data/has-accessed-location-tweaks.json';
import ISLANDS from '../data/islands.json';

import Locations from './locations';
import LogicHelper from './logic-helper';
import Macros from './macros';
import Permalink from './permalink';
import Settings from './settings';

/**
 * This class handles modifications that must be made to the randomizer logic
 * in order for it to work correctly with the tracker.
 *
 * @class
 */
class LogicTweaks {
  /**
   * This method makes the necessary modifications to the `Locations` and
   * `Macros` for the tracker to function correctly.
   */
  static applyTweaks() {
    this.#updateLocations();
    this.#updateMacros();
  }

  static applyHasAccessedLocationTweaksForLocations() {
    const itemLocationTweaks = HAS_ACCESSED_LOCATION_TWEAKS.itemLocations;
    _.forEach(itemLocationTweaks, (generalLocationInfo, generalLocation) => {
      _.forEach(generalLocationInfo, (detailedLocation) => {
        const requirements = Locations.getLocation(
          generalLocation,
          detailedLocation,
          Locations.KEYS.NEED,
        );
        const newNeeds = this.#replaceCanAccessOtherLocation(requirements);

        Locations.setLocation(
          generalLocation,
          detailedLocation,
          Locations.KEYS.NEED,
          newNeeds,
        );
      });
    });
  }

  static #updateLocations() {
    this.#addDefeatGanondorf();
    this.#updateTingleStatueReward();
    this.#updateSunkenTriforceTypes();
    this.applyHasAccessedLocationTweaksForLocations();
  }

  static #updateMacros() {
    this.#updateRandomEntranceMacros();
    this.#updateChartMacros();
    this.#updateTriforceMacro();
    this.#applyHasAccessedLocationTweaksForMacros();
    this.#updateRequiredBossesModeMacro();
  }

  static #addDefeatGanondorf() {
    Locations.setLocation(
      LogicHelper.DUNGEONS.GANONS_TOWER,
      LogicHelper.DEFEAT_GANONDORF_LOCATION,
      Locations.KEYS.NEED,
      'Can Reach and Defeat Ganondorf',
    );
  }

  static #updateTingleStatueReward() {
    Locations.setLocation(
      LogicHelper.ISLANDS.TINGLE_ISLAND,
      'Ankle - Reward for All Tingle Statues',
      Locations.KEYS.NEED,
      'Tingle Statue x5',
    );
  }

  static #updateSunkenTriforceTypes() {
    if (Settings.getOptionValue(Permalink.OPTIONS.RANDOMIZE_CHARTS)) {
      return;
    }

    _.forEach(ISLANDS, (islandName) => {
      const originalItem = Locations.getLocation(
        islandName,
        'Sunken Treasure',
        Locations.KEYS.ORIGINAL_ITEM,
      );

      if (_.startsWith(originalItem, 'Triforce Shard')) {
        Locations.setLocation(
          islandName,
          'Sunken Treasure',
          Locations.KEYS.TYPES,
          Settings.FLAGS.SUNKEN_TRIFORCE,
        );
      }
    });
  }

  static #applyHasAccessedLocationTweaksForMacros() {
    const macrosTweaks = HAS_ACCESSED_LOCATION_TWEAKS.macros;
    _.forEach(macrosTweaks, (macroName) => {
      const macroValue = Macros.getMacro(macroName);
      const newMacro = this.#replaceCanAccessOtherLocation(macroValue);
      Macros.setMacro(macroName, newMacro);
    });
  }

  static #updateRandomEntranceMacros() {
    _.forEach(LogicHelper.allRandomEntrances(), (exitMacroName) => {
      const macroName = `Can Access ${exitMacroName}`;
      const entryName = LogicHelper.entryName(exitMacroName);
      Macros.setMacro(macroName, entryName);
    });
  }

  static #updateChartMacros() {
    if (!Settings.getOptionValue(Permalink.OPTIONS.RANDOMIZE_CHARTS)) {
      return;
    }

    _.forEach(CHARTS, (chart, index) => {
      // assume everything is a Treasure Chart and clear any additional requirements like
      // wallet upgrades
      const island = LogicHelper.islandForChart(chart);
      const macroName = `Chart for Island ${index + 1}`;
      Macros.setMacro(macroName, LogicHelper.randomizedChartForIsland(island));
    });
  }

  static #updateTriforceMacro() {
    Macros.setMacro('All 8 Triforce Shards', 'Triforce Shard x8');
  }

  static #updateRequiredBossesModeMacro() {
    if (!Settings.getOptionValue(Permalink.OPTIONS.REQUIRED_BOSSES)) {
      return;
    }

    const bossRequirements = _.map(
      LogicHelper.REQUIRED_BOSSES_MODE_DUNGEONS,
      (dungeonName) => LogicHelper.bossRequirementForDungeon(dungeonName),
    );
    const macroValue = _.join(bossRequirements, ` ${LogicHelper.TOKENS.AND} `);
    Macros.setMacro('Can Defeat All Required Bosses', macroValue);
  }

  static #replaceCanAccessOtherLocation(requirements) {
    return requirements.replace(/Can Access Item Location/g, 'Has Accessed Other Location');
  }
}

export default LogicTweaks;