diff --git a/public/index.html b/public/index.html index aa069f2..23c4c3d 100644 --- a/public/index.html +++ b/public/index.html @@ -3,6 +3,7 @@ + viewArea.scrollTop = viewArea.scrollHeight) - } - } - - useEffect(() => { - if(!playAreaRef.current) return; - - const playArea = playAreaRef.current - - function onClick() { - inputRef.current.focus() - } - - playArea.addEventListener('click', onClick) - return () => playArea.removeEventListener('click', onClick) - }, []) + const {width, height} = useWindowSize() + const scaleX = width / 600 + const scaleY = height / 400 + // const scale = 0 || Math.min(scaleX, scaleY) useEffect(() => { game.onChange(setState) @@ -44,30 +20,11 @@ function App({onCommand, game}) { game.saveDraft() }, [game]) - const {directions, ...printedState} = state - return ( -
-
-
- {messages.map((message, i) => { - if(message.type === 'message') - return {message.message} - - if(message.type === 'command') - return

{message.command}

- - return null - })} -
-
- -
-
-
-
-          {JSON.stringify(printedState, jsonReplacer, 2)}
-        
+
+ +
+
); @@ -75,18 +32,3 @@ function App({onCommand, game}) { export default App; -function jsonReplacer(key, value) { - if(value instanceof Set) - return Array.from(value) - - if(value instanceof Map) - return Array.from(value).reduce((obj, [key, value]) => { - obj[key] = value; - return obj; - }, {}); - - if(key === 'messages' && Array.isArray(value) && value.length) - return ['...'] - - return value -} diff --git a/src/components/App/App.module.css b/src/components/App/App.module.css index c5f0a59..1d3a501 100644 --- a/src/components/App/App.module.css +++ b/src/components/App/App.module.css @@ -1,72 +1,29 @@ -.app { - min-height: 100vh; +.screen { display: flex; flex-direction: row; -} - -.playArea { - flex: 1; - display: flex; - flex-direction: column; - max-height: 100vh; - user-select: none; -} - -.output { - overflow-y: auto; - padding: 16px; - padding-bottom: 0; - min-height: 16px; -} - -.input { - padding: 16px; - padding-top: 0; position: relative; - left: -1px; - font-weight: bold; + width: 600px; + height: 400px; + background: black; + image-rendering: pixelated; } -.input input { - background: transparent; - border: none; - box-shadow: none; - outline: none; - width: 100%; - text-indent: 16px; - font-family: inherit; - font-weight: bold; - font-size: 16px; - position: relative; - top: -1px; - left: -1px; -} - -.input::before { +.overlay { position: absolute; - left: 16px; -} + top: 0; + right: 0; + left: 0; + bottom: 0; -.infoArea { - width: 300px; - overflow-x: hidden; - overflow-y: scroll; - max-height: 100vh; -} - -.command { - font-weight: bold; - position: relative; - text-indent: 16px; - white-space: pre; + opacity: .4; + overflow: hidden; + pointer-events: none; + mix-blend-mode: screen; } -.command::before { - position: absolute; - left: -16px; -} - -.command::before, .input::before { - content: '> '; - font-weight: bold; +.overlay img { + width: 100%; + height: 100%; + object-fit: cover; + filter: brightness(2.5); } diff --git a/src/components/App/background.png b/src/components/App/background.png new file mode 100644 index 0000000..03ea4b7 Binary files /dev/null and b/src/components/App/background.png differ diff --git a/src/components/Text/ReflectedText.js b/src/components/Text/ReflectedText.js new file mode 100644 index 0000000..a5f4fc4 --- /dev/null +++ b/src/components/Text/ReflectedText.js @@ -0,0 +1,30 @@ +import React, {useLayoutEffect, useRef} from 'react' +import ReactMarkdown from 'react-markdown' +import styles from './Text.module.css' + +export default function Text({messages, currentInput, currentScroll}) { + const outputRef = useRef() + + useLayoutEffect(() => { + outputRef.current.scrollTop = currentScroll + }, [currentScroll]) + + return ( +
+
+ {messages.map((message, i) => { + if(message.type === 'message') + return {message.message} + + if(message.type === 'command') + return

{message.command}

+ + return null + })} +
+
+ +
+
+ ) +} diff --git a/src/components/Text/Text.js b/src/components/Text/Text.js new file mode 100644 index 0000000..3931987 --- /dev/null +++ b/src/components/Text/Text.js @@ -0,0 +1,64 @@ +import React, {useState, useEffect, useRef} from 'react' +import ReactMarkdown from 'react-markdown' +import styles from './Text.module.css' +import Reflection from './ReflectedText' + +export default function Text({messages, handleCommand}) { + const inputRef = useRef() + const outputRef = useRef() + const textRef = useRef() + + const [currentInput, setCurrentInput] = useState('') + const [currentScroll, setCurrentScroll] = useState(0) + + function onSubmit(ev) { + if(ev) ev.preventDefault() + + if(!inputRef.current?.value.trim()) + return; + + if(inputRef.current){ + handleCommand(inputRef.current.value) + inputRef.current.value = '' + setCurrentInput('') + } + + if(outputRef.current) { + setImmediate(() => outputRef.current.scrollTop = outputRef.current.scrollHeight) + setCurrentScroll(outputRef.current.scrollHeight) + } + } + + useEffect(() => { + const playArea = textRef.current + + function onClick() { + inputRef.current.focus() + } + + playArea.addEventListener('click', onClick) + return () => playArea.removeEventListener('click', onClick) + }, []) + + return ( + <> +
+
setCurrentScroll(outputRef.current?.scrollTop)} className={styles.output}> + {messages.map((message, i) => { + if(message.type === 'message') + return {message.message} + + if(message.type === 'command') + return

{message.command}

+ + return null + })} +
+
+ setCurrentInput(ev.target.value)} id="gameInput"/> +
+
+ + + ) +} diff --git a/src/components/Text/Text.module.css b/src/components/Text/Text.module.css new file mode 100644 index 0000000..7414b48 --- /dev/null +++ b/src/components/Text/Text.module.css @@ -0,0 +1,80 @@ +.playArea { + flex: 1; + display: flex; + flex-direction: column; + max-height: 100%; + user-select: none; +} + +.reflectedArea { + display: flex; + flex-direction: column; + position: absolute; + width: 100%; + height: 100%; + transform: scale(.14, .12); + margin-top: -3px; + pointer-events: none; + opacity: .2; + filter: blur(2px); + overflow: hidden; +} + +.output { + overflow-y: auto; + padding: 16px; + padding-bottom: 0; + min-height: 16px; +} + +.input { + padding: 16px; + padding-top: 0; + position: relative; + font-weight: bold; +} + +.input input { + background: transparent; + border: none; + box-shadow: none; + outline: none; + width: 100%; + text-indent: 16px; + font-family: inherit; + font-weight: bold; + font-size: 16px; + position: relative; + top: -1px; + left: -1px; + color: inherit; +} + +.input::before { + position: absolute; + left: 16px; +} + +.command { + font-weight: bold; + position: relative; + text-indent: 16px; + white-space: pre; +} + +.command::before { + position: absolute; + left: -16px; +} + +.command::before, .input::before { + content: '> '; + font-weight: bold; +} + +/** + * Shadow + */ +.playArea { + +} diff --git a/src/engine/Game.ts b/src/engine/Game.ts index b436ddc..f8dfe52 100644 --- a/src/engine/Game.ts +++ b/src/engine/Game.ts @@ -158,13 +158,11 @@ export default class Game { findObjectsInRoom(name : string | undefined) : Item [] { let items : Item [] = [] - console.log(items) for(const item of this.getState().items.values()) if(item.location === name) items.push(item) - console.log(items) return items; } diff --git a/src/engine/Parser.ts b/src/engine/Parser.ts index 64bf82d..0583c7e 100644 --- a/src/engine/Parser.ts +++ b/src/engine/Parser.ts @@ -60,8 +60,6 @@ export default class Parser { } handleError(invalidCommands: InvalidCommandDetails []) { - console.log(invalidCommands) - if(!invalidCommands.length){ throw new Error("I'm unsure what you're trying to do") } @@ -73,7 +71,6 @@ export default class Parser { } this.game.say(mostValid.reason) - console.log(mostValid) } understand(name : string) : VerbBuilder { diff --git a/src/hooks/useWindowSize.js b/src/hooks/useWindowSize.js new file mode 100644 index 0000000..3825118 --- /dev/null +++ b/src/hooks/useWindowSize.js @@ -0,0 +1,23 @@ +import { useState, useEffect } from 'react'; + +export default function useWindowSize() { + const [windowSize, setWindowSize] = useState(getSize); + + useEffect(() => { + function handleResize() { + setWindowSize(getSize()); + } + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return windowSize; +} + +function getSize() { + return { + width: window.innerWidth, + height: window.innerHeight + }; +} diff --git a/src/index.css b/src/index.css index 6e8a07a..c9ba7be 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,16 @@ html, body { margin: 0; + background: black; + color: white; +} + +#root { + display: flex; + min-height: 100vh; + align-items: center; + justify-content: center; + background: black; + /* font-family: 'VT323', monospace; */ + font-family: 'Share Tech Mono', monospace; + overflow: hidden; }