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 104be418..e57c8466 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 e5a7c62f..2962f287 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 8b1b7c4b..63e7c648 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 6390ed32..37cb8860 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 ddfa1c17..da0f488b 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 ef7bc842..f9be7f1f 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 1386aa06..944a56be 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 c5233ec2..a4bbf37c 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 dedfa802..644ab974 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 1b72c940..8c85ab72 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 aee6e53d..c087f9d0 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