hook up settings

This commit is contained in:
HF 2024-02-04 22:59:07 +01:00
parent 47b4db2602
commit ff7ec9b0e9
11 changed files with 175 additions and 24 deletions

View File

@ -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 (
<>
<h3>{t`Templates`}</h3>
<h2>{t`Templates`}</h2>
<p>
{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.`}
</p>
<SettingsItem
title={t`Enable Overlay`}
keyBind={c('keybinds').t`T`}
value={oVEnabled}
onToggle={() => dispatch(toggleOVEnabled())}
>
{t`Show templates as overlays ingame.`}
</SettingsItem>
<SettingsItem
title={t`Small Pixels Overlay`}
value={oSmallPxls}
deactivated={!oVEnabled}
onToggle={() => dispatch(toggleSmallPxls())}
>
{t`Show overlay as small individual pixels (will only show in high zoomlevels).`}
</SettingsItem>
<div className="setitem">
<div className="setrow">
<h3 className="settitle">
{t`Overlay Opacity`}
</h3>
<div style={{ textAlign: 'right' }}>
<input
type="number"
value={oOpacity}
style={{ maxWidth: '6em' }}
step="1"
min="10"
max="100"
onChange={(evt) => dispatch(setOvOpacity(evt.target.value))}
/>
</div>
</div>
<div className="modaldesc">{t`Opacity of Overlay in percent.`}</div>
<div className="modaldivider" />
</div>
<div className="content">
{list.map(({
enabled, imageId, canvasId, title, x, y, width, height,

View File

@ -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;

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
};

View File

@ -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,

View File

@ -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':

View File

@ -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,

View File

@ -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);
}

View File

@ -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;

View File

@ -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