Compare commits
No commits in common. "ddd74a262c1560c44ee90c9e5efbe5d4402dd95e" and "36dafc72046b2e0053f8806ddf6065ee66e45a0d" have entirely different histories.
ddd74a262c
...
36dafc7204
39
index.html
39
index.html
|
|
@ -27,12 +27,11 @@
|
||||||
<button data-alt="i" data-button="planner:zoom:+">Zoom in</button>
|
<button data-alt="i" data-button="planner:zoom:+">Zoom in</button>
|
||||||
<button data-alt="o" data-button="planner:zoom:-">Zoom out</button>
|
<button data-alt="o" data-button="planner:zoom:-">Zoom out</button>
|
||||||
<button data-alt="c" data-toggle="planner:toggle-casing" data-toggled="false">Show casing</button>
|
<button data-alt="c" data-toggle="planner:toggle-casing" data-toggled="false">Show casing</button>
|
||||||
<button data-alt="c" data-button="dialogs:welcome">Show welcome</button>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<aside id="left-menu" data-state="planning">
|
<aside id="left-menu" data-state="planning">
|
||||||
<span data-resize="left-menu"></span>
|
<span data-resize="left-menu"></span>
|
||||||
<section id="block-picker" data-state="planning">
|
<section id="block-picker">
|
||||||
<label>
|
<label>
|
||||||
Filter:
|
Filter:
|
||||||
<input data-input="block-picker:filter">
|
<input data-input="block-picker:filter">
|
||||||
|
|
@ -44,9 +43,6 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
<aside id="bottom-menu" data-resize-max="0.8">
|
|
||||||
<span data-resize="bottom-menu"></span>
|
|
||||||
</aside>
|
|
||||||
<main>
|
<main>
|
||||||
<div data-state="initialization">
|
<div data-state="initialization">
|
||||||
Select configuration file:
|
Select configuration file:
|
||||||
|
|
@ -82,33 +78,12 @@
|
||||||
</main>
|
</main>
|
||||||
<aside id="right-menu">
|
<aside id="right-menu">
|
||||||
<span data-resize="right-menu"></span>
|
<span data-resize="right-menu"></span>
|
||||||
<section id="recipe-selector" data-state="planning">
|
</aside>
|
||||||
<template id="recipe-selector:template">
|
<aside id="bottom-menu">
|
||||||
<div>
|
<span data-resize="bottom-menu"></span>
|
||||||
<header data-text="recipe:type"></header>
|
|
||||||
<div class="recipe">
|
|
||||||
<div data-atlas="recipe:selected"></div>
|
|
||||||
<div data-text="recipe:selected"></div>
|
|
||||||
</div>
|
|
||||||
<input placeholder="Filter" type="text" data-input="recipe:filter">
|
|
||||||
<div class="select">
|
|
||||||
<template>
|
|
||||||
<div class="recipe">
|
|
||||||
<div data-atlas="recipe:item"></div>
|
|
||||||
<div data-text="recipe:item"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div id="recipe-selector:design" class="recipe-selector">
|
|
||||||
</div>
|
|
||||||
<div id="recipe-selector:selection" class="recipe-selector">
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</aside>
|
</aside>
|
||||||
<footer>
|
<footer>
|
||||||
<div>pre-alpha 1.1</div>
|
<div>pre-alpha 1.0</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://discord.gg/Eq72HwPqfg">Discord server</a>
|
<a href="https://discord.gg/Eq72HwPqfg">Discord server</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -128,10 +103,8 @@
|
||||||
Roadmap:
|
Roadmap:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Block and reactor recipe selection - WIP</li>
|
<li>Block and reactor recipe selection</li>
|
||||||
<li>Reactor tooltips</li>
|
|
||||||
<li>Reactor verification and stats</li>
|
<li>Reactor verification and stats</li>
|
||||||
<li>Casing improvements</li>
|
|
||||||
<li>Keyboard shortcuts</li>
|
<li>Keyboard shortcuts</li>
|
||||||
<li>MSR support</li>
|
<li>MSR support</li>
|
||||||
<li>Turbine support</li>
|
<li>Turbine support</li>
|
||||||
|
|
|
||||||
|
|
@ -1,199 +0,0 @@
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
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: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);
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { recall, save } from "./storage.js";
|
||||||
|
|
||||||
registerLoadCallback(() => {
|
registerLoadCallback(() => {
|
||||||
welcomeDialog();
|
welcomeDialog();
|
||||||
listenTo("dialogs:welcome", () => welcomeDialog(true));
|
|
||||||
listenTo("fileLoad:chooseDesign", designChoiceDialog);
|
listenTo("fileLoad:chooseDesign", designChoiceDialog);
|
||||||
listenTo("fileSave:nameDesign", designNameDialog);
|
listenTo("fileSave:nameDesign", designNameDialog);
|
||||||
});
|
});
|
||||||
|
|
@ -43,11 +42,8 @@ function initializeDialog(dialogName, endEvent, { emptyValue, onDismiss } = {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function welcomeDialog(force = false) {
|
function welcomeDialog() {
|
||||||
if (recall("hasSeenWelcome", false) && !force) {
|
if (recall("hasSeenWelcome", false)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeDialog("welcome", "welcome:close", {
|
initializeDialog("welcome", "welcome:close", {
|
||||||
onDismiss: () => {
|
onDismiss: () => {
|
||||||
save("hasSeenWelcome", true);
|
save("hasSeenWelcome", true);
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,9 @@ export class DomList {
|
||||||
list;
|
list;
|
||||||
template;
|
template;
|
||||||
|
|
||||||
constructor(rootElem, templateElement = undefined) {
|
constructor(rootElem) {
|
||||||
this.list = rootElem;
|
this.list = rootElem;
|
||||||
this.template = (templateElement ?? this.list.querySelector("template"))
|
this.template = this.list.querySelector("template").content.firstElementChild;
|
||||||
.content.firstElementChild;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
|
|
|
||||||
|
|
@ -157,10 +157,10 @@ export async function saveFile(name = undefined) {
|
||||||
|
|
||||||
if (multiblockType === "nuclearcraft:overhaul_sfr") {
|
if (multiblockType === "nuclearcraft:overhaul_sfr") {
|
||||||
const coolantRecipes = [];
|
const coolantRecipes = [];
|
||||||
let coolantRecipeIndex;
|
let coolantRecipeIndex = -1;
|
||||||
const plannerMetadata = getSolverMetadata(getPlannerDom());
|
const plannerMetadata = getSolverMetadata(getPlannerDom());
|
||||||
|
|
||||||
coolantRecipes.push(stripEntryToMinimums(plannerMetadata.coolant_recipe));
|
coolantRecipes.push(stripEntryToMinimums(plannerMetadata["coolant_recipe"]));
|
||||||
coolantRecipeIndex = 0;
|
coolantRecipeIndex = 0;
|
||||||
|
|
||||||
fileData = {
|
fileData = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import "./atlas.js";
|
import "./atlas.js";
|
||||||
import "./block-picker.js";
|
|
||||||
import { loadConfiguration } from "./configuration.js";
|
import { loadConfiguration } from "./configuration.js";
|
||||||
import "./dialogs.js";
|
import "./dialogs.js";
|
||||||
import "./dom-list.js";
|
import "./dom-list.js";
|
||||||
|
|
@ -10,7 +9,6 @@ import "./js-utils.js"
|
||||||
import "./menu-bar.js";
|
import "./menu-bar.js";
|
||||||
import "./ncpf-utils.js";
|
import "./ncpf-utils.js";
|
||||||
import "./planner.js";
|
import "./planner.js";
|
||||||
import "./recipe-selector.js";
|
|
||||||
import "./resize-menus.js";
|
import "./resize-menus.js";
|
||||||
import "./solver.js";
|
import "./solver.js";
|
||||||
import { getState, setState, STATE } from "./state.js";
|
import { getState, setState, STATE } from "./state.js";
|
||||||
|
|
@ -85,7 +83,7 @@ window.addEventListener("load", () => {
|
||||||
|
|
||||||
emitEvent("setup-end");
|
emitEvent("setup-end");
|
||||||
|
|
||||||
void loadConfiguration("addons");
|
void loadConfiguration("default");
|
||||||
|
|
||||||
// awaitEvent("config:end").then(() => loadExampleFile("tiny reactor"));
|
// awaitEvent("config:end").then(() => loadExampleFile("tiny reactor"));
|
||||||
// awaitEvent("config:end").then(() => loadExampleFile("multi-reactor"));
|
// awaitEvent("config:end").then(() => loadExampleFile("multi-reactor"));
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { registerLoadCallback } from "./main.js";
|
||||||
let activeMenuBar = null;
|
let activeMenuBar = null;
|
||||||
|
|
||||||
registerLoadCallback(() => {
|
registerLoadCallback(() => {
|
||||||
|
console.log("menubar-load");
|
||||||
document.querySelectorAll(".menu-bar").forEach(menuBar => {
|
document.querySelectorAll(".menu-bar").forEach(menuBar => {
|
||||||
menuBar.querySelectorAll("button").forEach(button => {
|
menuBar.querySelectorAll("button").forEach(button => {
|
||||||
button.setAttribute("tabindex", "-1");
|
button.setAttribute("tabindex", "-1");
|
||||||
|
|
@ -71,6 +72,7 @@ registerLoadCallback(() => {
|
||||||
|
|
||||||
|
|
||||||
function handleGlobalClick() {
|
function handleGlobalClick() {
|
||||||
|
console.log("Global clicked");
|
||||||
if (!activeMenuBar) return;
|
if (!activeMenuBar) return;
|
||||||
activeMenuBar.classList.remove("active");
|
activeMenuBar.classList.remove("active");
|
||||||
activeMenuBar.querySelectorAll(".active")
|
activeMenuBar.querySelectorAll(".active")
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { makeAtlasImage } from "./atlas.js";
|
import { atlases, makeAtlasImage } from "./atlas.js";
|
||||||
import { DomList } from "./dom-list.js";
|
import { DomList } from "./dom-list.js";
|
||||||
import { getElements } from "./dom-utils.js";
|
import { getElements } from "./dom-utils.js";
|
||||||
import { emitEvent, listenTo } from "./events.js";
|
import { emitEvent, listenTo } from "./events.js";
|
||||||
|
import { Filter } from "./filter.js";
|
||||||
import { initialize3dArray } from "./js-utils.js";
|
import { initialize3dArray } from "./js-utils.js";
|
||||||
import { recall, save } from "./storage.js";
|
import { recall, save } from "./storage.js";
|
||||||
|
import { createTooltip, tooltipDataToPlain } from "./tooltip.js";
|
||||||
|
import { lookupFromMetadata, resolveBlockName } from "./ncpf-utils.js";
|
||||||
import { registerLoadCallback } from "./main.js";
|
import { registerLoadCallback } from "./main.js";
|
||||||
import { getBlockPickerActiveItem } from "./block-picker.js";
|
|
||||||
|
|
||||||
const zoomOptions = [ 0.5, 1, 2, 3, 4, 5 ];
|
const zoomOptions = [ 0.5, 1, 2, 3, 4, 5 ];
|
||||||
const zoomDefault = 2;
|
const zoomDefault = 2;
|
||||||
|
|
@ -14,10 +16,14 @@ const zoomStorageKey = "zoom-size";
|
||||||
let suppressHover = false;
|
let suppressHover = false;
|
||||||
let multiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
|
let multiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
|
||||||
let multiblockType = "nuclearcraft:overhaul_sfr";
|
let multiblockType = "nuclearcraft:overhaul_sfr";
|
||||||
|
let activeItem = -1;
|
||||||
|
|
||||||
registerLoadCallback(() => {
|
registerLoadCallback(() => {
|
||||||
|
listenTo("config:end", buildBlockPicker);
|
||||||
listenTo("config:end", buildPlanner);
|
listenTo("config:end", buildPlanner);
|
||||||
|
|
||||||
|
listenTo("block-picker:filter:input", buildBlockPicker);
|
||||||
|
|
||||||
listenTo("planner:toggle-casing", e => {
|
listenTo("planner:toggle-casing", e => {
|
||||||
const planner = document.getElementById("planner");
|
const planner = document.getElementById("planner");
|
||||||
planner.dataset.casing = e.detail.state ? "show" : "hide";
|
planner.dataset.casing = e.detail.state ? "show" : "hide";
|
||||||
|
|
@ -71,7 +77,6 @@ registerLoadCallback(() => {
|
||||||
|
|
||||||
save(zoomStorageKey, scale);
|
save(zoomStorageKey, scale);
|
||||||
});
|
});
|
||||||
|
|
||||||
listenTo("block-picker:selection", clearSelectedCells);
|
listenTo("block-picker:selection", clearSelectedCells);
|
||||||
|
|
||||||
document.addEventListener("keydown", e => {
|
document.addEventListener("keydown", e => {
|
||||||
|
|
@ -82,6 +87,44 @@ registerLoadCallback(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 === multiblockType && 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");
|
||||||
|
}
|
||||||
|
item.appendChild(makeAtlasImage(m.index));
|
||||||
|
item.addEventListener("click", listClickEvent);
|
||||||
|
grid.addItem(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function listClickEvent(e) {
|
||||||
|
const items = document.querySelectorAll("#block-picker-list [data-index]");
|
||||||
|
if (e.currentTarget.dataset.active !== undefined) {
|
||||||
|
items.forEach(i => delete i.dataset.active);
|
||||||
|
activeItem = -1;
|
||||||
|
delete e.currentTarget.dataset.active;
|
||||||
|
emitEvent("block-picker:selection", activeItem);
|
||||||
|
emitEvent("block-picker:selection:clear");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
items.forEach(i => delete i.dataset.active);
|
||||||
|
activeItem = Number(e.currentTarget.dataset.index);
|
||||||
|
e.currentTarget.dataset.active = "";
|
||||||
|
emitEvent("block-picker:selection", activeItem);
|
||||||
|
}
|
||||||
|
|
||||||
function buildPlanner(textures) {
|
function buildPlanner(textures) {
|
||||||
// handle case when 1st argument is event
|
// handle case when 1st argument is event
|
||||||
if (!Array.isArray(textures)) textures = undefined;
|
if (!Array.isArray(textures)) textures = undefined;
|
||||||
|
|
@ -133,7 +176,7 @@ function buildPlanner(textures) {
|
||||||
|
|
||||||
function cellHoverEvent(e) {
|
function cellHoverEvent(e) {
|
||||||
if (suppressHover) return;
|
if (suppressHover) return;
|
||||||
if (getBlockPickerActiveItem() === -1) return;
|
if (activeItem === -1) return;
|
||||||
if (document.body.dataset.resizing) return;
|
if (document.body.dataset.resizing) return;
|
||||||
|
|
||||||
const cells = document.querySelectorAll("#planner [data-x]");
|
const cells = document.querySelectorAll("#planner [data-x]");
|
||||||
|
|
@ -176,7 +219,6 @@ function cellClickEvent(e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeItem = getBlockPickerActiveItem();
|
|
||||||
if (activeItem === -1) {
|
if (activeItem === -1) {
|
||||||
cellSelectEvent(e);
|
cellSelectEvent(e);
|
||||||
return;
|
return;
|
||||||
|
|
@ -297,3 +339,145 @@ export function getPlannerCellDom(x, y, z) {
|
||||||
|
|
||||||
export const getPlannerMultiblockType = () => multiblockType;
|
export const getPlannerMultiblockType = () => multiblockType;
|
||||||
export const getPlannerMultiblockSize = () => multiblockSize;
|
export const getPlannerMultiblockSize = () => multiblockSize;
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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: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 "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
import { registerLoadCallback } from "./main.js";
|
|
||||||
import { listenTo } from "./events.js";
|
|
||||||
import { configuration } from "./configuration.js";
|
|
||||||
import { getPlannerDom, getPlannerMultiblockType } from "./planner.js";
|
|
||||||
import { DomList } from "./dom-list.js";
|
|
||||||
import { getAtlasIndexFromMetadata, makeAtlasImage } from "./atlas.js";
|
|
||||||
import { getSolverMetadata, setSolverMetadata } from "./solver.js";
|
|
||||||
import { findFullEntry } from "./ncpf-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);
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
designItem.querySelectorAll('[data-text="recipe:type"]')
|
|
||||||
.forEach(e => e.innerText = "Coolant Recipe");
|
|
||||||
|
|
||||||
const plannerDom = getPlannerDom();
|
|
||||||
const plannerMetadata = getSolverMetadata(plannerDom);
|
|
||||||
let selectedRecipe = plannerMetadata?.coolant_recipe;
|
|
||||||
if (!plannerMetadata) {
|
|
||||||
selectedRecipe = defaultRecipes["nuclearcraft:overhaul_sfr"].coolant_recipe;
|
|
||||||
setSolverMetadata(plannerDom, {
|
|
||||||
coolant_recipe: selectedRecipe,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
selectedRecipe = findFullEntry("coolant_recipes", selectedRecipe, { configurationHint: multiblockType });
|
|
||||||
|
|
||||||
const selectedRecipeName = selectedRecipe.modules["plannerator:display_name"].display_name;
|
|
||||||
const selectedAtlasId = getAtlasIndexFromMetadata(selectedRecipe, multiblockType, "coolant_recipes");
|
|
||||||
designItem.querySelectorAll('[data-text="recipe:selected"]')
|
|
||||||
.forEach(e => e.innerText = selectedRecipeName);
|
|
||||||
designItem.querySelectorAll('[data-atlas="recipe:selected"]')
|
|
||||||
.forEach(e => e.appendChild(makeAtlasImage(selectedAtlasId)));
|
|
||||||
|
|
||||||
const recipeList = new DomList(designItem.querySelector(":scope > .select"));
|
|
||||||
recipeList.clear();
|
|
||||||
for (let i = 0; i < coolantRecipes.length; i++) {
|
|
||||||
const recipe = coolantRecipes[i];
|
|
||||||
const recipeItem = recipeList.makeItem();
|
|
||||||
|
|
||||||
const recipeName = recipe.modules["plannerator:display_name"].display_name;
|
|
||||||
const atlasId = getAtlasIndexFromMetadata(recipe, multiblockType, "coolant_recipes");
|
|
||||||
|
|
||||||
recipeItem.querySelectorAll('[data-text="recipe:item"]')
|
|
||||||
.forEach(e => e.innerText = recipeName);
|
|
||||||
recipeItem.querySelectorAll('[data-atlas="recipe:item"]')
|
|
||||||
.forEach(e => e.appendChild(makeAtlasImage(atlasId)));
|
|
||||||
|
|
||||||
console.log(recipe, recipeItem, atlasId);
|
|
||||||
recipeList.addItem(recipeItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
designRecipes.addItem(designItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,10 +20,7 @@ function startResize(e) {
|
||||||
|
|
||||||
function resize(e) {
|
function resize(e) {
|
||||||
const resizing = document.body.dataset.resizing;
|
const resizing = document.body.dataset.resizing;
|
||||||
const element = document.getElementById(resizing);
|
const elementBoundingBox = document.getElementById(resizing).getBoundingClientRect();
|
||||||
const sizeMin = Number(element.dataset.resizeMin ?? 0.05);
|
|
||||||
const sizeMax = Number(element.dataset.resizeMax ?? 0.5);
|
|
||||||
const elementBoundingBox = element.getBoundingClientRect();
|
|
||||||
let size = 0.25;
|
let size = 0.25;
|
||||||
switch (resizing) {
|
switch (resizing) {
|
||||||
case "left-menu":
|
case "left-menu":
|
||||||
|
|
@ -36,7 +33,7 @@ function resize(e) {
|
||||||
size = 1 - (e.pageY + window.innerHeight - elementBoundingBox.bottom) / window.innerHeight;
|
size = 1 - (e.pageY + window.innerHeight - elementBoundingBox.bottom) / window.innerHeight;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
size = Math.max(sizeMin, Math.min(size, sizeMax));
|
size = Math.max(0.05, Math.min(size, 0.5));
|
||||||
document.body.style.setProperty(`--${resizing}-size`, `${size*100}%`);
|
document.body.style.setProperty(`--${resizing}-size`, `${size*100}%`);
|
||||||
save(`${resizing}-size`, `${size*100}%`);
|
save(`${resizing}-size`, `${size*100}%`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
style.css
28
style.css
|
|
@ -67,28 +67,22 @@ body {
|
||||||
#left-menu {
|
#left-menu {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
border-right: 1px solid var(--menu-border-color);
|
border-right: 1px solid var(--menu-border-color)
|
||||||
}
|
|
||||||
#bottom-menu {
|
|
||||||
grid-column: 1;
|
|
||||||
grid-row: 3;
|
|
||||||
border-top: 1px solid var(--menu-border-color);
|
|
||||||
border-right: 1px solid var(--menu-border-color);
|
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
grid-row: 2 / 4;
|
grid-row: 2;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
#right-menu {
|
#right-menu {
|
||||||
grid-column: 3;
|
grid-column: 3;
|
||||||
grid-row: 2 / 4;
|
grid-row: 2;
|
||||||
border-left: 1px solid var(--menu-border-color);
|
border-left: 1px solid var(--menu-border-color)
|
||||||
}
|
}
|
||||||
#bottom-menu {
|
#bottom-menu {
|
||||||
grid-column: 1;
|
grid-column: 1 / -1;
|
||||||
grid-row: 3;
|
grid-row: 3;
|
||||||
border-top: 1px solid var(--menu-border-color);
|
border-top: 1px solid var(--menu-border-color)
|
||||||
}
|
}
|
||||||
body > footer {
|
body > footer {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
|
@ -119,7 +113,7 @@ body > footer {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
#left-menu > [data-resize], #right-menu > [data-resize] {
|
#left-menu [data-resize], #right-menu [data-resize] {
|
||||||
border-left: 1px solid var(--resize-handle-color);
|
border-left: 1px solid var(--resize-handle-color);
|
||||||
border-right: 1px solid var(--resize-handle-color);
|
border-right: 1px solid var(--resize-handle-color);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
@ -128,15 +122,15 @@ body > footer {
|
||||||
width: calc(var(--resize-handle-width) * 2 - 1px);
|
width: calc(var(--resize-handle-width) * 2 - 1px);
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
#left-menu > [data-resize] {
|
#left-menu [data-resize] {
|
||||||
cursor: w-resize;
|
cursor: w-resize;
|
||||||
right: calc(var(--resize-handle-width) * -1);
|
right: calc(var(--resize-handle-width) * -1);
|
||||||
}
|
}
|
||||||
#right-menu > [data-resize] {
|
#right-menu [data-resize] {
|
||||||
cursor: e-resize;
|
cursor: e-resize;
|
||||||
left: calc(var(--resize-handle-width) * -1);
|
left: calc(var(--resize-handle-width) * -1);
|
||||||
}
|
}
|
||||||
#bottom-menu > [data-resize] {
|
#bottom-menu [data-resize] {
|
||||||
border-top: 1px solid var(--resize-handle-color);
|
border-top: 1px solid var(--resize-handle-color);
|
||||||
border-bottom: 1px solid var(--resize-handle-color);
|
border-bottom: 1px solid var(--resize-handle-color);
|
||||||
cursor: s-resize;
|
cursor: s-resize;
|
||||||
|
|
@ -262,8 +256,6 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
||||||
outline: calc(var(--scale-factor) * 1px) solid var(--block-hover-outline-color, #6666);
|
outline: calc(var(--scale-factor) * 1px) solid var(--block-hover-outline-color, #6666);
|
||||||
}
|
}
|
||||||
|
|
||||||
#recipe-selector {}
|
|
||||||
|
|
||||||
[data-tooltip] {
|
[data-tooltip] {
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue