main
Ashelyn Dawn 4 years ago
parent 01dcae094a
commit 64a0c4e0b5

@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link href="https://fonts.googleapis.com/css2?family=Major+Mono+Display&family=Share+Tech+Mono&family=Source+Code+Pro:wght@300&family=VT323&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta

@ -1,42 +1,18 @@
import React, {useRef, useEffect, useState} from 'react';
import ReactMarkdown from 'react-markdown'
import React, {useEffect, useState} from 'react';
import useWindowSize from '../../hooks/useWindowSize'
import styles from './App.module.css';
import Text from '../Text/Text';
import backgroundURL from './background.png'
function App({onCommand, game}) {
const inputRef = useRef()
const playAreaRef = useRef()
const [state, setState] = useState({})
const messages = state.messages || []
function onSubmit(ev) {
if(ev) ev.preventDefault();
if(!inputRef.current?.value.trim())
return;
if(inputRef.current){
onCommand(inputRef.current.value)
inputRef.current.value = ''
}
if(playAreaRef.current) {
const viewArea = playAreaRef.current.firstChild;
setImmediate(() => 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 (
<div className={styles.app}>
<div ref={playAreaRef} className={styles.playArea}>
<div className={styles.output}>
{messages.map((message, i) => {
if(message.type === 'message')
return <ReactMarkdown key={i}>{message.message}</ReactMarkdown>
if(message.type === 'command')
return <p key={i} className={styles.command}>{message.command}</p>
return null
})}
</div>
<form className={styles.input} onSubmit={onSubmit}>
<input ref={inputRef}/>
</form>
</div>
<div className={styles.infoArea}>
<pre>
{JSON.stringify(printedState, jsonReplacer, 2)}
</pre>
<div style={{transform: `scale(${scaleX}, ${scaleY})`, overflow: 'hidden'}} className={styles.screen}>
<Text messages={messages} handleCommand={onCommand}/>
<div className={styles.overlay}>
<img alt="" src={backgroundURL}/>
</div>
</div>
);
@ -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
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

@ -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 (
<div className={styles.reflectedArea}>
<div ref={outputRef} className={styles.output}>
{messages.map((message, i) => {
if(message.type === 'message')
return <ReactMarkdown key={i}>{message.message}</ReactMarkdown>
if(message.type === 'command')
return <p key={i} className={styles.command}>{message.command}</p>
return null
})}
</div>
<div className={styles.input}>
<input tabIndex="-1" value={currentInput}/>
</div>
</div>
)
}

@ -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 (
<>
<div ref={textRef} className={styles.playArea}>
<div ref={outputRef} onScroll={() => setCurrentScroll(outputRef.current?.scrollTop)} className={styles.output}>
{messages.map((message, i) => {
if(message.type === 'message')
return <ReactMarkdown key={i}>{message.message}</ReactMarkdown>
if(message.type === 'command')
return <p key={i} className={styles.command}>{message.command}</p>
return null
})}
</div>
<form className={styles.input} onSubmit={onSubmit}>
<input ref={inputRef} onChange={ev => setCurrentInput(ev.target.value)} id="gameInput"/>
</form>
</div>
<Reflection messages={messages} currentInput={currentInput} currentScroll={currentScroll}/>
</>
)
}

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

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

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

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

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

Loading…
Cancel
Save