diff --git a/app/dialogue.js b/app/dialogue.js index b839ef7..ed40c4e 100644 --- a/app/dialogue.js +++ b/app/dialogue.js @@ -1,8 +1,11 @@ +import {createElement} from 'react'; import mdx from 'remark-mdx'; import parse from 'remark-parse'; import {unified} from 'unified'; import {visitParents as visit} from 'unist-util-visit-parents'; +import {TAU} from '@/util/math.js'; + const parser = unified().use(parse).use(mdx); function computeParams(ancestors) { @@ -54,3 +57,65 @@ export function parseLetters(source) { }); return letters; } + +export function render(letters, className) { + return (caret, radians) => ( + letters + .map(({character, indices, params}, i) => { + let color = 'inherit'; + let fade = 0; + let fontStyle = 'normal'; + let fontWeight = 'normal'; + let left = 0; + let opacity = 1; + let top = 0; + if (params.blink) { + const {frequency = 1} = params.blink; + opacity = (radians * (2 / frequency) % TAU) > (TAU / 2) ? opacity : 0; + } + if (params.fade) { + const {frequency = 1} = params.fade; + fade = frequency; + } + if (params.wave) { + const {frequency = 1, magnitude = 3} = params.wave; + top += magnitude * Math.cos((radians * (1 / frequency)) + TAU * indices.wave / params.wave.length); + } + if (params.rainbow) { + const {frequency = 1} = params.rainbow; + color = `hsl(${(radians * (1 / frequency)) + TAU * indices.rainbow / params.rainbow.length}rad 100 50)`; + } + if (params.shake) { + const {magnitude = 1} = params.shake; + left += (Math.random() * magnitude * 2) - magnitude; + top += (Math.random() * magnitude * 2) - magnitude; + } + if (params.em) { + fontStyle = 'italic'; + } + if (params.strong) { + fontWeight = 'bold'; + } + return ( + createElement( + 'span', + { + className, + key: i, + style: { + color, + fontStyle, + fontWeight, + left: `${left}px`, + opacity: i <= caret ? opacity : 0, + position: 'relative', + top: `${top}px`, + transition: `opacity ${fade}s`, + }, + }, + character, + ) + ); + }) + ); +} diff --git a/app/react-components/dom/chat.jsx b/app/react-components/dom/chat.jsx deleted file mode 100644 index 1ba950c..0000000 --- a/app/react-components/dom/chat.jsx +++ /dev/null @@ -1,91 +0,0 @@ -import {useEffect, useState} from 'react'; - -import {useClient} from '@/context/client.js'; - -import styles from './chat.module.css'; - -export default function Chat({ - chatHistory, - onClose, - message, - setChatHistory, - setMessage, -}) { - const client = useClient(); - const [disabled, setDisabled] = useState(true); - const [historyCaret, setHistoryCaret] = useState(0); - useEffect(() => { - setDisabled(false); - }, []); - return ( -
- { - dialogue.letters - .map(({character, indices, params}, i) => { - let color = 'inherit'; - let fade = 0; - let fontStyle = 'normal'; - let fontWeight = 'normal'; - let left = 0; - let opacity = 1; - let top = 0; - if (params.blink) { - const {frequency = 1} = params.blink; - opacity = (radians * (2 / frequency) % TAU) > (TAU / 2) ? opacity : 0; - } - if (params.fade) { - const {frequency = 1} = params.fade; - fade = frequency; - } - if (params.wave) { - const {frequency = 1, magnitude = 3} = params.wave; - top += magnitude * Math.cos((radians * (1 / frequency)) + TAU * indices.wave / params.wave.length); - } - if (params.rainbow) { - const {frequency = 1} = params.rainbow; - color = `hsl(${(radians * (1 / frequency)) + TAU * indices.rainbow / params.rainbow.length}rad 100 50)`; - } - if (params.shake) { - const {magnitude = 1} = params.shake; - left += (Math.random() * magnitude * 2) - magnitude; - top += (Math.random() * magnitude * 2) - magnitude; - } - if (params.em) { - fontStyle = 'italic'; - } - if (params.strong) { - fontWeight = 'bold'; - } - return ( - - {character} - - ); - }) - } + {localRender(caret, radians)}
); diff --git a/app/react-components/dom/dialogue.module.css b/app/react-components/dom/dialogue.module.css index 9afb5c1..2b2a057 100644 --- a/app/react-components/dom/dialogue.module.css +++ b/app/react-components/dom/dialogue.module.css @@ -11,6 +11,7 @@ border: solid 1px white; border-radius: 8px; color: white; + overflow-wrap: break-word; padding: 1em; position: fixed; margin: 0; @@ -26,5 +27,4 @@ .letter { opacity: 0; - position: relative; } diff --git a/app/react-components/dom/entities.jsx b/app/react-components/dom/entities.jsx index 003f1a3..37ebc20 100644 --- a/app/react-components/dom/entities.jsx +++ b/app/react-components/dom/entities.jsx @@ -6,7 +6,12 @@ import {parseLetters} from '@/dialogue.js'; import Entity from './entity.jsx'; -export default function Entities({camera, scale, setMonopolizers}) { +export default function Entities({ + camera, + scale, + setChatMessages, + setMonopolizers, +}) { const [ecs] = useEcs(); const [entities, setEntities] = useState({}); usePacket('EcsChange', async () => { @@ -34,6 +39,7 @@ export default function Entities({camera, scale, setMonopolizers}) { for (const key in dialogue) { dialogues[key] = dialogue[key]; dialogues[key].letters = parseLetters(dialogues[key].body); + setChatMessages((chatMessages) => [dialogues[key].letters, ...chatMessages]); const skipListeners = new Set(); dialogues[key].addSkipListener = (listener) => { skipListeners.add(listener); diff --git a/app/react-components/ui.jsx b/app/react-components/ui.jsx index a2980e6..337e366 100644 --- a/app/react-components/ui.jsx +++ b/app/react-components/ui.jsx @@ -9,7 +9,7 @@ import {useEcs, useEcsTick} from '@/context/ecs.js'; import {useMainEntity} from '@/context/main-entity.js'; import Disconnected from './dom/disconnected.jsx'; -import Chat from './dom/chat.jsx'; +import Chat from './dom/chat/chat.jsx'; import Dom from './dom/dom.jsx'; import Entities from './dom/entities.jsx'; import HotBar from './dom/hotbar.jsx'; @@ -69,6 +69,7 @@ export default function Ui({disconnected}) { const [message, setMessage] = useState(''); const [chatIsOpen, setChatIsOpen] = useState(false); const [chatHistory, setChatHistory] = useState([]); + const [chatMessages, setChatMessages] = useState([]); useEffect(() => { async function setEcsStuff() { const {default: Components} = await import('@/ecs-components/index.js'); @@ -97,6 +98,10 @@ export default function Ui({disconnected}) { }, [disconnected]); useEffect(() => { return addKeyListener(document.body, ({event, type, payload}) => { + if ('Escape' === payload && 'keyDown' === type && chatIsOpen) { + setChatIsOpen(false); + return; + } if (chatIsOpen) { return; } @@ -323,6 +328,10 @@ export default function Ui({disconnected}) {