Things can print to screen again

main
Ashelyn Dawn 4 years ago
parent 246418bcc4
commit 5f9c76ba72

@ -2,10 +2,11 @@ import React, {useRef, useEffect, useState} from 'react';
import ReactMarkdown from 'react-markdown' import ReactMarkdown from 'react-markdown'
import styles from './App.module.css'; import styles from './App.module.css';
function App({onCommand, messages, game}) { function App({onCommand, game}) {
const inputRef = useRef() const inputRef = useRef()
const playAreaRef = useRef() const playAreaRef = useRef()
const [state, setState] = useState({}) const [state, setState] = useState({})
const messages = state.messages || []
function onSubmit(ev) { function onSubmit(ev) {
if(ev) ev.preventDefault(); if(ev) ev.preventDefault();
@ -84,5 +85,8 @@ function jsonReplacer(key, value) {
return obj; return obj;
}, {}); }, {});
if(key === 'messages' && Array.isArray(value) && value.length)
return ['...']
return value return value
} }

@ -48,6 +48,8 @@
.infoArea { .infoArea {
width: 300px; width: 300px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll;
max-height: 100vh;
} }
.command { .command {

@ -1,6 +1,7 @@
import {enableMapSet, createDraft, finishDraft, Draft} from 'immer' import {enableMapSet, createDraft, finishDraft, Draft} from 'immer'
import GameState, { GameObject, Room, Door, Item, ObjectType } from './types/GameState' import GameState, { GameObject, Room, Door, Item, ObjectType } from './types/GameState'
import ParsedCommand, { TokenType, ParsedTokenExpression, ValidCommandDetails, InvalidCommandDetails } from './types/ParsedCommand' import ParsedCommand, { ValidCommandDetails, InvalidCommandDetails } from './types/ParsedCommand'
import { GameEventMessage, GameEventCommand } from './types/GameEvent'
enableMapSet() enableMapSet()
@ -13,7 +14,15 @@ export type CommandValidateResult = {
} }
export default class Game { export default class Game {
private gameState : GameState = {directions: new Map(), rooms: new Map(), doors: new Map(), items: new Map(), player: {location: ''}} private gameState : GameState = {
directions: new Map(),
rooms: new Map(),
doors: new Map(),
items: new Map(),
player: {location: ''},
messages: []
}
private draft : Draft<GameState> | null = null private draft : Draft<GameState> | null = null
private onChangeListeners : ChangeListener [] = [] private onChangeListeners : ChangeListener [] = []
@ -29,6 +38,16 @@ export default class Game {
this.saveDraft() this.saveDraft()
} }
outputCommand(commandString: string) {
const state = this.getState()
state.messages.push(new GameEventCommand(commandString))
}
say(message: string) {
const state = this.getState()
state.messages.push(new GameEventMessage(message))
}
filterValidCommands(commands: ParsedCommand[]) : CommandValidateResult { filterValidCommands(commands: ParsedCommand[]) : CommandValidateResult {
let validCommands : ValidCommandDetails[] = [] let validCommands : ValidCommandDetails[] = []
let invalidCommands : InvalidCommandDetails[] = [] let invalidCommands : InvalidCommandDetails[] = []
@ -43,7 +62,12 @@ export default class Game {
} }
} }
invalidCommands.sort((a,b) => a.severity - b.severity) invalidCommands.sort((a,b) => {
if(a.severity !== b.severity)
return a.severity - b.severity
return b.command.getNumTokens() - a.command.getNumTokens()
})
return {validCommands, invalidCommands} return {validCommands, invalidCommands}
} }

@ -1,6 +1,6 @@
import Game from "./Game"; import Game from "./Game";
import RulesEngine from './RulesEngine' import RulesEngine from './RulesEngine'
import ParsedCommand from "./types/ParsedCommand"; import ParsedCommand, { InvalidCommandDetails } from "./types/ParsedCommand";
import Verb from './types/Verb'; import Verb from './types/Verb';
export default class Parser { export default class Parser {
@ -14,6 +14,8 @@ export default class Parser {
} }
handleCommand(rawCommand : string) { handleCommand(rawCommand : string) {
this.game.outputCommand(rawCommand)
// Parse command for syntactical validity // Parse command for syntactical validity
// (according to known verb templates) // (according to known verb templates)
const grammaticalParsings : ParsedCommand[] = this.parseCommandString(rawCommand) const grammaticalParsings : ParsedCommand[] = this.parseCommandString(rawCommand)
@ -21,6 +23,15 @@ export default class Parser {
// Ask the game state container to filter commands for object validity // Ask the game state container to filter commands for object validity
// (nouns refer to valid objects, all objects are visible, etc) // (nouns refer to valid objects, all objects are visible, etc)
const validationResult = this.game.filterValidCommands(grammaticalParsings) const validationResult = this.game.filterValidCommands(grammaticalParsings)
if(validationResult.validCommands.length < 1) {
this.handleError(validationResult.invalidCommands)
} else {
// TODO: Do the thing
}
this.game.saveDraft()
console.log(validationResult) console.log(validationResult)
} }
@ -35,6 +46,25 @@ export default class Parser {
return parsings return parsings
} }
handleError(invalidCommands: InvalidCommandDetails []) {
console.log(invalidCommands)
if(!invalidCommands.length){
this.game.say("I'm unsure what you're trying to do")
return
}
const mostValid = invalidCommands[0]
if(mostValid.command.verb.name === 'go'){
this.game.say("I'm unsure what you're trying to do")
return
}
this.game.say(mostValid.reason)
console.log(mostValid)
}
understand(name : string) : VerbBuilder { understand(name : string) : VerbBuilder {
const verb = new Verb(name) const verb = new Verb(name)
this.verbs.push(verb) this.verbs.push(verb)

@ -32,7 +32,7 @@ export default class Renderer {
private render() { private render() {
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App messages={this.output} game={this.game} onCommand={this.handleCommand.bind(this)}/> <App game={this.game} onCommand={this.handleCommand.bind(this)}/>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
) )

@ -3,24 +3,28 @@ export enum GameEventType {
Command = 'command' Command = 'command'
} }
export default interface GameEvent { export default class GameEvent {
getType() : GameEventType; readonly type : GameEventType
constructor(type : GameEventType) {
this.type = type
}
} }
export class GameEventMessage implements GameEvent { export class GameEventMessage extends GameEvent {
private message : string readonly message : string
getType() : GameEventType { return GameEventType.Message }
constructor(message : string) { constructor(message : string) {
super(GameEventType.Message)
this.message = message this.message = message
} }
} }
export class GameEventCommand implements GameEvent { export class GameEventCommand extends GameEvent {
private rawCommand : string readonly command : string
getType() : GameEventType { return GameEventType.Command } constructor(command : string) {
constructor(rawCommand : string) { super(GameEventType.Command)
this.rawCommand = rawCommand this.command = command
} }
} }

@ -1,3 +1,5 @@
import GameEvent from './GameEvent'
type GameState = { type GameState = {
readonly directions: Map<ObjectID, Direction>, readonly directions: Map<ObjectID, Direction>,
readonly rooms: Map<ObjectID, Room>, readonly rooms: Map<ObjectID, Room>,
@ -6,6 +8,7 @@ type GameState = {
readonly player: { readonly player: {
readonly location: ObjectID readonly location: ObjectID
} }
readonly messages: GameEvent []
} }
export default GameState export default GameState

@ -54,6 +54,10 @@ export default class ParsedCommand {
this.verb = verb this.verb = verb
} }
getNumTokens() : number {
return this.tokens.length
}
prepend(token : ParsedToken) { prepend(token : ParsedToken) {
const newCommand = new ParsedCommand(this.verb) const newCommand = new ParsedCommand(this.verb)
newCommand.tokens = [token, ...this.tokens] newCommand.tokens = [token, ...this.tokens]
@ -76,11 +80,14 @@ export default class ParsedCommand {
severity: ParsingErrorSeverity.NoSuchObject severity: ParsingErrorSeverity.NoSuchObject
} }
// TODO: Optionally print "the" depending on if the original
// command name had one at the beginning (don't print the the book)
// (but also don't do "you cannot see heart of the cards")
if(!game.isVisible(gameObject)) if(!game.isVisible(gameObject))
return { return {
isValid: false, isValid: false,
command: this, command: this,
reason: `You cannot see ${noun.name}`, reason: `You cannot see the ${noun.name}`,
severity: ParsingErrorSeverity.NotVisible severity: ParsingErrorSeverity.NotVisible
} }

@ -86,6 +86,13 @@ game.addItem({
location: 'entry' location: 'entry'
}) })
game.addItem({
type: ObjectType.Item,
name: 'ruby',
aliases: ['gem'],
location: 'office'
})
game.getState().player.location = 'entry' game.getState().player.location = 'entry'
game.saveDraft() game.saveDraft()

Loading…
Cancel
Save