add reactor resizing

This commit is contained in:
CodedSakura 2026-07-25 23:00:58 +02:00
parent f80bebe9de
commit c958112590
7 changed files with 378 additions and 38 deletions

View File

@ -21,6 +21,7 @@
<div data-section="Edit" data-alt="e"> <div data-section="Edit" data-alt="e">
<button data-alt="u" data-button="edit:undo" disabled>Undo</button> <button data-alt="u" data-button="edit:undo" disabled>Undo</button>
<button data-alt="r" data-button="edit:redo" disabled>Redo</button> <button data-alt="r" data-button="edit:redo" disabled>Redo</button>
<button data-alt="e" data-toggle="planner:resize" data-toggled="false">Resize mode</button>
<button data-toggle="dev-mode:toggle" data-toggled="false">Developer mode</button> <button data-toggle="dev-mode:toggle" data-toggled="false">Developer mode</button>
</div> </div>
<div data-section="Select" data-alt="s"> <div data-section="Select" data-alt="s">
@ -124,7 +125,7 @@
</section> </section>
</aside> </aside>
<footer> <footer>
<div>pre-alpha 1.3</div> <div>pre-alpha 1.4</div>
<div> <div>
<a href="https://discord.gg/Eq72HwPqfg">Discord server</a> <a href="https://discord.gg/Eq72HwPqfg">Discord server</a>
</div> </div>

View File

@ -41,4 +41,6 @@ registerLoadCallback(() => {
awaitEvent("config:end").then(() => { awaitEvent("config:end").then(() => {
void loadExampleFile("Pandora's Fission Reactor"); void loadExampleFile("Pandora's Fission Reactor");
}); });
queueMicrotask(() => emitEvent("dev-mode:end"));
}); });

View File

@ -52,6 +52,8 @@ export async function loadFile(data, name) {
if (!data.designs) throw new UserError("File must contain a list of designs!"); if (!data.designs) throw new UserError("File must contain a list of designs!");
if (data.designs.length === 0) throw new UserError("Fils must contain at least one design!"); if (data.designs.length === 0) throw new UserError("Fils must contain at least one design!");
emitEvent("fileLoad:start");
let design = data.designs[0]; let design = data.designs[0];
if (data.designs.length > 1) { if (data.designs.length > 1) {
const prepChoice = awaitEvent("fileLoad:chooseDesign:choice"); const prepChoice = awaitEvent("fileLoad:chooseDesign:choice");
@ -74,6 +76,8 @@ export async function loadFile(data, name) {
getPlannerDom().dataset.name = name; getPlannerDom().dataset.name = name;
} }
emitEvent("fileLoad:end");
console.log("load file", data); console.log("load file", data);
} }

View File

@ -12,6 +12,7 @@ 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 "./planner-resize.js";
import "./recipe-selector.js"; import "./recipe-selector.js";
import "./resize-menus.js"; import "./resize-menus.js";
import "./solver.js"; import "./solver.js";

200
scripts/planner-resize.js Normal file
View File

@ -0,0 +1,200 @@
import { registerLoadCallback } from "./main.js";
import { awaitEvent, listenTo } from "./events.js";
import {
createPlannerCell,
createPlannerPlane,
getPlannerDom,
getPlannerMultiblockSize, getPlannerMultiblockType,
maxMultiblockSize
} from "./planner.js";
import { DomList } from "./dom-list.js";
registerLoadCallback(() => {
listenTo("planner:resize", e => {
if (e.detail.state) {
initializeResize();
} else {
cleanUpResize();
}
});
awaitEvent("dev-mode:end").then(async () => {
await awaitEvent("fileLoad:end");
initializeResize();
});
});
function createButton({ axis, n, add, topLeft }) {
const div = document.createElement("div");
div.innerText = add ? "+" : "-";
if (axis === "y") {
div.classList.add(`planner--resize--${add ? "add" : "remove"}-layer`);
if (!add) {
div.addEventListener("mouseenter", () => highlight(axis, n, add));
div.addEventListener("mouseleave", () => highlight(null));
}
} else {
div.classList.add(`planner--resize--${add ? "add" : "remove"}-${axis}`);
div.classList.add(`planner--resize--${topLeft ? "top-left" : "bottom-right"}`);
div.style.setProperty(`--${axis}`, `${n}`);
div.addEventListener("mouseenter", () => highlight(axis, n, add));
div.addEventListener("mouseleave", () => highlight(null));
}
div.addEventListener("click", e => resize(axis, n, add, e));
return div;
}
function highlight(axis, n, add) {
const planner = getPlannerDom();
const resizes = [...planner.querySelectorAll(":scope > .planner--resize")];
if (!axis) {
resizes.forEach(r => {
r.querySelectorAll(":scope > .planner--resize--highlight").forEach(e => r.removeChild(e));
});
return;
}
if (axis === "y") {
const div = document.createElement("div");
div.classList.add("planner--resize--highlight", `planner--resize--highlight__remove-layer`);
resizes[n].appendChild(div);
return;
}
resizes.forEach(r => {
const div = document.createElement("div");
div.classList.add("planner--resize--highlight", `planner--resize--highlight__${add ? "add" : "remove"}-${axis}`);
div.style.setProperty(`--${axis}`, `${n}`);
r.appendChild(div);
});
}
function resize(axis, n, add, e) {
const planner = getPlannerDom();
const size = getPlannerMultiblockSize();
if (!add) {
if (axis === "y") {
const resizes = planner.querySelectorAll(`:scope > .planner--resize`);
planner.removeChild(resizes[n]);
size[1]--;
} else {
const tiles = planner.querySelectorAll(`[data-${axis}="${n}"]`);
const tileGroups = Object.values(Object.groupBy(tiles, t => t.parentElement.dataset.y));
for (const tg of tileGroups) {
const parent = tg[0].parentElement;
tg.forEach(t => parent.removeChild(t));
}
size[axis === "x" ? 0 : 2]--;
}
} else {
if (axis === "y") {
const list = new DomList(planner);
const plane = list.makeItem();
createPlannerPlane(plane, 0, null);
const parentPlane = document.createElement("div");
parentPlane.classList.add("planner--resize");
parentPlane.appendChild(plane)
const nextSibling = planner.querySelector(`:scope > .planner--resize[data-y="${n+1}"]`);
planner.insertBefore(parentPlane, nextSibling);
size[1]++;
} else {
const tiles = planner.querySelectorAll(`[data-${axis}="${n}"]`);
const tileGroups = Object.values(Object.groupBy(tiles, t => t.parentElement.dataset.y));
for (const tg of tileGroups) {
const parent = tg[0].parentElement;
const grid = new DomList(parent);
tg.forEach(t => {
const cell = grid.makeItem();
createPlannerCell(cell, 0, 0, 0, -1);
parent.insertBefore(cell, axis === "x" ? t : tg[0]);
});
}
size[axis === "x" ? 0 : 2]++;
}
}
fixPlannerNumbering();
cleanUpResize();
initializeResize();
}
function initializeResize() {
const planner = getPlannerDom();
const size = getPlannerMultiblockSize();
const type = getPlannerMultiblockType();
const grids = [...planner.querySelectorAll(":scope > .grid")];
grids.forEach((grid, gridIndex) => {
const casing = gridIndex === 0 || gridIndex === grids.length - 1;
const removedGrid = planner.removeChild(grid);
const newDiv = document.createElement("div");
newDiv.classList.add("planner--resize");
newDiv.style.setProperty("--width", `${size[0]}`);
newDiv.style.setProperty("--height", `${size[2]}`);
newDiv.dataset.y = gridIndex.toString(10);
if (casing) {
newDiv.dataset.casing = "";
}
if (!casing && size[1] > maxMultiblockSize[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++) {
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++) {
newDiv.appendChild(createButton({ axis: "z", add: false, n: z+1, topLeft: true }));
}
}
newDiv.appendChild(removedGrid);
planner.appendChild(newDiv);
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: false }));
}
for (let x = 0; x < size[0] - 2 && size[0] > maxMultiblockSize[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++) {
newDiv.appendChild(createButton({ axis: "z", add: false, n: z+1, topLeft: false }));
}
}
planner.appendChild(newDiv);
if (gridIndex !== grids.length - 1 && size[1] < maxMultiblockSize[type][1]) {
planner.appendChild(createButton({ axis: "y", add: true, n: gridIndex }));
}
});
}
function cleanUpResize() {
const planner = getPlannerDom();
planner.replaceChildren(...planner.querySelectorAll(":scope > .planner--resize > .grid"), ...planner.querySelectorAll(":scope > template"));
}
function fixPlannerNumbering() {
const planner = getPlannerDom();
const size = getPlannerMultiblockSize();
const grids = planner.querySelectorAll(":scope > .planner--resize > .grid");
grids.forEach(grid => {
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.z = z.toString(10);
}
}
});
}

View File

@ -19,9 +19,12 @@ const casingDefault = false; // hidden by default
const defaultMultiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y) const defaultMultiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
const defaultMultiblockType = "nuclearcraft:overhaul_sfr"; const defaultMultiblockType = "nuclearcraft:overhaul_sfr";
const maxMultiblockSize = { export const maxMultiblockSize = {
["nuclearcraft:overhaul_sfr"]: [26, 26, 26], ["nuclearcraft:overhaul_sfr"]: [26, 26, 26],
} }
export const minMultiblockSize = {
["nuclearcraft:overhaul_sfr"]: [3, 3, 3],
}
let suppressHover = false; let suppressHover = false;
let multiblockSize = [...defaultMultiblockSize]; let multiblockSize = [...defaultMultiblockSize];
@ -107,44 +110,50 @@ function buildPlanner(textures) {
for (let y = 0; y < multiblockSize[1]; y++) { for (let y = 0; y < multiblockSize[1]; y++) {
const item = list.makeItem(); const item = list.makeItem();
item.style.setProperty("--width", multiblockSize[0]); createPlannerPlane(item, y, textures);
item.dataset.y = y.toString(10);
if (y === 0 || y === multiblockSize[1] - 1) {
item.dataset.casing = "";
}
item.addEventListener("mousemove", retainTooltipHover);
const grid = new DomList(item);
grid.clear();
for (let z = 0; z < multiblockSize[2]; z++) {
for (let x = 0; x < multiblockSize[0]; x++) {
const texture = textures ? textures[x][y][z] : -1;
if (textures && !texture) {
console.log(x, y, z, textures);
}
const cell = grid.makeItem();
cell.dataset.x = x.toString(10);
cell.dataset.y = y.toString(10);
cell.dataset.z = z.toString(10);
cell.dataset.tile = texture.toString(10);
if (x === 0 || x === multiblockSize[0] - 1
|| y === 0 || y === multiblockSize[1] - 1
|| z === 0 || z === multiblockSize[2] - 1) {
cell.dataset.casing = "";
}
cell.dataset.tooltip = "auto";
cell.appendChild(makeAtlasImage(texture));
cell.addEventListener("mouseenter", cellHoverEvent);
cell.addEventListener("mouseleave", cellHoverEvent);
cell.addEventListener("mousedown", cellClickEvent);
cell.addEventListener("mouseenter", cellClickEvent);
cell.addEventListener("mouseleave", cellClickEvent);
cell.addEventListener("contextmenu", cellClickEvent);
grid.addItem(cell);
}
}
list.addItem(item); list.addItem(item);
} }
} }
export function createPlannerPlane(dom, y, textures) {
dom.style.setProperty("--width", multiblockSize[0]);
dom.dataset.y = y.toString();
if (y === 0 || y === multiblockSize[1] - 1) {
dom.dataset.casing = "";
}
dom.addEventListener("mousemove", retainTooltipHover);
const grid = new DomList(dom);
grid.clear();
for (let z = 0; z < multiblockSize[2]; z++) {
for (let x = 0; x < multiblockSize[0]; x++) {
const texture = textures ? textures[x][y][z] : -1;
if (textures && !texture) {
console.log(x, y, z, textures);
}
const cell = grid.makeItem();
createPlannerCell(cell, x, y, z, texture);
grid.addItem(cell);
}
}
}
export function createPlannerCell(dom, x, y, z, texture) {
dom.dataset.x = x.toString();
dom.dataset.y = y.toString();
dom.dataset.z = z.toString();
dom.dataset.tile = texture.toString();
if (x === 0 || x === multiblockSize[0] - 1
|| y === 0 || y === multiblockSize[1] - 1
|| z === 0 || z === multiblockSize[2] - 1) {
dom.dataset.casing = "";
}
dom.dataset.tooltip = "auto";
dom.appendChild(makeAtlasImage(texture));
dom.addEventListener("mouseenter", cellHoverEvent);
dom.addEventListener("mouseleave", cellHoverEvent);
dom.addEventListener("mousedown", cellClickEvent);
dom.addEventListener("mouseenter", cellClickEvent);
dom.addEventListener("mouseleave", cellClickEvent);
dom.addEventListener("contextmenu", cellClickEvent);
}
function cellHoverEvent(e) { function cellHoverEvent(e) {
if (suppressHover) return; if (suppressHover) return;

125
style.css
View File

@ -240,7 +240,7 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4); calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4);
background-color: var(--highlight-color, #f808); background-color: var(--highlight-color, #f808);
} }
[data-casing="hide"] #planner > * { [data-casing="hide"] #planner .grid {
--width-override: calc(var(--width) - 2); --width-override: calc(var(--width) - 2);
} }
[data-casing="hide"] [data-casing] { [data-casing="hide"] [data-casing] {
@ -254,6 +254,129 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
outline: none; outline: none;
} }
.planner--resize {
--resize-width: var(--width);
--resize-height: var(--height);
--resize-offset: 2;
display: grid;
grid-template-columns: repeat(calc(var(--resize-width) * 2 + 4), calc(var(--scale-factor) * var(--default-size) / 2));
grid-template-rows: repeat(calc(var(--resize-height) * 2 + 4), calc(var(--scale-factor) * var(--default-size) / 2));
margin: 0.5rem;
}
[data-casing="hide"] .planner--resize {
--resize-offset: 0;
grid-template-columns: repeat(calc(var(--resize-width) * 2), calc(var(--scale-factor) * var(--default-size) / 2));
grid-template-rows: repeat(calc(var(--resize-height) * 2), calc(var(--scale-factor) * var(--default-size) / 2));
}
.planner--resize--remove-layer {
grid-column: 1;
grid-row: 1;
}
.planner--resize--add-x {
grid-column: calc(var(--x) * 2 + 0 + var(--resize-offset)) / calc(var(--x) * 2 + 2 + var(--resize-offset));
}
.planner--resize--remove-x {
grid-column: calc(var(--x) * 2 + 1 + var(--resize-offset)) / calc(var(--x) * 2 + 3 + var(--resize-offset));
}
.planner--resize--add-x.planner--resize--top-left {
grid-row: 1;
}
.planner--resize--remove-x.planner--resize--top-left {
grid-row: 2;
}
.planner--resize--add-x.planner--resize--bottom-right {
grid-row: calc(var(--resize-height) * 2 - 0 + var(--resize-offset) * 2);
}
.planner--resize--remove-x.planner--resize--bottom-right {
grid-row: calc(var(--resize-height) * 2 - 1 + var(--resize-offset) * 2);
}
.planner--resize--add-z {
grid-row: calc(var(--z) * 2 + 0 + var(--resize-offset)) / calc(var(--z) * 2 + 2 + var(--resize-offset));
}
.planner--resize--remove-z {
grid-row: calc(var(--z) * 2 + 1 + var(--resize-offset)) / calc(var(--z) * 2 + 3 + var(--resize-offset));
}
.planner--resize--add-z.planner--resize--top-left {
grid-column: 1;
}
.planner--resize--remove-z.planner--resize--top-left {
grid-column: 2;
}
.planner--resize--add-z.planner--resize--bottom-right {
grid-column: calc(var(--resize-width) * 2 - 0 + var(--resize-offset) * 2);
}
.planner--resize--remove-z.planner--resize--bottom-right {
grid-column: calc(var(--resize-width) * 2 - 1 + var(--resize-offset) * 2);
}
.planner--resize > .grid {
grid-column: 3 / calc(var(--resize-width) * 2 - 1);
grid-row: 3 / calc(var(--resize-height) * 2 - 1);
margin: 0;
}
.planner--resize--add-layer {
width: calc(var(--scale-factor) * var(--default-size));
margin: 0.5rem;
}
.planner--resize--add-layer, .planner--resize--add-x, .planner--resize--add-z,
.planner--resize--remove-layer, .planner--resize--remove-x, .planner--resize--remove-z {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
user-select: none;
}
.planner--resize--add-layer, .planner--resize--add-x, .planner--resize--add-z {
background: var(--resize-add-bg, #4f88);
}
.planner--resize--add-layer:hover, .planner--resize--add-x:hover, .planner--resize--add-z:hover {
background: var(--resize-add-bg-hover, #4f8);
}
.planner--resize--remove-layer, .planner--resize--remove-x, .planner--resize--remove-z {
background: var(--resize-remove-bg, #f888);
}
.planner--resize--remove-layer:hover, .planner--resize--remove-x:hover, .planner--resize--remove-z:hover {
background: var(--resize-remove-bg-hover, #f88);
}
.planner--resize--add-x, .planner--resize--remove-x {
margin: 0 0.125rem;
}
.planner--resize--add-z, .planner--resize--remove-z {
margin: 0.125em 0;
}
.planner--resize--highlight {
z-index: 2;
}
.planner--resize--highlight__add-x {
background: var(--resize-add-bg-highlight, #4f88);
margin: 0 25%;
grid-row: 3 / -3;
grid-column: calc(var(--x) * 2 + 0 + var(--resize-offset)) / calc(var(--x) * 2 + 2 + var(--resize-offset));
}
.planner--resize--highlight__remove-x {
background: var(--resize-remove-bg-highlight, #f888);
grid-row: 3 / -3;
grid-column: calc(var(--x) * 2 + 1 + var(--resize-offset)) / calc(var(--x) * 2 + 3 + var(--resize-offset));
}
.planner--resize--highlight__add-z {
background: var(--resize-add-bg-highlight, #4f88);
/*margin: 25% 0;*/
margin-top: calc(var(--scale-factor) * var(--default-size) / 4);
height: 50%;
grid-row: calc(var(--z) * 2 + 0 + var(--resize-offset)) / calc(var(--z) * 2 + 2 + var(--resize-offset));
grid-column: 3 / -3;
}
.planner--resize--highlight__remove-z {
background: var(--resize-remove-bg-highlight, #f888);
grid-row: calc(var(--z) * 2 + 1 + var(--resize-offset)) / calc(var(--z) * 2 + 3 + var(--resize-offset));
grid-column: 3 / -3;
}
.planner--resize--highlight__remove-layer {
background: var(--resize-remove-bg-highlight, #f888);
grid-row: 3 / -3;
grid-column: 3 / -3;
}
#planner .atlas-air { #planner .atlas-air {
outline: calc(var(--scale-factor) * 0.5px) solid var(--cell-empty-outline-color, #ccc); outline: calc(var(--scale-factor) * 0.5px) solid var(--cell-empty-outline-color, #ccc);
outline-offset: calc(var(--scale-factor) * -0.5px); outline-offset: calc(var(--scale-factor) * -0.5px);