nc-planner/scripts/block-picker.js

225 lines
6.9 KiB
JavaScript

import { DomList } from "./dom-list.js";
import { atlases, makeAtlasImage } from "./atlas.js";
import { Filter } from "./filter.js";
import { createTooltip, tooltipDataToPlain } from "./tooltip.js";
import { emitEvent, listenTo } from "./events.js";
import { getPlannerMultiblockType } from "./planner.js";
import { registerLoadCallback } from "./main.js";
import { lookupFromMetadata, resolveBlockName } from "./ncpf-utils.js";
registerLoadCallback(() => {
listenTo("config:end", buildBlockPicker);
listenTo("block-picker:filter:input", buildBlockPicker);
listenTo("block-picker:selection", () => setBlockPickerRecipe(null));
});
let activeItemRecipe = null;
function buildBlockPicker() {
const filter = Filter.fromInput("block-picker:filter");
const grid = new DomList(document.getElementById("block-picker-list"));
grid.clear();
atlases.flatMap(a => a.metadata)
.filter(m => m.configuration === getPlannerMultiblockType() && m.configurationList === "blocks")
.forEach(m => {
const item = grid.makeItem();
item.dataset.index = m.index.toString(10);
const tooltipData = [...getBlockTooltip(m)];
item.dataset.tooltip = createTooltip(tooltipData).toString(10);
if (!filter.test(...tooltipDataToPlain(tooltipData))) {
item.classList.add("hidden");
}
if (tooltipData.some(v => v.type === "meta" && v.casing)) {
item.dataset.casing = "";
}
item.appendChild(makeAtlasImage(m.index));
item.addEventListener("click", blockPickerClickEvent);
grid.addItem(item);
});
}
function blockPickerClickEvent(e) {
const blockPickerDom = document.getElementById("block-picker-list");
const items = document.querySelectorAll("#block-picker-list [data-index]");
if (e.currentTarget.dataset.active !== undefined) {
items.forEach(i => delete i.dataset.active);
blockPickerDom.dataset.activeItem = (-1).toString(10);
delete e.currentTarget.dataset.active;
emitEvent("block-picker:selection", -1);
emitEvent("block-picker:selection:clear");
return;
}
items.forEach(i => delete i.dataset.active);
blockPickerDom.dataset.activeItem = e.currentTarget.dataset.index;
e.currentTarget.dataset.active = "";
emitEvent("block-picker:selection", blockPickerDom.dataset.activeItem);
}
function* getBlockTooltip(metadata) {
const data = lookupFromMetadata(metadata);
if (!data) {
console.warn("failed to get data from metadata", metadata);
return;
}
yield data.modules["plannerator:display_name"].display_name;
let hasAdditionalData = false;
if ("nuclearcraft:overhaul_sfr:heat_sink" in data.modules) {
hasAdditionalData = true;
const heatSink = data.modules["nuclearcraft:overhaul_sfr:heat_sink"];
yield "Heatsink";
if (heatSink.cooling) {
yield `Cooling: ${heatSink.cooling} H/t`;
}
if (heatSink.rules) {
for (const rule of heatSink.rules) {
yield "Requires " + parseRuleAsTooltip(rule, { configurationHint: metadata.configuration });
}
}
}
if ("nuclearcraft:overhaul_sfr:casing" in data.modules) {
hasAdditionalData = true;
yield "Casing";
yield {
type: "meta",
casing: true,
};
}
if ("nuclearcraft:overhaul_sfr:controller" in data.modules) {
hasAdditionalData = true;
yield "Controller";
}
if ("nuclearcraft:overhaul_sfr:fuel_cell" in data.modules) {
hasAdditionalData = true;
yield "Fuel Cell";
}
if ("nuclearcraft:overhaul_sfr:port" in data.modules) {
hasAdditionalData = true;
if (data.modules["nuclearcraft:overhaul_sfr:port"].output) {
yield "Output Port";
} else {
yield "Input Port";
}
}
if ("nuclearcraft:overhaul_sfr:irradiator" in data.modules) {
hasAdditionalData = true;
yield "Irradiator";
}
if ("nuclearcraft:overhaul_sfr:conductor" in data.modules) {
hasAdditionalData = true;
yield "Conductor";
}
if ("nuclearcraft:overhaul_sfr:neutron_source" in data.modules) {
hasAdditionalData = true;
const neutronSource = data.modules["nuclearcraft:overhaul_sfr:neutron_source"];
yield "Neutron Source";
yield `Efficiency: ${neutronSource.efficiency}`;
}
if ("nuclearcraft:overhaul_sfr:moderator" in data.modules) {
hasAdditionalData = true;
const moderator = data.modules["nuclearcraft:overhaul_sfr:moderator"];
yield "Moderator";
yield `Flux: ${moderator.flux}`;
yield `Efficiency: ${moderator.efficiency}`;
}
if ("nuclearcraft:overhaul_sfr:reflector" in data.modules) {
hasAdditionalData = true;
const reflector = data.modules["nuclearcraft:overhaul_sfr:reflector"];
yield "Reflector";
yield `Efficiency: ${reflector.efficiency}`;
yield `Reflectivity: ${reflector.reflectivity}`;
}
if ("nuclearcraft:overhaul_sfr:neutron_shield" in data.modules) {
hasAdditionalData = true;
const shield = data.modules["nuclearcraft:overhaul_sfr:neutron_shield"];
yield "Neutron Shield";
yield `Efficiency: ${shield.efficiency}`;
yield `Heat per Flux: ${shield.heat_per_flux}`;
}
if (!hasAdditionalData) {
console.log("unknown block", data);
}
yield {
type: "italics",
text: metadata.configurationAddon.name,
};
}
function parseRuleAsTooltip(rule, props) {
switch (rule.type) {
case "and":
return rule.rules.map(r => parseRuleAsTooltip(r, props)).join(" AND ");
case "or":
return rule.rules.map(r => parseRuleAsTooltip(r, props)).join(" OR ");
case "between": {
const block = resolveBlockName(rule.block, props);
if (!block) {
console.warn("could not find block %o", rule.block);
return "???";
}
if (rule.min === rule.max) {
return `exactly ${rule.min} ${block}`;
} else if (rule.max === 6) {
return `at least ${rule.min} ${block}`;
} else if (rule.min === 0) {
return `no more than ${rule.max} ${block}`;
} else {
return `between ${rule.min} and ${rule.max} ${block}`;
}
}
case "axial": {
const block = resolveBlockName(rule.block, props);
if (!block) {
console.warn("could not find block %o", rule.block);
return "???";
}
if (rule.min === rule.max) {
return `exactly ${rule.min} axial ${block}`;
} else if (rule.max === 3) {
return `at least ${rule.min} axial ${block}`;
} else if (rule.min === 0) {
return `no more than ${rule.max} axial ${block}`;
} else {
return `between ${rule.min} and ${rule.max} axial ${block}`;
}
}
default:
console.warn("unknown rule: %o", rule);
return "???";
}
}
export function getBlockPickerActiveItem() {
const blockPickerDom = document.getElementById("block-picker-list");
return Number(blockPickerDom.dataset.activeItem ?? -1);
}
export function getBlockPickerRecipe() {
return activeItemRecipe;
}
export function setBlockPickerRecipe(recipe) {
activeItemRecipe = recipe;
}