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 styles from './App.module.css';
function App({onCommand, messages, game}) {
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();
@ -84,5 +85,8 @@ function jsonReplacer(key, value) {
return obj;
}, {});
if(key === 'messages' && Array.isArray(value) && value.length)
return ['...']
return value
}

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

@ -1,6 +1,7 @@
import {enableMapSet, createDraft, finishDraft, Draft} from 'immer'
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()
@ -13,7 +14,15 @@ export type CommandValidateResult = {
}
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 onChangeListeners : ChangeListener [] = []
@ -29,6 +38,16 @@ export default class Game {
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 {
let validCommands : ValidCommandDetails[] = []
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}
}
@ -104,15 +128,15 @@ export default class Game {
case ObjectType.Door:
collection = this.getState().doors
break
case ObjectType.Item:
collection = this.getState().items
break
case ObjectType.Room:
collection = this.getState().rooms
break
case ObjectType.Direction:
collection = this.getState().directions
break
@ -123,7 +147,7 @@ export default class Game {
const exactMatch = objects.find((object) => name === object.name)
if(exactMatch)
return exactMatch
const aliasMatch = objects.find(({aliases}) => aliases.includes(name))
if(aliasMatch)
return aliasMatch

@ -1,6 +1,6 @@
import Game from "./Game";
import RulesEngine from './RulesEngine'
import ParsedCommand from "./types/ParsedCommand";
import ParsedCommand, { InvalidCommandDetails } from "./types/ParsedCommand";
import Verb from './types/Verb';
export default class Parser {
@ -14,6 +14,8 @@ export default class Parser {
}
handleCommand(rawCommand : string) {
this.game.outputCommand(rawCommand)
// Parse command for syntactical validity
// (according to known verb templates)
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
// (nouns refer to valid objects, all objects are visible, etc)
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)
}
@ -35,6 +46,25 @@ export default class Parser {
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 {
const verb = new Verb(name)
this.verbs.push(verb)

@ -32,7 +32,7 @@ export default class Renderer {
private render() {
ReactDOM.render(
<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>,
document.getElementById('root')
)

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

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

@ -54,6 +54,10 @@ export default class ParsedCommand {
this.verb = verb
}
getNumTokens() : number {
return this.tokens.length
}
prepend(token : ParsedToken) {
const newCommand = new ParsedCommand(this.verb)
newCommand.tokens = [token, ...this.tokens]
@ -65,7 +69,7 @@ export default class ParsedCommand {
let subject : GameObject | null = null
let object : GameObject | null = null
for(const noun of nouns) {
let gameObject = game.findObjectByName(noun.name, noun.itemType)
if(!gameObject)
@ -76,11 +80,14 @@ export default class ParsedCommand {
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))
return {
isValid: false,
command: this,
reason: `You cannot see ${noun.name}`,
reason: `You cannot see the ${noun.name}`,
severity: ParsingErrorSeverity.NotVisible
}

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

Loading…
Cancel
Save