implement metadata controlled block overlays (WIP)
This commit is contained in:
parent
521ff4e757
commit
a46f8d85a0
|
|
@ -37,6 +37,7 @@
|
|||
<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="c" data-toggle="planner:toggle-casing" data-toggled="false">Show casing</button>
|
||||
<button data-alt="r" data-toggle="planner:toggle-block-recipe-overlay" data-toggled="true">Show block recipes</button>
|
||||
<button data-alt="c" data-button="dialogs:welcome">Show welcome</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
@ -90,7 +91,7 @@
|
|||
<template>
|
||||
<div class="grid m2">
|
||||
<template>
|
||||
<div></div>
|
||||
<div class="planner--cell"></div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -125,7 +126,7 @@
|
|||
</section>
|
||||
</aside>
|
||||
<footer>
|
||||
<div>pre-alpha 1.4</div>
|
||||
<div>pre-alpha 1.5</div>
|
||||
<div>
|
||||
<a href="https://discord.gg/Eq72HwPqfg">Discord server</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { emitEvent } from "./events.js";
|
||||
import { checkEquivalence } from "./ncpf-utils.js";
|
||||
import { registerLoadCallback } from "./main.js";
|
||||
import { getPlannerMultiblockType } from "./planner.js";
|
||||
|
||||
const defaultSize = 16; // px
|
||||
|
||||
|
|
@ -192,7 +193,10 @@ export function getAtlasMetadata(index) {
|
|||
return null;
|
||||
}
|
||||
|
||||
export function getAtlasIndexFromMetadata(metadata, configuration, configurationList = null) {
|
||||
export function getAtlasIndexFromMetadata(metadata, configuration = null, configurationList = null) {
|
||||
if (!configuration) {
|
||||
configuration = getPlannerMultiblockType();
|
||||
}
|
||||
let index = 0;
|
||||
for (const atlas of atlases) {
|
||||
for (const atlasItemMeta of atlas.metadata) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { deleteTooltip } from "./tooltip.js";
|
||||
import { deleteSolverMetadata } from "./metadata.js";
|
||||
import { deleteMetadata } from "./metadata.js";
|
||||
|
||||
export class DomList {
|
||||
list;
|
||||
|
|
@ -15,7 +15,7 @@ export class DomList {
|
|||
this.list.querySelectorAll("[data-tooltip]")
|
||||
.forEach(e => deleteTooltip(Number(e.dataset.tooltip)));
|
||||
this.list.querySelectorAll("[data-solver-metadata]")
|
||||
.forEach(e => deleteSolverMetadata(Number(e.dataset.solverMetadata)));
|
||||
.forEach(e => deleteMetadata(Number(e.dataset.solverMetadata)));
|
||||
this.list.replaceChildren(...this.list.querySelectorAll(":scope > template, :scope > [data-keep]"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { getAtlasIndexFromMetadata, getAtlasMetadata } from "./atlas.js";
|
|||
import { resetSolver } from "./solver.js";
|
||||
import { registerLoadCallback } from "./main.js";
|
||||
import { DevError, UserError } from "./errors.js";
|
||||
import { getSolverMetadata } from "./metadata.js";
|
||||
import { getMetadata } from "./metadata.js";
|
||||
|
||||
registerLoadCallback(() => {
|
||||
listenTo("file:load", () => showOpenFileDialog(({ data, filename }) => loadFile(data, filename)));
|
||||
|
|
@ -132,7 +132,7 @@ export async function saveFile(name = undefined) {
|
|||
if (!fullBlock || !fullBlock.modules || !("ncpf:block_recipes" in fullBlock.modules)) return;
|
||||
|
||||
const designIndex = designBlocksAtlasMap[Number(designAsCellData[x][y][z].tile)].designIndex;
|
||||
const solverMetadata = getSolverMetadata(designAsCellData[x][y][z].solverMetadata);
|
||||
const solverMetadata = getMetadata(designAsCellData[x][y][z].solverMetadata);
|
||||
const minimizedBlock = minimizedBlocks[designIndex];
|
||||
let recipeIndex = -1;
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ export async function saveFile(name = undefined) {
|
|||
if (multiblockType === "nuclearcraft:overhaul_sfr") {
|
||||
const coolantRecipes = [];
|
||||
let coolantRecipeIndex;
|
||||
const plannerMetadata = getSolverMetadata(getPlannerDom());
|
||||
const plannerMetadata = getMetadata(getPlannerDom());
|
||||
|
||||
coolantRecipes.push(stripEntryToMinimums(plannerMetadata.coolant_recipe));
|
||||
coolantRecipeIndex = 0;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import "./js-utils.js"
|
|||
import "./menu-bar.js";
|
||||
import "./metadata.js";
|
||||
import "./ncpf-utils.js";
|
||||
import "./overlays.js";
|
||||
import "./planner.js";
|
||||
import "./planner-resize.js";
|
||||
import "./recipe-selector.js";
|
||||
|
|
|
|||
|
|
@ -1,38 +1,58 @@
|
|||
import { clearCellOverlays, processCellOverlays } from "./overlays.js";
|
||||
|
||||
const solverMetadata = {};
|
||||
let solverMetadataCounter = 0;
|
||||
function createSolverMetadata(data) {
|
||||
const id = solverMetadataCounter;
|
||||
solverMetadata[id] = data;
|
||||
solverMetadataCounter++;
|
||||
const metadata = {};
|
||||
let metadataCounter = 0;
|
||||
function createMetadata(data) {
|
||||
const id = metadataCounter;
|
||||
metadata[id] = data;
|
||||
metadataCounter++;
|
||||
return id;
|
||||
}
|
||||
export function setSolverMetadata(domNode, data) {
|
||||
export function setDomMetadata(domNode, data) {
|
||||
if (domNode.dataset.solverMetadata) {
|
||||
solverMetadata[Number(domNode.dataset.solverMetadata)] = data;
|
||||
metadata[Number(domNode.dataset.solverMetadata)] = data;
|
||||
return;
|
||||
}
|
||||
domNode.dataset.solverMetadata = createSolverMetadata(data).toString(10);
|
||||
domNode.dataset.solverMetadata = createMetadata(data).toString(10);
|
||||
}
|
||||
export function getSolverMetadata(id) {
|
||||
export function setCellMetadata(node, key, value) {
|
||||
if (!node.dataset.solverMetadata) {
|
||||
node.dataset.solverMetadata = createMetadata({ [key]: value }).toString(10);
|
||||
} else {
|
||||
metadata[Number(node.dataset.solverMetadata)][key] = value;
|
||||
}
|
||||
processCellOverlays(node, key, true);
|
||||
}
|
||||
|
||||
export function getMetadata(id) {
|
||||
if (typeof id === "object") {
|
||||
// assume DOM element
|
||||
if (id.dataset && id.dataset.solverMetadata) {
|
||||
return getSolverMetadata(id.dataset.solverMetadata);
|
||||
return getMetadata(id.dataset.solverMetadata);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (typeof id === "string") {
|
||||
return getSolverMetadata(Number(id));
|
||||
return getMetadata(Number(id));
|
||||
}
|
||||
return solverMetadata[id];
|
||||
return metadata[id];
|
||||
}
|
||||
export function deleteSolverMetadata(id) {
|
||||
export function getCellMetadata(node, key) {
|
||||
return metadata[Number(node.dataset.solverMetadata)][key];
|
||||
}
|
||||
|
||||
export function deleteMetadata(id) {
|
||||
if (typeof id === "object") {
|
||||
// assume DOM object
|
||||
if (id.dataset && id.dataset.solverMetadata) {
|
||||
return deleteSolverMetadata(id.dataset.solverMetadata);
|
||||
clearCellOverlays(id);
|
||||
return deleteMetadata(id.dataset.solverMetadata);
|
||||
}
|
||||
}
|
||||
delete solverMetadata[id];
|
||||
delete metadata[id];
|
||||
}
|
||||
export function clearCellMetadata(node, key) {
|
||||
if (!node.dataset.solverMetadata) return;
|
||||
delete metadata[Number(node.dataset.solverMetadata)][key];
|
||||
processCellOverlays(node, key, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { getCellMetadata } from "./metadata.js";
|
||||
import { getAtlasIndexFromMetadata, makeAtlasImage } from "./atlas.js";
|
||||
|
||||
export function processCellOverlays(node, key = null, added = null) {
|
||||
if (key) {
|
||||
const metadata = getCellMetadata(node, key);
|
||||
const atlasIndex = getAtlasIndexFromMetadata(metadata);
|
||||
if (atlasIndex && atlasIndex !== -1) {
|
||||
const overlayNode = makeAtlasImage(atlasIndex);
|
||||
overlayNode.dataset.overlay = key;
|
||||
overlayNode.classList.add("planner--overlay");
|
||||
node.appendChild(overlayNode);
|
||||
}
|
||||
// FIXME: handle remove
|
||||
}
|
||||
// TODO: handle generic update
|
||||
}
|
||||
|
||||
export function clearCellOverlays(node) {
|
||||
// TODO
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import {
|
|||
createPlannerPlane,
|
||||
getPlannerDom,
|
||||
getPlannerMultiblockSize, getPlannerMultiblockType,
|
||||
maxMultiblockSize
|
||||
maxMultiblockSize, minMultiblockSize
|
||||
} from "./planner.js";
|
||||
import { DomList } from "./dom-list.js";
|
||||
|
||||
|
|
@ -133,20 +133,20 @@ function initializeResize() {
|
|||
newDiv.dataset.casing = "";
|
||||
}
|
||||
|
||||
if (!casing && size[1] > maxMultiblockSize[type][1]) {
|
||||
if (!casing && size[1] > minMultiblockSize[type][1]) {
|
||||
newDiv.appendChild(createButton({ axis: "y", add: false, n: gridIndex }));
|
||||
}
|
||||
if (!casing) {
|
||||
for (let x = 0; x < size[0] - 1 && size[0] < maxMultiblockSize[type][0]; x++) {
|
||||
newDiv.appendChild(createButton({ axis: "x", add: true, n: x+1, topLeft: true }));
|
||||
}
|
||||
for (let x = 0; x < size[0] - 2 && size[0] > maxMultiblockSize[type][0]; x++) {
|
||||
for (let x = 0; x < size[0] - 2 && size[0] > minMultiblockSize[type][0]; x++) {
|
||||
newDiv.appendChild(createButton({ axis: "x", add: false, n: x+1, topLeft: true }));
|
||||
}
|
||||
for (let z = 0; z < size[2] - 1 && size[2] < maxMultiblockSize[type][2]; z++) {
|
||||
newDiv.appendChild(createButton({ axis: "z", add: true, n: z+1, topLeft: true }));
|
||||
}
|
||||
for (let z = 0; z < size[2] - 2 && size[2] > maxMultiblockSize[type][2]; z++) {
|
||||
for (let z = 0; z < size[2] - 2 && size[2] > minMultiblockSize[type][2]; z++) {
|
||||
newDiv.appendChild(createButton({ axis: "z", add: false, n: z+1, topLeft: true }));
|
||||
}
|
||||
}
|
||||
|
|
@ -158,13 +158,13 @@ function initializeResize() {
|
|||
for (let x = 0; x < size[0] - 1 && size[0] < maxMultiblockSize[type][0]; x++) {
|
||||
newDiv.appendChild(createButton({ axis: "x", add: true, n: x+1, topLeft: false }));
|
||||
}
|
||||
for (let x = 0; x < size[0] - 2 && size[0] > maxMultiblockSize[type][0]; x++) {
|
||||
for (let x = 0; x < size[0] - 2 && size[0] > minMultiblockSize[type][0]; x++) {
|
||||
newDiv.appendChild(createButton({ axis: "x", add: false, n: x+1, topLeft: false }));
|
||||
}
|
||||
for (let z = 0; z < size[2] - 1 && size[2] < maxMultiblockSize[type][2]; z++) {
|
||||
newDiv.appendChild(createButton({ axis: "z", add: true, n: z+1, topLeft: false }));
|
||||
}
|
||||
for (let z = 0; z < size[2] - 2 && size[2] > maxMultiblockSize[type][2]; z++) {
|
||||
for (let z = 0; z < size[2] - 2 && size[2] > minMultiblockSize[type][2]; z++) {
|
||||
newDiv.appendChild(createButton({ axis: "z", add: false, n: z+1, topLeft: false }));
|
||||
}
|
||||
}
|
||||
|
|
@ -186,13 +186,14 @@ function fixPlannerNumbering() {
|
|||
const planner = getPlannerDom();
|
||||
const size = getPlannerMultiblockSize();
|
||||
const grids = planner.querySelectorAll(":scope > .planner--resize > .grid");
|
||||
grids.forEach(grid => {
|
||||
grids.forEach((grid, y) => {
|
||||
grid.style.setProperty("--width", size[0].toString(10));
|
||||
const tiles = [...grid.querySelectorAll("[data-tile]")];
|
||||
for (let z = 0; z < size[2]; z++) {
|
||||
for (let x = 0; x < size[0]; x++) {
|
||||
const tile = tiles.shift();
|
||||
tile.dataset.x = x.toString(10);
|
||||
tile.dataset.y = y.toString(10);
|
||||
tile.dataset.z = z.toString(10);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { initialize3dArray } from "./js-utils.js";
|
|||
import { recall, save } from "./storage.js";
|
||||
import { registerLoadCallback } from "./main.js";
|
||||
import { getBlockPickerActiveItem, getBlockPickerRecipe } from "./block-picker.js";
|
||||
import { deleteSolverMetadata, getSolverMetadata, setSolverMetadata } from "./metadata.js";
|
||||
import { clearCellMetadata, getMetadata, setCellMetadata } from "./metadata.js";
|
||||
import { retainTooltipHover } from "./tooltip.js";
|
||||
import { checkEquivalence, lookupFromMetadata } from "./ncpf-utils.js";
|
||||
|
||||
|
|
@ -17,6 +17,9 @@ const zoomStorageKey = "zoom-size";
|
|||
const casingStorageKey = "casing";
|
||||
const casingDefault = false; // hidden by default
|
||||
|
||||
const recipeOverlayStorageKey = "recipeOverlay";
|
||||
const recipeOverlayDefault = true; // shown by default
|
||||
|
||||
const defaultMultiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
|
||||
const defaultMultiblockType = "nuclearcraft:overhaul_sfr";
|
||||
export const maxMultiblockSize = {
|
||||
|
|
@ -42,6 +45,15 @@ registerLoadCallback(() => {
|
|||
document.body.dataset.casing = e.detail.state ? "show" : "hide";
|
||||
});
|
||||
|
||||
const recipeOverlayShow = recall(recipeOverlayStorageKey, recipeOverlayDefault);
|
||||
document.body.dataset.recipeOverlay = recipeOverlayShow ? "show" : "hide";
|
||||
getElements("toggle", "planner:toggle-block-recipe-overlay")
|
||||
.forEach(e => e.dataset.toggled = recipeOverlayShow.toString());
|
||||
listenTo("planner:toggle-block-recipe-overlay", e => {
|
||||
save(recipeOverlayStorageKey, e.detail.state);
|
||||
document.body.dataset.recipeOverlay = e.detail.state ? "show" : "hide";
|
||||
});
|
||||
|
||||
const zoomLevel = recall(zoomStorageKey, zoomDefault);
|
||||
document.getElementById("planner").style
|
||||
.setProperty("--scale-factor", zoomLevel);
|
||||
|
|
@ -238,7 +250,7 @@ function cellClickEvent(e) {
|
|||
if (e.target.tile === "-1") return;
|
||||
|
||||
e.target.dataset.previewRemove = "yes";
|
||||
deleteSolverMetadata(e.target);
|
||||
clearCellMetadata(e.target, "block_recipe");
|
||||
} else if (e.buttons === 1) {
|
||||
// add
|
||||
if (e.target.dataset.preview === activeItem.toString(10)) return;
|
||||
|
|
@ -247,7 +259,7 @@ function cellClickEvent(e) {
|
|||
e.target.replaceChildren(makeAtlasImage(activeItem));
|
||||
const pickerRecipe = getBlockPickerRecipe();
|
||||
if (pickerRecipe) {
|
||||
setSolverMetadata(e.target, { block_recipe: pickerRecipe });
|
||||
setCellMetadata(e.target, "block_recipe", pickerRecipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -327,7 +339,7 @@ export function* getCellTooltip(cell) {
|
|||
}
|
||||
const metadata = getAtlasMetadata(cell.dataset.tile)
|
||||
const data = lookupFromMetadata(metadata);
|
||||
const solverMetadata = getSolverMetadata(cell);
|
||||
const solverMetadata = getMetadata(cell);
|
||||
|
||||
yield data.modules["plannerator:display_name"].display_name;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ 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 { deleteSolverMetadata, getSolverMetadata, setSolverMetadata } from "./metadata.js";
|
||||
import { clearCellMetadata, getMetadata, setCellMetadata, setDomMetadata } from "./metadata.js";
|
||||
import {
|
||||
checkEquivalence,
|
||||
findFullEntry,
|
||||
|
|
@ -154,11 +154,11 @@ function rescanDesignRecipes() {
|
|||
const designItem = designRecipes.makeItem();
|
||||
|
||||
const plannerDom = getPlannerDom();
|
||||
const plannerMetadata = getSolverMetadata(plannerDom);
|
||||
const plannerMetadata = getMetadata(plannerDom);
|
||||
let currentSelection = plannerMetadata?.coolant_recipe;
|
||||
if (!plannerMetadata) {
|
||||
currentSelection = defaultRecipes[multiblockType].coolant_recipe;
|
||||
setSolverMetadata(plannerDom, {
|
||||
setDomMetadata(plannerDom, {
|
||||
coolant_recipe: currentSelection,
|
||||
});
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ function rescanDesignRecipes() {
|
|||
title: "Coolant Recipe",
|
||||
entryListType: "coolant_recipes",
|
||||
recipes: coolantRecipes,
|
||||
updateSelection: r => setSolverMetadata(plannerDom, { coolant_recipe: r }),
|
||||
updateSelection: r => setDomMetadata(plannerDom, { coolant_recipe: r }),
|
||||
multiblockType,
|
||||
currentSelection,
|
||||
});
|
||||
|
|
@ -204,7 +204,7 @@ function rescanSelectionRecipes() {
|
|||
return;
|
||||
}
|
||||
|
||||
const cellRecipes = selectedCells.map(v => getSolverMetadata(v)?.block_recipe);
|
||||
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]));
|
||||
console.log(">", cellRecipes, recipeSet, recipeAllSame);
|
||||
|
|
@ -238,11 +238,11 @@ function rescanSelectionRecipes() {
|
|||
multiblockType,
|
||||
updateSelection: (e) => {
|
||||
if (e === recipes[0]) {
|
||||
selectedCells.forEach(v => deleteSolverMetadata(v));
|
||||
selectedCells.forEach(v => clearCellMetadata(v, "block_recipe"));
|
||||
return;
|
||||
}
|
||||
const stripped = stripEntryToMinimums(e);
|
||||
selectedCells.forEach(v => setSolverMetadata(v, { block_recipe: stripped }));
|
||||
selectedCells.forEach(v => setCellMetadata(v, "block_recipe", stripped));
|
||||
if (pickerTile !== -1) {
|
||||
setBlockPickerRecipe(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { emitEvent } from "./events.js";
|
||||
import { getPlannerCellDom, getPlannerDom } from "./planner.js";
|
||||
import { findFullEntry } from "./ncpf-utils.js";
|
||||
import { setSolverMetadata } from "./metadata.js";
|
||||
import { setCellMetadata, setDomMetadata } from "./metadata.js";
|
||||
|
||||
export function resetSolver(design = undefined, designConfiguration) {
|
||||
const planner = getPlannerDom();
|
||||
|
|
@ -15,7 +15,7 @@ export function resetSolver(design = undefined, designConfiguration) {
|
|||
designCoolantRecipe,
|
||||
{ configurationHint: design.type },
|
||||
);
|
||||
setSolverMetadata(planner, { coolant_recipe: fullCoolantRecipe });
|
||||
setDomMetadata(planner, { coolant_recipe: fullCoolantRecipe });
|
||||
|
||||
const designBlockRecipes = design.block_recipes.flat(3);
|
||||
for (let x = 0; x < design.dimensions[0]; x++) {
|
||||
|
|
@ -25,7 +25,7 @@ export function resetSolver(design = undefined, designConfiguration) {
|
|||
if (!block || !block.modules || !block.modules["ncpf:block_recipes"]) continue;
|
||||
const blockRecipeIndex = designBlockRecipes.shift();
|
||||
const blockRecipe = block.modules["ncpf:block_recipes"].recipes[blockRecipeIndex];
|
||||
setSolverMetadata(getPlannerCellDom(x, y, z), { block_recipe: blockRecipe });
|
||||
setCellMetadata(getPlannerCellDom(x, y, z), "block_recipe", blockRecipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
style.css
15
style.css
|
|
@ -240,12 +240,18 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4);
|
||||
background-color: var(--highlight-color, #f808);
|
||||
}
|
||||
|
||||
[data-casing="hide"] #planner .grid {
|
||||
--width-override: calc(var(--width) - 2);
|
||||
}
|
||||
[data-casing="hide"] [data-casing] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-recipe-overlay="hide"] .planner--overlay[data-overlay="block_recipe"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-selected] {
|
||||
outline: calc(var(--scale-factor) * 1px) dashed var(--cell-selected-outline, #f80);
|
||||
outline-offset: calc(var(--scale-factor) * -1px);
|
||||
|
|
@ -254,6 +260,15 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.planner--cell {
|
||||
position: relative;
|
||||
}
|
||||
.planner--cell > .planner--overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: var(--overlay-opacity, 0.8);
|
||||
}
|
||||
|
||||
.planner--resize {
|
||||
--resize-width: var(--width);
|
||||
--resize-height: var(--height);
|
||||
|
|
|
|||
Loading…
Reference in New Issue