328 lines
11 KiB
JavaScript
328 lines
11 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 { clearCellMetadata, getMetadata, setCellMetadata, setDomMetadata } from "./metadata.js";
|
|
import {
|
|
checkEquivalence,
|
|
findFullEntry,
|
|
findFullEntryAnywhere,
|
|
findFullEntryFromAtlasMetadata,
|
|
stripEntryToMinimums
|
|
} from "./ncpf-utils.js";
|
|
import { setTooltip, tooltipDataToPlain } from "./tooltip.js";
|
|
import { Filter } from "./filter.js";
|
|
import { debounce } from "./dom-utils.js";
|
|
import { getBlockPickerActiveItem, setBlockPickerRecipe } from "./block-picker.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",
|
|
"block-picker:selection",
|
|
], rescanSelectionRecipes);
|
|
});
|
|
|
|
function initializeRecipePicker({
|
|
parent,
|
|
title,
|
|
entryListType,
|
|
multiblockType,
|
|
recipes,
|
|
currentSelection,
|
|
updateSelection,
|
|
}) {
|
|
|
|
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 = "Unknown");
|
|
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();
|
|
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() {
|
|
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 = getMetadata(plannerDom);
|
|
let currentSelection = plannerMetadata?.coolant_recipe;
|
|
if (!plannerMetadata) {
|
|
currentSelection = defaultRecipes[multiblockType].coolant_recipe;
|
|
setDomMetadata(plannerDom, {
|
|
coolant_recipe: currentSelection,
|
|
});
|
|
}
|
|
currentSelection = findFullEntry("coolant_recipes", currentSelection, { configurationHint: multiblockType });
|
|
|
|
initializeRecipePicker({
|
|
parent: designItem,
|
|
title: "Coolant Recipe",
|
|
entryListType: "coolant_recipes",
|
|
recipes: coolantRecipes,
|
|
updateSelection: r => setDomMetadata(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 pickerTile = getBlockPickerActiveItem();
|
|
|
|
const designRecipes = new DomList(
|
|
document.getElementById("recipe-selector:selection"),
|
|
document.getElementById("recipe-selector:template"),
|
|
);
|
|
designRecipes.clear();
|
|
|
|
if ((selectedTiles.size !== 1 || selectedTiles.has(-1)) && pickerTile === -1) {
|
|
return;
|
|
}
|
|
|
|
const multiblockType = getPlannerMultiblockType();
|
|
|
|
const tileMetadata = getAtlasMetadata(selectedCells.length > 0 ? selectedCells[0].dataset.tile : pickerTile);
|
|
const tiles = findFullEntryFromAtlasMetadata(tileMetadata, { all: true });
|
|
|
|
if (!tiles[0] || !tiles[0].modules || !tiles[0].modules["ncpf:block_recipes"]) {
|
|
return;
|
|
}
|
|
|
|
const cellRecipes = selectedCells.map(v => getMetadata(v)?.block_recipe);
|
|
const recipeSet = cellRecipes.some(v => v);
|
|
const recipeAllSame = cellRecipes.every(v => v === cellRecipes[0] || checkEquivalence(v, cellRecipes[0]));
|
|
|
|
const recipes = tiles.flatMap(t => t.modules["ncpf:block_recipes"].recipes);
|
|
|
|
recipes.unshift({
|
|
modules: {
|
|
["plannerator:display_name"]: {
|
|
display_name: "Unfiltered",
|
|
},
|
|
},
|
|
});
|
|
|
|
let currentRecipe = null
|
|
if (recipeAllSame) {
|
|
if (recipeSet) {
|
|
currentRecipe = recipes.find(r => checkEquivalence(cellRecipes[0], r));
|
|
} else {
|
|
currentRecipe = recipes[0];
|
|
}
|
|
}
|
|
|
|
const designItem = designRecipes.makeItem();
|
|
|
|
initializeRecipePicker({
|
|
parent: designItem,
|
|
title: "Block Recipe",
|
|
entryType: "ncpf:block_recipes",
|
|
recipes,
|
|
multiblockType,
|
|
updateSelection: (e) => {
|
|
if (e === recipes[0]) {
|
|
selectedCells.forEach(v => clearCellMetadata(v, "block_recipe"));
|
|
return;
|
|
}
|
|
const stripped = stripEntryToMinimums(e);
|
|
selectedCells.forEach(v => setCellMetadata(v, "block_recipe", stripped));
|
|
if (pickerTile !== -1) {
|
|
setBlockPickerRecipe(e);
|
|
}
|
|
},
|
|
currentSelection: currentRecipe,
|
|
});
|
|
|
|
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 ("nuclearcraft:overhaul_sfr:fuel_stats" in recipe.modules) {
|
|
hasAdditionalData = true;
|
|
const stats = recipe.modules["nuclearcraft:overhaul_sfr:fuel_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 `Efficiency: ${stats.efficiency}`;
|
|
yield `Heat: ${stats.heat}`;
|
|
yield `Time: ${stats.time}`;
|
|
yield `Criticality: ${stats.criticality}`;
|
|
if (stats.self_priming) {
|
|
yield "Self-priming";
|
|
}
|
|
yield {
|
|
type: "item",
|
|
atlasIndex: atlasId,
|
|
name: outputName,
|
|
prefix: "Output: ",
|
|
};
|
|
}
|
|
|
|
if ("nuclearcraft:overhaul_sfr:irradiator_stats" in recipe.modules) {
|
|
hasAdditionalData = true;
|
|
const stats = recipe.modules["nuclearcraft:overhaul_sfr:irradiator_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 `Efficiency: ${stats.efficiency}`;
|
|
yield `Heat: ${stats.heat}`;
|
|
yield {
|
|
type: "item",
|
|
atlasIndex: atlasId,
|
|
name: outputName,
|
|
prefix: "Output: ",
|
|
};
|
|
}
|
|
|
|
if (!hasAdditionalData) {
|
|
console.log("unknown recipe", recipe, metadata);
|
|
}
|
|
|
|
if (!metadata) {
|
|
console.trace("no metadata for recipe", recipe, metadata);
|
|
return;
|
|
}
|
|
|
|
if (metadata.configurationAddon) {
|
|
yield {
|
|
type: "italics",
|
|
text: metadata.configurationAddon.name,
|
|
};
|
|
}
|
|
}
|