nc-planner/scripts/recipe-selector.js

229 lines
7.8 KiB
JavaScript

import { registerLoadCallback } from "./main.js";
import { listenTo } from "./events.js";
import { configuration } from "./configuration.js";
import { getPlannerDom, getPlannerMultiblockType, getSelectedCells } from "./planner.js";
import { DomList } from "./dom-list.js";
import { getAtlasIndexFromMetadata, getAtlasMetadata, makeAtlasImage } from "./atlas.js";
import { getSolverMetadata, setSolverMetadata } from "./solver.js";
import { findFullEntry, findFullEntryAnywhere, lookupFromMetadata } from "./ncpf-utils.js";
import { createTooltip, setTooltip, tooltipDataToPlain } from "./tooltip.js";
import { Filter } from "./filter.js";
import { debounce } from "./dom-utils.js";
const defaultRecipes = {
["nuclearcraft:overhaul_sfr"]: {
coolant_recipe: {
elements: [
{ name: "water", type: "legacy_fluid" },
{ name: "condensate_water", type: "legacy_fluid" },
],
type: "list",
},
},
};
registerLoadCallback(() => {
listenTo([ "config:end", "solver:reset:done" ], rescanDesignRecipes);
listenTo([ "planner:selection:removed", "planner:selection:added"], rescanSelectionRecipes);
});
function initializeRecipePicker(params) {
const { parent, title, entryListType, multiblockType, recipes, currentSelection, updateSelection } = params;
parent.querySelectorAll('[data-text="recipe:type"]')
.forEach(e => e.innerText = title);
function updateSelectedRecipe(selectedRecipe) {
if (!selectedRecipe) {
parent.querySelectorAll('[data-text="recipe:selected"]')
.forEach(e => e.innerText = "None");
parent.querySelectorAll('[data-atlas="recipe:selected"]')
.forEach(e => e.replaceChildren(makeAtlasImage(-1)));
return;
}
const selectedRecipeName = selectedRecipe.modules["plannerator:display_name"].display_name;
const selectedAtlasId = getAtlasIndexFromMetadata(selectedRecipe, multiblockType, entryListType);
const metadata = getAtlasMetadata(selectedAtlasId);
parent.querySelectorAll('[data-text="recipe:selected"]')
.forEach(e => e.innerText = selectedRecipeName);
parent.querySelectorAll('[data-atlas="recipe:selected"]')
.forEach(e => e.replaceChildren(makeAtlasImage(selectedAtlasId)));
parent.querySelectorAll('.recipe--selected')
.forEach(e => setTooltip(e, [...getRecipeTooltip(selectedRecipe, metadata)]));
}
updateSelectedRecipe(currentSelection);
function handleDismiss() {
console.log("dismiss");
parent.classList.remove("selecting");
window.removeEventListener("click", handleDismiss);
}
parent.addEventListener("click", e => e.stopPropagation());
const filterElem = parent.querySelector('[data-input="recipe:filter"]');
function updateRecipeList() {
const filter = Filter.fromInput(filterElem);
const recipeList = new DomList(parent.querySelector(":scope > .select"));
recipeList.clear();
console.log(recipes);
for (let i = 0; i < recipes.length; i++) {
const recipe = recipes[i];
const recipeItem = recipeList.makeItem();
const recipeName = recipe.modules["plannerator:display_name"].display_name;
const atlasId = getAtlasIndexFromMetadata(recipe, multiblockType, entryListType);
const metadata = getAtlasMetadata(atlasId);
recipeItem.querySelectorAll('[data-text="recipe:item"]')
.forEach(e => e.innerText = recipeName);
recipeItem.querySelectorAll('[data-atlas="recipe:item"]')
.forEach(e => e.replaceChildren(makeAtlasImage(atlasId)));
recipeItem.addEventListener("click", () => {
handleDismiss();
updateSelectedRecipe(recipe);
updateSelection(recipe);
});
const tooltipData = [...getRecipeTooltip(recipe, metadata)];
setTooltip(recipeItem, tooltipData);
if (!filter.test(recipeName, ...tooltipDataToPlain(tooltipData))) {
recipeItem.classList.add("hidden");
}
recipeList.addItem(recipeItem);
}
}
updateRecipeList();
filterElem.addEventListener("input", debounce(updateRecipeList));
parent.querySelectorAll(':scope > .recipe--selected').forEach(e => {
e.addEventListener("click", () => {
parent.classList.add("selecting");
parent.querySelector('[autofocus]').focus();
window.addEventListener("click", handleDismiss);
});
});
}
function rescanDesignRecipes() {
console.log("rescan recipes");
console.log(configuration);
const multiblockType = getPlannerMultiblockType();
const configs = [
...Object.entries(configuration.configuration),
...configuration.addons.flatMap(a => Object.entries(a.configuration)),
].filter(([type]) => type === multiblockType);
const designRecipes = new DomList(
document.getElementById("recipe-selector:design"),
document.getElementById("recipe-selector:template"),
);
designRecipes.clear();
if (multiblockType === "nuclearcraft:overhaul_sfr") {
const coolantRecipes = configs.filter(([, v]) => "coolant_recipes" in v && v.coolant_recipes.length > 0)
.flatMap(([, v]) => v.coolant_recipes);
const designItem = designRecipes.makeItem();
const plannerDom = getPlannerDom();
const plannerMetadata = getSolverMetadata(plannerDom);
let currentSelection = plannerMetadata?.coolant_recipe;
if (!plannerMetadata) {
currentSelection = defaultRecipes[multiblockType].coolant_recipe;
setSolverMetadata(plannerDom, {
coolant_recipe: currentSelection,
});
}
currentSelection = findFullEntry("coolant_recipes", currentSelection, { configurationHint: multiblockType });
initializeRecipePicker({
parent: designItem,
title: "Coolant Recipe",
entryListType: "coolant_recipes",
recipes: coolantRecipes,
updateSelection: r => setSolverMetadata(plannerDom, { coolant_recipe: r }),
multiblockType,
currentSelection,
});
designRecipes.addItem(designItem);
}
}
function rescanSelectionRecipes() {
const selectedCells = getSelectedCells();
const selectedTiles = new Set(selectedCells.map(v => Number(v.dataset.tile)));
const designRecipes = new DomList(
document.getElementById("recipe-selector:selection"),
document.getElementById("recipe-selector:template"),
);
designRecipes.clear();
if (selectedTiles.size !== 1 || selectedTiles.has(-1)) {
return;
}
const multiblockType = getPlannerMultiblockType();
const tileMetadata = getAtlasMetadata(selectedCells[0].dataset.tile);
const tile = findFullEntry("blocks", tileMetadata, { configurationHint: multiblockType });
if (!tile.modules || !tile.modules["ncpf:block_recipes"]) {
return;
}
const designItem = designRecipes.makeItem();
initializeRecipePicker({
parent: designItem,
title: "Block Recipe",
entryType: "ncpf:block_recipes",
recipes: tile.modules["ncpf:block_recipes"].recipes,
multiblockType,
});
designRecipes.addItem(designItem);
}
function* getRecipeTooltip(recipe, metadata) {
const multiblockType = getPlannerMultiblockType();
let hasAdditionalData = false;
if ("nuclearcraft:overhaul_sfr:coolant_recipe_stats" in recipe.modules) {
hasAdditionalData = true;
const stats = recipe.modules["nuclearcraft:overhaul_sfr:coolant_recipe_stats"];
const { entry: output, type } = findFullEntryAnywhere(stats.output, { configurationHint: multiblockType });
const outputName = output.modules["plannerator:display_name"].display_name;
const atlasId = getAtlasIndexFromMetadata(output, multiblockType, type);
yield `Heat: ${stats.heat}`;
yield `Output:`
yield `Ratio ${stats.output_ratio}`;
yield {
type: "item",
atlasIndex: atlasId,
name: outputName,
};
}
if (!hasAdditionalData) {
console.log("unknown recipe", recipe, metadata);
}
if (metadata.configurationAddon) {
yield {
type: "italics",
text: metadata.configurationAddon.name,
};
}
}