Merge pull request #1204 from redbirdly/main

Improve mobile support and add fill tool on worldEdit.ts
This commit is contained in:
slweeb 2025-08-11 15:42:22 -04:00 committed by GitHub
commit 1916c35e75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 175 additions and 52 deletions

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
// WorldEdit.js (compiled) // WorldEdit.js (compiled)
// Version: 1.0.1 // Version: 1.1.0
// Constants // Constants
const w_accentColor = "#7cff62"; const w_accentColor = "#7cff62";
const w_style = { const w_style = {
@ -92,6 +92,15 @@ function limitPointToWorld(point) {
}; };
} }
function mousePosToWorldPos(pos) {
const rect = canvas.getBoundingClientRect();
let x = pos.x - rect.left;
let y = pos.y - rect.top;
x = Math.floor((x / canvas.clientWidth) * (width + 1));
y = Math.floor((y / canvas.clientHeight) * (height + 1));
return {x: x, y: y};
}
function updatePastePreviewCanvas() { function updatePastePreviewCanvas() {
const clipboard = w_state.clipboard; const clipboard = w_state.clipboard;
if (!clipboard) if (!clipboard)
@ -116,7 +125,9 @@ function renderSelection(ctx) {
const selection = w_state.selection; const selection = w_state.selection;
if (!selection) if (!selection)
return; return;
const isSelecting = (mouseIsDown && mouseType === "left" && currentElement === "w_select"); const isSelecting = (mouseIsDown &&
(mouseType !== "middle" && mouseType !== "right") &&
currentElement === "w_select");
ctx.globalAlpha = 1.0; ctx.globalAlpha = 1.0;
// Fill // Fill
if (!isSelecting) { if (!isSelecting) {
@ -140,6 +151,7 @@ function renderPastePreview(ctx) {
if (!clipboard) if (!clipboard)
return; return;
const clipboardRect = Rect.fromGrid(clipboard, mousePos); const clipboardRect = Rect.fromGrid(clipboard, mousePos);
ctx.globalAlpha = 1.0;
// Fill // Fill
ctx.fillStyle = w_style.pasteFill; ctx.fillStyle = w_style.pasteFill;
ctx.fillRect(clipboardRect.x * pixelSize, clipboardRect.y * pixelSize, clipboardRect.w * pixelSize, clipboardRect.h * pixelSize); ctx.fillRect(clipboardRect.x * pixelSize, clipboardRect.y * pixelSize, clipboardRect.w * pixelSize, clipboardRect.h * pixelSize);
@ -178,6 +190,9 @@ function addWorldEditKeybinds() {
keybinds.Delete = () => { keybinds.Delete = () => {
elements.w_delete.rawOnSelect(); elements.w_delete.rawOnSelect();
}; };
keybinds.g = () => {
elements.w_fill.rawOnSelect();
};
} }
function modifySelectElement() { function modifySelectElement() {
@ -212,21 +227,6 @@ function addWorldEditElements(elementsToAdd) {
} }
} }
function updateSelection() {
if (!mouseIsDown)
return;
if (showingMenu)
return;
if (mouseType !== "left")
return;
if (currentElement !== "w_select")
return;
const rect = Rect.fromCorners(w_state.firstSelectionPos, limitPointToWorld(mousePos)).normalized();
rect.x2 += 1;
rect.y2 += 1;
w_state.selection = rect;
}
// Elements // Elements
worldEditElements.w_deselect = { worldEditElements.w_deselect = {
onSelect: function () { onSelect: function () {
@ -242,14 +242,30 @@ worldEditElements.w_select_all = {
} }
}; };
worldEditElements.w_select = { worldEditElements.w_select = {
onMouseDown: function () { onPointerDown: function (e) {
const pos = mousePosToWorldPos({x: e.clientX, y: e.clientY});
if (showingMenu) if (showingMenu)
return; return;
if (!isPointInWorld(mousePos)) if (!isPointInWorld(pos))
return; return;
if (mouseType !== "left") if (mouseType === "middle" || mouseType === "right")
return; return;
w_state.firstSelectionPos = mousePos; w_state.firstSelectionPos = pos;
},
onPointerMove: function (e) {
const pos = mousePosToWorldPos({x: e.clientX, y: e.clientY});
if (!mouseIsDown)
return;
if (showingMenu)
return;
if (mouseType === "middle" || mouseType === "right")
return;
if (currentElement !== "w_select")
return;
const rect = Rect.fromCorners(w_state.firstSelectionPos, limitPointToWorld(pos)).normalized();
rect.x2 += 1;
rect.y2 += 1;
w_state.selection = rect;
}, },
shouldStaySelected: true shouldStaySelected: true
}; };
@ -275,12 +291,12 @@ worldEditElements.w_copy = {
} }
}; };
worldEditElements.w_paste = { worldEditElements.w_paste = {
onMouseDown: function () { onPointerDown: function () {
if (showingMenu) if (showingMenu)
return; return;
if (!isPointInWorld(mousePos)) if (!isPointInWorld(mousePos))
return; return;
if (mouseType !== "left") if (mouseType === "middle" || mouseType === "right")
return; return;
const clipboard = w_state.clipboard; const clipboard = w_state.clipboard;
if (!clipboard) { if (!clipboard) {
@ -361,6 +377,30 @@ worldEditElements.w_delete = {
logMessage(`Deleted ${selection.w}x${selection.h}=${selection.area} pixel area.`); logMessage(`Deleted ${selection.w}x${selection.h}=${selection.area} pixel area.`);
} }
}; };
worldEditElements.w_fill = {
onSelect: function () {
const selection = w_state.selection;
const fillElement = w_state.prevNonWorldEditElement;
if (!selection) {
logMessage("Error: Nothing is selected.");
return;
}
// Fill area
for (let y = selection.y; y < selection.y2; y++) {
for (let x = selection.x; x < selection.x2; x++) {
const placed = currentPixels.push(new Pixel(x, y, fillElement));
if (!placed)
return;
if (currentPixels.length > maxPixelCount || !fillElement) {
currentPixels[currentPixels.length - 1].del = true;
} else if (elements[fillElement] && elements[fillElement].onPlace !== undefined) {
elements[fillElement].onPlace(currentPixels[currentPixels.length - 1]);
}
}
}
logMessage(`Filled in ${selection.w}x${selection.h}=${selection.area} pixel area.`);
}
};
// Setup and hooks // Setup and hooks
modifySelectElement(); modifySelectElement();
addWorldEditElements(worldEditElements); addWorldEditElements(worldEditElements);
@ -370,6 +410,20 @@ runAfterReset(() => {
w_state.selection = null; w_state.selection = null;
}); });
runAfterReset(updatePastePreviewCanvas); runAfterReset(updatePastePreviewCanvas);
renderPrePixel(updateSelection);
renderPostPixel(renderSelection); renderPostPixel(renderSelection);
renderPostPixel(renderPastePreview); renderPostPixel(renderPastePreview);
// Mobile support
let addedCustomEventListeners = false;
runAfterReset(() => {
if (addedCustomEventListeners)
return;
gameCanvas.addEventListener("pointerdown", (e) => {
if (elements[currentElement] && elements[currentElement].onPointerDown)
elements[currentElement].onPointerDown(e);
}, {passive: false});
gameCanvas.addEventListener("pointermove", (e) => {
if (elements[currentElement] && elements[currentElement].onPointerMove)
elements[currentElement].onPointerMove(e);
}, {passive: false});
addedCustomEventListeners = true;
});

View File

@ -1,5 +1,5 @@
// WorldEdit.ts // WorldEdit.ts
// Version: 1.0.1 // Version: 1.1.0
// Interfaces // Interfaces
interface WorldEditState { interface WorldEditState {
@ -132,6 +132,18 @@ function limitPointToWorld(point: Vec2D): Vec2D {
} }
} }
function mousePosToWorldPos(pos: Vec2D) {
const rect = canvas.getBoundingClientRect()
let x = pos.x - rect.left
let y = pos.y - rect.top
x = Math.floor((x / canvas.clientWidth) * (width + 1))
y = Math.floor((y / canvas.clientHeight) * (height + 1))
return {x: x, y: y}
}
function updatePastePreviewCanvas(): void { function updatePastePreviewCanvas(): void {
const clipboard = w_state.clipboard const clipboard = w_state.clipboard
if (!clipboard) return if (!clipboard) return
@ -161,7 +173,11 @@ function renderSelection(ctx: CanvasRenderingContext2D): void {
const selection = w_state.selection const selection = w_state.selection
if (!selection) return if (!selection) return
const isSelecting = (mouseIsDown && mouseType === "left" && currentElement === "w_select") const isSelecting = (
mouseIsDown &&
(mouseType !== "middle" && mouseType !== "right") &&
currentElement === "w_select"
)
ctx.globalAlpha = 1.0 ctx.globalAlpha = 1.0
@ -201,6 +217,8 @@ function renderPastePreview(ctx: CanvasRenderingContext2D): void {
const clipboardRect = Rect.fromGrid(clipboard, mousePos) const clipboardRect = Rect.fromGrid(clipboard, mousePos)
ctx.globalAlpha = 1.0
// Fill // Fill
ctx.fillStyle = w_style.pasteFill ctx.fillStyle = w_style.pasteFill
ctx.fillRect( ctx.fillRect(
@ -251,6 +269,9 @@ function addWorldEditKeybinds(): void {
keybinds.Delete = () => { // Delete keybinds.Delete = () => { // Delete
elements.w_delete.rawOnSelect() elements.w_delete.rawOnSelect()
} }
keybinds.g = () => { // Fill
elements.w_fill.rawOnSelect()
}
} }
function modifySelectElement(): void { function modifySelectElement(): void {
@ -289,23 +310,6 @@ function addWorldEditElements(elementsToAdd: ElementsType): void {
} }
} }
function updateSelection(): void {
if (!mouseIsDown) return
if (showingMenu) return
if (mouseType !== "left") return
if (currentElement !== "w_select") return
const rect = Rect.fromCorners(
w_state.firstSelectionPos,
limitPointToWorld(mousePos)
).normalized()
rect.x2 += 1
rect.y2 += 1
w_state.selection = rect
}
// Elements // Elements
worldEditElements.w_deselect = { worldEditElements.w_deselect = {
onSelect: function (): void { onSelect: function (): void {
@ -325,12 +329,32 @@ worldEditElements.w_select_all = {
} }
worldEditElements.w_select = { worldEditElements.w_select = {
onMouseDown: function (): void { onPointerDown: function (e: PointerEvent): void {
if (showingMenu) return const pos = mousePosToWorldPos({x: e.clientX, y: e.clientY})
if (!isPointInWorld(mousePos)) return
if (mouseType !== "left") return
w_state.firstSelectionPos = mousePos if (showingMenu) return
if (!isPointInWorld(pos)) return
if (mouseType === "middle" || mouseType === "right") return
w_state.firstSelectionPos = pos
},
onPointerMove: function (e: PointerEvent): void {
const pos = mousePosToWorldPos({x: e.clientX, y: e.clientY})
if (!mouseIsDown) return
if (showingMenu) return
if (mouseType === "middle" || mouseType === "right") return
if (currentElement !== "w_select") return
const rect = Rect.fromCorners(
w_state.firstSelectionPos,
limitPointToWorld(pos)
).normalized()
rect.x2 += 1
rect.y2 += 1
w_state.selection = rect
}, },
shouldStaySelected: true shouldStaySelected: true
} }
@ -364,10 +388,10 @@ worldEditElements.w_copy = {
} }
worldEditElements.w_paste = { worldEditElements.w_paste = {
onMouseDown: function (): void { onPointerDown: function (): void {
if (showingMenu) return if (showingMenu) return
if (!isPointInWorld(mousePos)) return if (!isPointInWorld(mousePos)) return
if (mouseType !== "left") return if (mouseType === "middle" || mouseType === "right") return
const clipboard = w_state.clipboard const clipboard = w_state.clipboard
@ -462,6 +486,34 @@ worldEditElements.w_delete = {
} }
} }
worldEditElements.w_fill = {
onSelect: function (): void {
const selection = w_state.selection
const fillElement = w_state.prevNonWorldEditElement
if (!selection) {
logMessage("Error: Nothing is selected.")
return
}
// Fill area
for (let y = selection.y; y < selection.y2; y++) {
for (let x = selection.x; x < selection.x2; x++) {
const placed = currentPixels.push(new Pixel(x, y, fillElement))
if (!placed) return
if (currentPixels.length > maxPixelCount || !fillElement) {
currentPixels[currentPixels.length - 1].del = true
} else if (elements[fillElement] && elements[fillElement].onPlace !== undefined) {
elements[fillElement].onPlace(currentPixels[currentPixels.length - 1])
}
}
}
logMessage(`Filled in ${selection.w}x${selection.h}=${selection.area} pixel area.`)
}
}
// Setup and hooks // Setup and hooks
modifySelectElement() modifySelectElement()
addWorldEditElements(worldEditElements) addWorldEditElements(worldEditElements)
@ -473,6 +525,23 @@ runAfterReset(() => {
}) })
runAfterReset(updatePastePreviewCanvas) runAfterReset(updatePastePreviewCanvas)
renderPrePixel(updateSelection)
renderPostPixel(renderSelection) renderPostPixel(renderSelection)
renderPostPixel(renderPastePreview) renderPostPixel(renderPastePreview)
// Mobile support
let addedCustomEventListeners = false
runAfterReset(() => {
if (addedCustomEventListeners) return
gameCanvas.addEventListener("pointerdown", (e: PointerEvent) => {
if (elements[currentElement] && elements[currentElement].onPointerDown)
elements[currentElement].onPointerDown(e)
}, {passive: false})
gameCanvas.addEventListener("pointermove", (e: PointerEvent) => {
if (elements[currentElement] && elements[currentElement].onPointerMove)
elements[currentElement].onPointerMove(e)
}, {passive: false})
addedCustomEventListeners = true
})