From ff7ec9b0e95c0866d5bbe049cf499e54076d213e Mon Sep 17 00:00:00 2001 From: HF Date: Sun, 4 Feb 2024 22:59:07 +0100 Subject: [PATCH] hook up settings --- src/components/TemplateSettings.jsx | 63 +++++++++++++++++++++++-- src/components/buttons/PencilButton.jsx | 4 +- src/controls/PixelPainterControls.js | 8 ++++ src/controls/keypress.js | 17 ++++++- src/core/utils.js | 2 +- src/store/actions/templates.js | 28 ++++++++++- src/store/middleware/rendererHook.js | 7 +++ src/store/reducers/templates.js | 20 +++++++- src/ui/Renderer2D.js | 9 ++-- src/ui/render2Delements.js | 3 ++ src/ui/templateLoader.js | 38 +++++++++++---- 11 files changed, 175 insertions(+), 24 deletions(-) diff --git a/src/components/TemplateSettings.jsx b/src/components/TemplateSettings.jsx index 104be41..e57c846 100644 --- a/src/components/TemplateSettings.jsx +++ b/src/components/TemplateSettings.jsx @@ -3,21 +3,38 @@ */ import React, { useState, useCallback, useRef } from 'react'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import fileDownload from 'js-file-download'; -import { t } from 'ttag'; +import { c, t } from 'ttag'; import TemplateItem from './TemplateItem'; import TemplateItemEdit from './TemplateItemEdit'; +import SettingsItem from './SettingsItem'; import templateLoader from '../ui/templateLoader'; +import { + toggleOVEnabled, + toggleSmallPxls, + setOvOpacity, +} from '../store/actions/templates'; const TemplateSettings = () => { const [showAdd, setShowAdd] = useState(false); - const list = useSelector((state) => state.templates.list); + const [ + list, + oVEnabled, + oSmallPxls, + oOpacity, + ] = useSelector((state) => [ + state.templates.list, + state.templates.ovEnabled, + state.templates.oSmallPxls, + state.templates.oOpacity, + ], shallowEqual); const [editingIndices, setEditingIndices] = useState([]); const close = useCallback(() => setShowAdd(false), []); const importRef = useRef(); + const dispatch = useDispatch(); const toggleEditing = useCallback((title) => { const index = list.findIndex((t) => t.title === title); @@ -32,10 +49,48 @@ const TemplateSettings = () => { return ( <> -

{t`Templates`}

+

{t`Templates`}

{t`Tired of always spaming one single color? Want to create art instead, but you have to count pixels from some other image? Templates can help you with that! Templates can show as overlay and you can draw over them. One pixel on the template, should be one pixel on the canvas.`}

+ dispatch(toggleOVEnabled())} + > + {t`Show templates as overlays ingame.`} + + dispatch(toggleSmallPxls())} + > + {t`Show overlay as small individual pixels (will only show in high zoomlevels).`} + + +
+
+

+ {t`Overlay Opacity`} +

+
+ dispatch(setOvOpacity(evt.target.value))} + /> +
+
+
{t`Opacity of Overlay in percent.`}
+
+
+
{list.map(({ enabled, imageId, canvasId, title, x, y, width, height, diff --git a/src/components/buttons/PencilButton.jsx b/src/components/buttons/PencilButton.jsx index e5a7c62..2962f28 100644 --- a/src/components/buttons/PencilButton.jsx +++ b/src/components/buttons/PencilButton.jsx @@ -16,9 +16,11 @@ const PencilButton = () => { const [ holdPaint, showMvmCtrls, + easterEgg, ] = useSelector((state) => [ state.gui.holdPaint, state.gui.showMvmCtrls, + state.gui.easterEgg, ], shallowEqual); const dispatch = useDispatch(); @@ -34,7 +36,7 @@ const PencilButton = () => { } // eslint-disable-next-line no-fallthrough case HOLD_PAINT.HISTORY: - if (window.let_me_cheat) { + if (easterEgg) { nextMode = HOLD_PAINT.OVERLAY; dispatch(notify(t`Overlay Pencil ON`)); break; diff --git a/src/controls/PixelPainterControls.js b/src/controls/PixelPainterControls.js index 8b1b7c4..63e7c64 100644 --- a/src/controls/PixelPainterControls.js +++ b/src/controls/PixelPainterControls.js @@ -21,6 +21,7 @@ import { import { HOLD_PAINT, } from '../core/constants'; +import templateLoader from '../ui/templateLoader'; class PixelPainterControls { store; @@ -413,6 +414,13 @@ class PixelPainterControls { if (state.gui.holdPaint === HOLD_PAINT.HISTORY) { return renderer.getColorIndexOfPixel(...cell, true); } + if (state.gui.holdPaint === HOLD_PAINT.OVERLAY) { + const rgb = templateLoader.getColorOfPixel(...cell); + if (!rgb) { + return null; + } + return state.canvas.palette.getIndexOfColor(...rgb); + } return state.canvas.selectedColor; } diff --git a/src/controls/keypress.js b/src/controls/keypress.js index 6390ed3..37cb886 100644 --- a/src/controls/keypress.js +++ b/src/controls/keypress.js @@ -17,10 +17,13 @@ import { setMoveV, setMoveW, } from '../store/actions'; +import { + toggleOVEnabled, +} from '../store/actions/templates'; import { HOLD_PAINT } from '../core/constants'; import { notify } from '../store/actions/thunks'; -const charKeys = ['g', 'h', 'x', 'm', 'r', 'z', '+', '-']; +const charKeys = ['g', 'h', 'x', 'm', 't', 'r', 'z', '+', '-']; export function createKeyUpHandler(store) { return (event) => { @@ -149,7 +152,10 @@ export function createKeyDownHandler(store) { } if (event.location === KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { // right shift - store.dispatch(selectHoldPaint(HOLD_PAINT.HISTORY, true)); + store.dispatch(selectHoldPaint( + (store.getState().gui.easterEgg) ? HOLD_PAINT.OVERLAY : HOLD_PAINT.HISTORY, + true, + )); return; } return; @@ -204,6 +210,13 @@ export function createKeyDownHandler(store) { store.dispatch(notify(t`Copied!`)); return; } + case 't': { + store.dispatch(toggleOVEnabled()); + store.dispatch(notify((store.getState().templates.ovEnabled) + ? t`Overlay ON` + : t`Overlay OFF`)); + return; + } case 'z': store.dispatch(toggleEasterEgg()); store.dispatch(notify((store.getState().gui.easterEgg) diff --git a/src/core/utils.js b/src/core/utils.js index ddfa1c1..da0f488 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -696,7 +696,7 @@ export function bufferToBase64(array) { reader.onload = (event) => { const dataUrl = event.target.result; - const [_, base64] = dataUrl.split(','); + const [, base64] = dataUrl.split(','); resolve(base64); }; diff --git a/src/store/actions/templates.js b/src/store/actions/templates.js index ef7bc84..f9be7f1 100644 --- a/src/store/actions/templates.js +++ b/src/store/actions/templates.js @@ -15,7 +15,7 @@ export function listTemplate(imageId, title, canvasId, x, y, width, height) { }; } -export function templatesReady(title) { +export function templatesReady() { return { type: 'TEMPLATES_READY', }; @@ -36,8 +36,32 @@ export function changeTemplate(title, props) { }; } +export function toggleOVEnabled() { + return { + type: 's/TGL_OVENABLED', + }; +} + +export function toggleSmallPxls() { + return { + type: 's/TGL_SMALLPXLS', + }; +} + +export function setOvOpacity(opacity) { + return { + type: 's/SET_O_OPACITY', + opacity, + }; +} + +export function receivedTemplate() { + return { + type: 'REC_TEMPLATE', + }; +} + export function updatedTemplateImage(imageId, width, height) { - console.log('update', width, height, 'store'); return { type: 's/UPD_TEMPLATE_IMG', imageId, diff --git a/src/store/middleware/rendererHook.js b/src/store/middleware/rendererHook.js index 1386aa0..944a56b 100644 --- a/src/store/middleware/rendererHook.js +++ b/src/store/middleware/rendererHook.js @@ -105,6 +105,13 @@ export default (store) => (next) => (action) => { break; } + case 's/CHG_TEMPLATE': + case 's/TGL_OVENABLED': + case 's/TGL_SMALLPXLS': + case 's/REM_TEMPLATE': + case 's/UPD_TEMPLATE_IMG': + case 's/SET_O_OPACITY': + // case 'REQ_BIG_CHUNK': case 'PRE_LOADED_BIG_CHUNK': case 'REC_BIG_CHUNK': diff --git a/src/store/reducers/templates.js b/src/store/reducers/templates.js index c5233ec..a4bbf37 100644 --- a/src/store/reducers/templates.js +++ b/src/store/reducers/templates.js @@ -7,7 +7,7 @@ const initialState = { // prefix o: overlay, m: minimap ovEnabled: false, mEnabled: false, - oOpacity: 1.0, + oOpacity: 70, oSmallPxls: false, /* * [{ @@ -100,6 +100,24 @@ export default function templates( }; } + case 's/TGL_OVENABLED': + return { + ...state, + ovEnabled: !state.ovEnabled, + }; + + case 's/TGL_SMALLPXLS': + return { + ...state, + oSmallPxls: !state.oSmallPxls, + }; + + case 's/SET_O_OPACITY': + return { + ...state, + oOpacity: action.opacity, + }; + case 'TEMPLATES_READY': return { ...state, diff --git a/src/ui/Renderer2D.js b/src/ui/Renderer2D.js index dedfa80..644ab97 100644 --- a/src/ui/Renderer2D.js +++ b/src/ui/Renderer2D.js @@ -422,8 +422,8 @@ class Renderer2D extends Renderer { } } context.restore(); - // TODO conditions - if (false) { + + if (state.templates.ovEnabled && !state.templates.oSmallPxls) { renderOverlay( state, this.canvas, chunkPosition, scale, this.tiledScale, this.scaleThreshold, @@ -554,8 +554,9 @@ class Renderer2D extends Renderer { ); } - // TODO conditions - if (viewscale >= 5) { + if (viewscale >= 5 && state.templates.ovEnabled + && state.templates.oSmallPxls + ) { renderSmallPOverlay(viewport, _view, viewscale); } diff --git a/src/ui/render2Delements.js b/src/ui/render2Delements.js index 1b72c94..8c85ab7 100644 --- a/src/ui/render2Delements.js +++ b/src/ui/render2Delements.js @@ -113,6 +113,7 @@ export function renderOverlay( tiledScale, scaleThreshold, ) { + if (!templateLoader.ready) return; const { canvasSize } = state.canvas; // world coordinates of center of center chunk const [x, y] = centerChunk @@ -135,6 +136,7 @@ export function renderOverlay( context.save(); context.scale(offscreenScale, offscreenScale); + context.globalAlpha = state.templates.oOpacity / 100; for (const template of templates) { const image = templateLoader.getTemplateSync(template.imageId); if (!image) continue; @@ -156,6 +158,7 @@ export function renderSmallPOverlay( view, scale, ) { + if (!templateLoader.ready) return; const [x, y] = view; const { width, height } = $viewport; const horizontalRadius = width / 2 / scale; diff --git a/src/ui/templateLoader.js b/src/ui/templateLoader.js index aee6e53..c087f9d 100644 --- a/src/ui/templateLoader.js +++ b/src/ui/templateLoader.js @@ -9,6 +9,7 @@ import { updatedTemplateImage, changeTemplate, templatesReady, + receivedTemplate, } from '../store/actions/templates'; import { bufferToBase64, base64ToBuffer } from '../core/utils'; import Template from './Template'; @@ -76,21 +77,39 @@ class TemplateLoader { return null; } + getColorOfPixel(x, y) { + const templatesInView = this.#store.getState().templates.list + .filter((template) => ( + template.enabled && template.x < x && template.y < y + && template.x + template.width > x + && template.y + template.height > y + )); + + for (const tData of templatesInView) { + const image = this.getTemplateSync(tData.imageId); + if (!image) { + continue; + } + const ctx = image.getContext('2d'); + const rgb = ctx.getImageData(x - tData.x, y - tData.y, 1, 1).data; + if (rgb[3] > 200) { + return [rgb[0], rgb[1], rgb[2]]; + } + } + return null; + } + getTemplatesInView(x, y, horizontalRadius, verticalRadius) { const topX = x - horizontalRadius; const topY = y - verticalRadius; const bottomX = x + horizontalRadius; const bottomY = y + verticalRadius; - const templates = []; - this.#store.getState().templates.list.forEach((template) => { - if (x < bottomX && y < bottomY - && x + template.width > topX && y + template.height > topY - ) { - templates.push(template); - } - }); - return templates; + return this.#store.getState().templates.list.filter((template) => ( + template.enabled && template.x < bottomX && template.y < bottomY + && template.x + template.width > topX + && template.y + template.height > topY + )); } /* @@ -141,6 +160,7 @@ class TemplateLoader { const template = new Template(imageId); await template.fromBuffer(buffer, mimetype); this.#templates.set(imageId, template); + this.#store.dispatch(receivedTemplate()); return template; } catch (err) { // eslint-disable-next-line no-console