From 12ce0ccbd5e25ad1f23f1114ec4e27f5020ccea3 Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 14 Jul 2024 02:26:43 -0500 Subject: [PATCH] fun: chat --- app/create-homestead.js | 4 +- app/create-player.js | 1 + app/engine.js | 19 +++- app/react-components/dom/chat.jsx | 91 ++++++++++++++++++++ app/react-components/dom/chat.module.css | 22 +++++ app/react-components/dom/dialogue.module.css | 5 +- app/react-components/ui.jsx | 27 +++++- 7 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 app/react-components/dom/chat.jsx create mode 100644 app/react-components/dom/chat.module.css diff --git a/app/create-homestead.js b/app/create-homestead.js index 8d5ec55..54bc98f 100644 --- a/app/create-homestead.js +++ b/app/create-homestead.js @@ -77,9 +77,7 @@ export default async function createHomestead(id) { interacting: 1, interactScript: ` subject.Interlocutor.dialogue({ - // body: 'Hey what is uuuuuuuuuuuuuuup? ^_^', - body: 'This is a fairly decently long amount of text that will be used to show just how the box grows when there is a kinda loooooooooooooooong ass bunch of text here', - linger: 5, + body: "Sure, I'm a treasure chest. Probably. Do you really think that means you're about to get some treasure? Hah!", monopolizer: true, origin: subject.Position.toJSON(), position: {x: subject.Position.x, y: subject.Position.y - 32}, diff --git a/app/create-player.js b/app/create-player.js index 5b90e87..4aa9aea 100644 --- a/app/create-player.js +++ b/app/create-player.js @@ -19,6 +19,7 @@ export default async function createPlayer(id) { Emitter: {}, Forces: {}, Interacts: {}, + Interlocutor: {}, Inventory: { slots: { 1: { diff --git a/app/engine.js b/app/engine.js index bb825af..6b23e80 100644 --- a/app/engine.js +++ b/app/engine.js @@ -156,9 +156,26 @@ export default class Engine { if (!entity) { continue; } - const {Controlled, Ecs, Interacts, Inventory, Wielder} = entity; + const { + Controlled, + Ecs, + Interacts, + Interlocutor, + Inventory, + Position, + Wielder, + } = entity; for (const payload of payloads) { switch (payload.type) { + case 'chat': { + Interlocutor.dialogue({ + body: payload.value, + linger: 5, + origin: Position.toJSON(), + position: {x: Position.x, y: Position.y - 32}, + }); + break; + } case 'paint': { const ecs = this.ecses[Ecs.path]; const {TileLayers} = ecs.get(1); diff --git a/app/react-components/dom/chat.jsx b/app/react-components/dom/chat.jsx new file mode 100644 index 0000000..1ba950c --- /dev/null +++ b/app/react-components/dom/chat.jsx @@ -0,0 +1,91 @@ +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 ( +
+
{ + if (message) { + client.send({ + type: 'Action', + payload: {type: 'chat', value: message}, + }); + setChatHistory([message, ...chatHistory]); + setMessage(''); + onClose(); + } + event.preventDefault(); + }} + > + { + setMessage(event.target.value); + }} + onKeyDown={(event) => { + switch (event.key) { + case 'ArrowDown': { + if (0 === historyCaret) { + break; + } + let localHistoryCaret = historyCaret - 1; + setMessage(chatHistory[localHistoryCaret]) + setHistoryCaret(localHistoryCaret); + if (0 === localHistoryCaret) { + setChatHistory(chatHistory.slice(1)); + } + break; + } + case 'ArrowUp': { + if (historyCaret === chatHistory.length - 1) { + break; + } + let localHistoryCaret = historyCaret; + let localChatHistory = chatHistory; + if (0 === historyCaret) { + localChatHistory = [message, ...localChatHistory]; + setChatHistory(localChatHistory); + } + localHistoryCaret += 1; + setMessage(localChatHistory[localHistoryCaret]) + setHistoryCaret(localHistoryCaret); + break; + } + case 'Escape': { + onClose(); + break; + } + } + }} + onMouseDown={(event) => { + event.stopPropagation(); + }} + maxLength="255" + ref={(element) => { + if (element) { + element.focus(); + } + }} + type="text" + value={message} + /> +
+
+ ); +} \ No newline at end of file diff --git a/app/react-components/dom/chat.module.css b/app/react-components/dom/chat.module.css new file mode 100644 index 0000000..41151e7 --- /dev/null +++ b/app/react-components/dom/chat.module.css @@ -0,0 +1,22 @@ +.chat { + background-color: #00000044; + position: absolute; + bottom: 0; + width: 100%; +} + +.chat form { + line-height: 0; + input[type="text"] { + background-color: #00000044; + border: 1px solid #333333; + color: #ffffff; + margin: 4px; + width: calc(100% - 8px); + &:focus-visible { + border: 1px solid #999999; + outline: none; + } + } +} + diff --git a/app/react-components/dom/dialogue.module.css b/app/react-components/dom/dialogue.module.css index 9449bd1..9afb5c1 100644 --- a/app/react-components/dom/dialogue.module.css +++ b/app/react-components/dom/dialogue.module.css @@ -12,11 +12,12 @@ border-radius: 8px; color: white; padding: 1em; - position: absolute; + position: fixed; margin: 0; + margin-right: -33%; transform: translate(-50%, -50%); user-select: none; - width: 33%; + max-width: 33%; } .letters { diff --git a/app/react-components/ui.jsx b/app/react-components/ui.jsx index 7c0b7e9..65e7b84 100644 --- a/app/react-components/ui.jsx +++ b/app/react-components/ui.jsx @@ -9,6 +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 Dom from './dom/dom.jsx'; import Entities from './dom/entities.jsx'; import HotBar from './dom/hotbar.jsx'; @@ -65,6 +66,9 @@ export default function Ui({disconnected}) { const [Systems, setSystems] = useState(); const [applyFilters, setApplyFilters] = useState(true); const [monopolizers, setMonopolizers] = useState([]); + const [message, setMessage] = useState(''); + const [chatIsOpen, setChatIsOpen] = useState(false); + const [chatHistory, setChatHistory] = useState([]); useEffect(() => { async function setEcsStuff() { const {default: Components} = await import('@/ecs-components/index.js'); @@ -93,6 +97,9 @@ export default function Ui({disconnected}) { }, [disconnected]); useEffect(() => { return addKeyListener(document.body, ({event, type, payload}) => { + if (chatIsOpen) { + return; + } const KEY_MAP = { keyDown: 1, keyUp: 0, @@ -148,6 +155,12 @@ export default function Ui({disconnected}) { actionPayload = {type: 'use', value: KEY_MAP[type]}; break; } + case 'Enter': { + if ('keyDown' === type) { + setChatIsOpen(true); + } + break + } case 'e': { if (KEY_MAP[type]) { if (monopolizers.length > 0) { @@ -226,7 +239,7 @@ export default function Ui({disconnected}) { }); } }); - }, [client, debug, devtoolsIsOpen, monopolizers, setDebug, setScale]); + }, [chatIsOpen, client, debug, devtoolsIsOpen, monopolizers, setDebug, setScale]); usePacket('EcsChange', async () => { setMainEntity(undefined); setEcs(new ClientEcs({Components, Systems})); @@ -356,7 +369,6 @@ export default function Ui({disconnected}) { }); break; } - event.preventDefault(); }} onMouseUp={(event) => { switch (event.button) { @@ -413,6 +425,17 @@ export default function Ui({disconnected}) { scale={scale} setMonopolizers={setMonopolizers} /> + {chatIsOpen && ( + { + setChatIsOpen(false); + }} + /> + )} {showDisconnected && ( )}