Add ability to restart the game
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
theme: jekyll-theme-hacker
|
|
||||||
+104
-86
@@ -4,6 +4,10 @@ class GameLog
|
|||||||
@content = ""
|
@content = ""
|
||||||
@onChange = onChange
|
@onChange = onChange
|
||||||
|
|
||||||
|
clear: ->
|
||||||
|
@content = ""
|
||||||
|
@onChange(this)
|
||||||
|
|
||||||
echoInput: (text)->
|
echoInput: (text)->
|
||||||
@content += "<br>> " + text + "<br>"
|
@content += "<br>> " + text + "<br>"
|
||||||
@onChange(this)
|
@onChange(this)
|
||||||
@@ -21,22 +25,33 @@ class GameLog
|
|||||||
class Inventory
|
class Inventory
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
@items = {}
|
@clear()
|
||||||
@length = 0
|
|
||||||
|
# Properties ###################################################################################
|
||||||
|
|
||||||
|
Object.defineProperties @prototype,
|
||||||
|
length:
|
||||||
|
get: -> return @_length
|
||||||
|
|
||||||
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
add: (item)->
|
add: (item)->
|
||||||
if not @items[item.name]
|
if not @_items[item.name]
|
||||||
@items[item.name] = item
|
@_items[item.name] = item
|
||||||
@length += 1
|
@_length += 1
|
||||||
|
|
||||||
all: ->
|
all: ->
|
||||||
return (item for name, item of @items)
|
return (item for name, item of @_items)
|
||||||
|
|
||||||
|
clear: ->
|
||||||
|
@_items = {}
|
||||||
|
@_length = 0
|
||||||
|
|
||||||
describe: (options={})->
|
describe: (options={})->
|
||||||
options.simple ?= false
|
options.simple ?= false
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for name, item of @items
|
for name, item of @_items
|
||||||
if options.simple
|
if options.simple
|
||||||
result.push(item.name)
|
result.push(item.name)
|
||||||
else
|
else
|
||||||
@@ -45,16 +60,16 @@ class Inventory
|
|||||||
return result.join("\n")
|
return result.join("\n")
|
||||||
|
|
||||||
eachItem: (withItem=(->))->
|
eachItem: (withItem=(->))->
|
||||||
for name, item of @items
|
for name, item of @_items
|
||||||
withItem(item)
|
withItem(item)
|
||||||
|
|
||||||
has: (item)->
|
has: (item)->
|
||||||
return !! @items[item.name]
|
return !! @_items[item.name]
|
||||||
|
|
||||||
remove: (itemToRemove)->
|
remove: (itemToRemove)->
|
||||||
if @items[itemToRemove.name]
|
if @_items[itemToRemove.name]
|
||||||
delete @items[itemToRemove.name]
|
delete @_items[itemToRemove.name]
|
||||||
@length -= 1
|
@_length -= 1
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -96,6 +111,8 @@ class Location
|
|||||||
@transitions = []
|
@transitions = []
|
||||||
@visited = false
|
@visited = false
|
||||||
|
|
||||||
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
addTransition: (direction, toLocation, locked=false)->
|
addTransition: (direction, toLocation, locked=false)->
|
||||||
@transitions[direction] = new Transition(direction, toLocation, locked)
|
@transitions[direction] = new Transition(direction, toLocation, locked)
|
||||||
|
|
||||||
@@ -111,6 +128,7 @@ class Location
|
|||||||
result.push("\n")
|
result.push("\n")
|
||||||
result.push(@description)
|
result.push(@description)
|
||||||
|
|
||||||
|
console.log("@inventory.length: #{@inventory.length}")
|
||||||
if @inventory.length > 0
|
if @inventory.length > 0
|
||||||
result.push("")
|
result.push("")
|
||||||
result.push(@inventory.describe())
|
result.push(@inventory.describe())
|
||||||
@@ -142,10 +160,7 @@ class ParseError extends Error
|
|||||||
class Parser
|
class Parser
|
||||||
|
|
||||||
constructor: (@story)->
|
constructor: (@story)->
|
||||||
@aliases = {}
|
@restart()
|
||||||
@directions = []
|
|
||||||
@verbs = {}
|
|
||||||
@fillerWords = new Set()
|
|
||||||
|
|
||||||
addAliases: (aliasMap)->
|
addAliases: (aliasMap)->
|
||||||
for alias, meaning of aliasMap
|
for alias, meaning of aliasMap
|
||||||
@@ -178,6 +193,12 @@ class Parser
|
|||||||
@_validateSentence(sentence)
|
@_validateSentence(sentence)
|
||||||
return sentence
|
return sentence
|
||||||
|
|
||||||
|
restart: ->
|
||||||
|
@aliases = {}
|
||||||
|
@directions = []
|
||||||
|
@fillerWords = new Set()
|
||||||
|
@verbs = {}
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
_normalizeSentence: (sentence)->
|
_normalizeSentence: (sentence)->
|
||||||
@@ -278,9 +299,7 @@ class Player
|
|||||||
constructor: (@story)->
|
constructor: (@story)->
|
||||||
@inventory = new Inventory()
|
@inventory = new Inventory()
|
||||||
@onChange = (->)
|
@onChange = (->)
|
||||||
@verbs = {}
|
@restart()
|
||||||
|
|
||||||
@_score = 0
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@@ -326,6 +345,12 @@ class Player
|
|||||||
|
|
||||||
@story.arrive(location)
|
@story.arrive(location)
|
||||||
|
|
||||||
|
restart: ->
|
||||||
|
@verbs = {}
|
||||||
|
@inventory.clear()
|
||||||
|
@score = 0
|
||||||
|
@_configureDefaultVerbs()
|
||||||
|
|
||||||
take: (item)->
|
take: (item)->
|
||||||
localItems = @story.currentLocation.inventory
|
localItems = @story.currentLocation.inventory
|
||||||
if not item
|
if not item
|
||||||
@@ -338,13 +363,51 @@ class Player
|
|||||||
|
|
||||||
if not foundTakeableItem then @story.log.writeln("There's nothing here you can take.")
|
if not foundTakeableItem then @story.log.writeln("There's nothing here you can take.")
|
||||||
else if localItems.has(item)
|
else if localItems.has(item)
|
||||||
console.log(item)
|
|
||||||
if item.take()
|
if item.take()
|
||||||
localItems.remove(item)
|
localItems.remove(item)
|
||||||
@inventory.add(item)
|
@inventory.add(item)
|
||||||
else
|
else
|
||||||
throw new ParseError("There isn't a #{item} here.")
|
throw new ParseError("There isn't a #{item} here.")
|
||||||
|
|
||||||
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
|
_configureDefaultVerbs: ->
|
||||||
|
@addVerb "drop", (sentence)=>
|
||||||
|
if sentence.has(item: 0)
|
||||||
|
@drop()
|
||||||
|
else
|
||||||
|
for itemToken in sentence.tokens.item
|
||||||
|
@drop(itemToken.referant)
|
||||||
|
|
||||||
|
@addVerb "go", (sentence)=>
|
||||||
|
if sentence.has(location: 1)
|
||||||
|
@move(sentence.location)
|
||||||
|
else
|
||||||
|
throw new ParseError("I'm not sure where you want to go...")
|
||||||
|
|
||||||
|
@addVerb "inventory", =>
|
||||||
|
if @inventory.length is 0
|
||||||
|
@story.log.writeln("You're not carrying anything.")
|
||||||
|
else
|
||||||
|
@story.log.writeln("You have:")
|
||||||
|
@story.log.writeln(@inventory.describe(simple: true))
|
||||||
|
|
||||||
|
@addVerb "look", (sentence)=>
|
||||||
|
if sentence.has(item: 1)
|
||||||
|
@story.log.writeln(sentence.item.describe(verbose: true))
|
||||||
|
else
|
||||||
|
@story.log.writeln(@story.currentLocation.describe(verbose: true))
|
||||||
|
|
||||||
|
@addVerb "restart", =>
|
||||||
|
@story.restart()
|
||||||
|
|
||||||
|
@addVerb "take", (sentence)=>
|
||||||
|
if sentence.has(item: 0)
|
||||||
|
@take()
|
||||||
|
else
|
||||||
|
for item in sentence.items
|
||||||
|
@take(item)
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
@@ -377,6 +440,8 @@ class Sentence
|
|||||||
Object.defineProperties @prototype,
|
Object.defineProperties @prototype,
|
||||||
"item":
|
"item":
|
||||||
get: -> return @tokens.item[0].referant
|
get: -> return @tokens.item[0].referant
|
||||||
|
"items":
|
||||||
|
get: -> (i.referant for i in @tokens.item)
|
||||||
"location":
|
"location":
|
||||||
get: -> return @tokens.location[0].referant
|
get: -> return @tokens.location[0].referant
|
||||||
"verb":
|
"verb":
|
||||||
@@ -387,18 +452,12 @@ class Sentence
|
|||||||
|
|
||||||
class Story
|
class Story
|
||||||
|
|
||||||
constructor: (@title)->
|
constructor: (@title, @onRestart=(->))->
|
||||||
@currentLocation = null
|
|
||||||
@items = []
|
|
||||||
@locations = []
|
|
||||||
@log = new GameLog()
|
@log = new GameLog()
|
||||||
@onChange = (->)
|
|
||||||
@parser = new Parser(this)
|
@parser = new Parser(this)
|
||||||
@player = new Player(this)
|
@player = new Player(this)
|
||||||
|
|
||||||
@_turns = 0
|
@onChange = (->)
|
||||||
|
|
||||||
@_configure()
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@@ -434,11 +493,6 @@ class Story
|
|||||||
|
|
||||||
location.visited = true
|
location.visited = true
|
||||||
|
|
||||||
begin: ->
|
|
||||||
@log.writeln(@title)
|
|
||||||
@log.writeln()
|
|
||||||
@arrive(@currentLocation)
|
|
||||||
|
|
||||||
interpret: (userInput)->
|
interpret: (userInput)->
|
||||||
try
|
try
|
||||||
@log.echoInput(userInput)
|
@log.echoInput(userInput)
|
||||||
@@ -453,67 +507,31 @@ class Story
|
|||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
_configure: ->
|
restart: ->
|
||||||
|
@currentLocation = null
|
||||||
|
@items = []
|
||||||
|
@locations = []
|
||||||
|
@turns = 0
|
||||||
|
|
||||||
|
@log.clear()
|
||||||
|
@parser.restart()
|
||||||
|
@player.restart()
|
||||||
|
|
||||||
@parser.addDirections(
|
@parser.addDirections(
|
||||||
"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down"
|
"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down"
|
||||||
)
|
)
|
||||||
@parser.addFillerWords(
|
@parser.addFillerWords(
|
||||||
"a",
|
"a", "all", "an", "around", "at", "everything", "of", "the", "to"
|
||||||
"an",
|
|
||||||
"around",
|
|
||||||
"at",
|
|
||||||
"of",
|
|
||||||
"the",
|
|
||||||
"to"
|
|
||||||
)
|
)
|
||||||
@parser.addAliases({
|
@parser.addAliases({
|
||||||
"d": "down",
|
"d": "down", "e": "east", "g": "go", "i": "inventory", "l": "look", "n": "north", "ne": "northeast",
|
||||||
"e": "east",
|
"nw": "northwest", "s": "south", "se": "southeast", "sw": "southwest", "u": "up", "w": "west",
|
||||||
"everything": "all",
|
|
||||||
"g": "go"
|
|
||||||
"i": "inventory"
|
|
||||||
"l": "look",
|
|
||||||
"n": "north",
|
|
||||||
"ne": "northeast",
|
|
||||||
"nw": "northwest",
|
|
||||||
"s": "south",
|
|
||||||
"se": "southeast",
|
|
||||||
"sw": "southwest",
|
|
||||||
"u": "up",
|
|
||||||
"w": "west",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
@player.addVerb "drop", (sentence)=>
|
@onRestart()
|
||||||
if sentence.has(item: 0)
|
@log.writeln(@title)
|
||||||
@player.drop()
|
@log.writeln()
|
||||||
else
|
@arrive(@currentLocation)
|
||||||
for itemToken in sentence.tokens.item
|
|
||||||
@player.drop(itemToken.referant)
|
|
||||||
|
|
||||||
@player.addVerb "go", (sentence)=>
|
|
||||||
if sentence.has(location: 1)
|
|
||||||
@player.move(sentence.location)
|
|
||||||
else
|
|
||||||
throw new ParseError("I'm not sure where you want to go...")
|
|
||||||
|
|
||||||
@player.addVerb "inventory", =>
|
|
||||||
if @player.inventory.length is 0
|
|
||||||
@log.writeln("You're not carrying anything.")
|
|
||||||
else
|
|
||||||
@log.writeln("You have:")
|
|
||||||
@log.writeln(@player.inventory.describe(simple: true))
|
|
||||||
|
|
||||||
@player.addVerb "look", (sentence)=>
|
|
||||||
if sentence.has(item: 1)
|
|
||||||
@log.writeln(sentence.item.describe(verbose: true))
|
|
||||||
else
|
|
||||||
@log.writeln(@currentLocation.describe(verbose: true))
|
|
||||||
|
|
||||||
@player.addVerb "take", (sentence)=>
|
|
||||||
if sentence.has(item: 1)
|
|
||||||
@player.take(sentence.item)
|
|
||||||
else if sentence.has(item: 0)
|
|
||||||
@player.take()
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="styles.css"/>
|
<link rel="stylesheet" type="text/css" href="styles.css"/>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div class="game">
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<span class="turns"> </span>
|
<span class="turns"> </span>
|
||||||
<span class="score"> </span>
|
<span class="score"> </span>
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
<p class="log-text"></p>
|
<p class="log-text"></p>
|
||||||
</div>
|
</div>
|
||||||
<input class="entry" type="text"></input>
|
<input class="entry" type="text"></input>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
<script src="https://coffeescript.org/v2/browser-compiler/coffeescript.js"></script>
|
<script src="https://coffeescript.org/v2/browser-compiler/coffeescript.js"></script>
|
||||||
|
|||||||
+1
-1
@@ -33,5 +33,5 @@ $(document).ready ->
|
|||||||
STORY.onChange(STORY)
|
STORY.onChange(STORY)
|
||||||
|
|
||||||
# start the story
|
# start the story
|
||||||
STORY.begin()
|
STORY.restart()
|
||||||
$input.focus()
|
$input.focus()
|
||||||
|
|||||||
+21
-17
@@ -1,39 +1,43 @@
|
|||||||
window.STORY = s = new Story("A Walk Through My House")
|
window.STORY = new Story "A Walk Through My House", ->
|
||||||
|
|
||||||
# Items ################################################################################################################
|
# Items ################################################################################################################
|
||||||
|
|
||||||
boxOfTile = s.addItem("box of tiles")
|
boxOfTile = @addItem "box of tiles"
|
||||||
boxOfTile.description =
|
boxOfTile.description =
|
||||||
"This appears to be a box of flooring tiles. The tiles appear to be a very light color of natural stone. It is
|
"This appears to be a box of flooring tile@ The tiles appear to be a very light color of natural stone. It is
|
||||||
quite heavy."
|
quite heavy."
|
||||||
boxOfTile.take = ->
|
boxOfTile.take = =>
|
||||||
s.log.writeln("Oof! It's too heavy.")
|
@log.writeln "Oof! It's too heavy."
|
||||||
return false
|
return false
|
||||||
|
|
||||||
hose = s.addItem("garden hose")
|
car = @addItem "your car"
|
||||||
|
car.description =
|
||||||
|
"Your car isn't anything special, but it gets you from place to place."
|
||||||
|
|
||||||
|
hose = @addItem "garden hose"
|
||||||
hose.description = "It's a pretty ordinary garden hose about 20' long."
|
hose.description = "It's a pretty ordinary garden hose about 20' long."
|
||||||
|
|
||||||
# Locations ############################################################################################################
|
# Locations ############################################################################################################
|
||||||
|
|
||||||
driveway = s.addLocation("Driveway")
|
driveway = @addLocation "Driveway"
|
||||||
driveway.description =
|
driveway.description =
|
||||||
"The driveway extends slightly uphill a short way to the west back to the street. At this part, it's recessed a
|
"The driveway extends slightly uphill a short way to the west back to the street. At this part, it's recessed a
|
||||||
little below the level of the lawn with a stone wall defining the boundary. To the south are three garage doors
|
little below the level of the lawn with a stone wall defining the boundary. To the south are three garage doors
|
||||||
which make up the entire side of the house. Above the doors are a few windows. The back yard is to the east, and
|
which make up the entire side of the house. Above the doors are a few windows. The back yard is to the east, and
|
||||||
the some steps lead up to the front porch to the southwest."
|
the some steps lead up to the front porch to the southwest."
|
||||||
driveway.addItem(hose)
|
driveway.addItem hose
|
||||||
|
|
||||||
frontPorch = s.addLocation("Front Porch")
|
frontPorch = @addLocation "Front Porch"
|
||||||
frontPorch.description =
|
frontPorch.description =
|
||||||
"You're standing on the front porch. It's a large wooden deck with an overhang above, and a railing all about. To
|
"You're standing on the front porch. It's a large wooden deck with an overhang above, and a railing all about.
|
||||||
the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside the
|
To the north, a staircase leads down a dozen steps or so to the driveway. To the south double door leads inside
|
||||||
house. It has a fancy crystal inset in the windows, so while you can see a light on inside the house, you can't make
|
the house. It has a fancy crystal inset in the windows, so while you can see a light on inside the house, you
|
||||||
out any details."
|
can't make out any details."
|
||||||
frontPorch.addItem(boxOfTile)
|
frontPorch.addItem boxOfTile
|
||||||
|
|
||||||
# Configure Map ########################################################################################################
|
# Configure Map ########################################################################################################
|
||||||
|
|
||||||
driveway.addTransition("southwest", frontPorch)
|
driveway.addTransition "southwest", frontPorch
|
||||||
frontPorch.addTransition("north", driveway)
|
frontPorch.addTransition "north", driveway
|
||||||
|
|
||||||
s.currentLocation = frontPorch
|
@currentLocation = frontPorch
|
||||||
|
|||||||
+21
-9
@@ -3,47 +3,59 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
flex-direction: column;
|
height: 100%;
|
||||||
justify-content: center;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
background: black;
|
background: black;
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 1px solid gray;
|
border-top: 1px solid #444;
|
||||||
color: green;
|
color: green;
|
||||||
|
flex: 0 0 auto;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
width: 800px;
|
}
|
||||||
|
|
||||||
|
.game {
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: stretch;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log {
|
.log {
|
||||||
background: black;
|
background: black;
|
||||||
color: green;
|
color: green;
|
||||||
height: 600px;
|
flex: 1 1 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0.25em 1em;
|
padding: 0.25em 1em;
|
||||||
width: 800px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
background: black;
|
background: black;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid gray;
|
border-bottom: 1px solid #444;
|
||||||
color: green;
|
color: green;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5em 1em;
|
padding: 0.5em 1em;
|
||||||
width: 800px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.score {
|
.score {
|
||||||
|
|||||||
Reference in New Issue
Block a user