Refactor verb handling to Actor superclass
This commit is contained in:
+145
-107
@@ -1,3 +1,43 @@
|
|||||||
|
configureDefaults = (story)->
|
||||||
|
player = story.player
|
||||||
|
player.addVerb "drop", (sentence)-> player.drop(sentence)
|
||||||
|
player.addVerb "go", (sentence)-> player.go(sentence)
|
||||||
|
player.addVerb "inventory", (sentence)-> player.listInventory(sentence)
|
||||||
|
player.addVerb "take", (sentence)-> player.take(sentence)
|
||||||
|
|
||||||
|
story.addVerb "look", (sentence)-> story.look(sentence)
|
||||||
|
story.addVerb "restart", -> story.reset()
|
||||||
|
|
||||||
|
########################################################################################################################
|
||||||
|
|
||||||
|
class Actor
|
||||||
|
|
||||||
|
constructor: (story)->
|
||||||
|
@_verbs = {}
|
||||||
|
@story = story or this
|
||||||
|
|
||||||
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
|
addVerb: (verbs..., action)->
|
||||||
|
for verb in verbs
|
||||||
|
@_verbs[verb] = action
|
||||||
|
@story.parser.addVerb(verb)
|
||||||
|
|
||||||
|
can: (verb)->
|
||||||
|
return !! @_verbs[verb]
|
||||||
|
|
||||||
|
do: (sentence)->
|
||||||
|
if @can(sentence.verb)
|
||||||
|
@_verbs[sentence.verb](sentence)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
reset: ->
|
||||||
|
@_verbs = {}
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################################################################
|
||||||
|
|
||||||
class GameLog
|
class GameLog
|
||||||
|
|
||||||
constructor: (onChange=(->))->
|
constructor: (onChange=(->))->
|
||||||
@@ -30,6 +70,8 @@ class Inventory
|
|||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
Object.defineProperties @prototype,
|
||||||
|
items:
|
||||||
|
get: -> return (item for name, item of @_items)
|
||||||
length:
|
length:
|
||||||
get: -> return @_length
|
get: -> return @_length
|
||||||
|
|
||||||
@@ -74,14 +116,15 @@ class Inventory
|
|||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
class Item
|
class Item extends Actor
|
||||||
|
|
||||||
constructor: (@name, options={})->
|
constructor: (story, name, options={})->
|
||||||
options.fixed ?= false
|
options.fixed ?= false
|
||||||
|
super(story)
|
||||||
|
|
||||||
@description = "non-descript item"
|
@description = "non-descript item"
|
||||||
@fixed = options.fixed
|
@fixed = options.fixed
|
||||||
@story = null
|
@name = name
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
@@ -102,12 +145,15 @@ class Item
|
|||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
class Location
|
class Location extends Actor
|
||||||
|
|
||||||
|
constructor: (story, name)->
|
||||||
|
super(story)
|
||||||
|
|
||||||
constructor: (@name)->
|
|
||||||
@description = "Non-descript Place"
|
@description = "Non-descript Place"
|
||||||
@destinations = {}
|
@destinations = {}
|
||||||
@inventory = new Inventory()
|
@inventory = new Inventory()
|
||||||
|
@name = name
|
||||||
@transitions = []
|
@transitions = []
|
||||||
@visited = false
|
@visited = false
|
||||||
|
|
||||||
@@ -118,7 +164,6 @@ class Location
|
|||||||
|
|
||||||
addItem: (item)->
|
addItem: (item)->
|
||||||
@inventory.add(item)
|
@inventory.add(item)
|
||||||
return this
|
|
||||||
|
|
||||||
describe: (options={})->
|
describe: (options={})->
|
||||||
options.verbose ?= false
|
options.verbose ?= false
|
||||||
@@ -139,12 +184,14 @@ class Location
|
|||||||
for direction, transition of @transitions
|
for direction, transition of @transitions
|
||||||
if transition.toLocation is location
|
if transition.toLocation is location
|
||||||
return transition
|
return transition
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
removeItem: (item)->
|
removeItem: (item)->
|
||||||
@items.remove(item)
|
@inventory.remove(item)
|
||||||
return this
|
|
||||||
|
reset: ->
|
||||||
|
super()
|
||||||
|
@inventory.clear()
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
return @name
|
return @name
|
||||||
@@ -159,8 +206,9 @@ class ParseError extends Error
|
|||||||
|
|
||||||
class Parser
|
class Parser
|
||||||
|
|
||||||
constructor: (@story)->
|
constructor: (story)->
|
||||||
@restart()
|
@story = story
|
||||||
|
@reset()
|
||||||
|
|
||||||
addAliases: (aliasMap)->
|
addAliases: (aliasMap)->
|
||||||
for alias, meaning of aliasMap
|
for alias, meaning of aliasMap
|
||||||
@@ -193,7 +241,7 @@ class Parser
|
|||||||
@_validateSentence(sentence)
|
@_validateSentence(sentence)
|
||||||
return sentence
|
return sentence
|
||||||
|
|
||||||
restart: ->
|
reset: ->
|
||||||
@aliases = {}
|
@aliases = {}
|
||||||
@directions = []
|
@directions = []
|
||||||
@fillerWords = new Set()
|
@fillerWords = new Set()
|
||||||
@@ -266,9 +314,7 @@ class Parser
|
|||||||
throw new ParseError("You can't go #{rawWord} from here.")
|
throw new ParseError("You can't go #{rawWord} from here.")
|
||||||
|
|
||||||
for direction, transition of @story.currentLocation.transitions
|
for direction, transition of @story.currentLocation.transitions
|
||||||
if rawWord is direction
|
if transition.toLocation.name.indexOf(rawWord) isnt -1
|
||||||
candidates.add(transition.toLocation)
|
|
||||||
else if transition.toLocation.name.indexOf(rawWord) isnt -1
|
|
||||||
candidates.add(transition.toLocation)
|
candidates.add(transition.toLocation)
|
||||||
|
|
||||||
if @story.currentLocation.name.indexOf(rawWord) isnt -1
|
if @story.currentLocation.name.indexOf(rawWord) isnt -1
|
||||||
@@ -294,12 +340,13 @@ class Parser
|
|||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
class Player
|
class Player extends Actor
|
||||||
|
|
||||||
|
constructor: (story)->
|
||||||
|
super(story)
|
||||||
|
|
||||||
constructor: (@story)->
|
|
||||||
@inventory = new Inventory()
|
@inventory = new Inventory()
|
||||||
@onChange = (->)
|
@onChange = (->)
|
||||||
@restart()
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@@ -313,11 +360,8 @@ class Player
|
|||||||
|
|
||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
addVerb: (verb, onVerb)->
|
drop: (sentence)->
|
||||||
@verbs[verb] = onVerb
|
items = sentence.items
|
||||||
@story.parser.addVerb(verb)
|
|
||||||
|
|
||||||
drop: (items...)->
|
|
||||||
if items.length is 0
|
if items.length is 0
|
||||||
items = @inventory.all()
|
items = @inventory.all()
|
||||||
|
|
||||||
@@ -329,84 +373,56 @@ class Player
|
|||||||
else
|
else
|
||||||
@story.log.writeln("You're not holding a #{item.name}.")
|
@story.log.writeln("You're not holding a #{item.name}.")
|
||||||
|
|
||||||
enact: (sentence)->
|
go: (sentence)->
|
||||||
onVerb = @verbs[sentence.verb]
|
if not sentence.location
|
||||||
if onVerb
|
throw new ParseError("I'm not sure where you want to go...")
|
||||||
onVerb(sentence)
|
|
||||||
else
|
|
||||||
throw new ParseError("I'm not sure how to #{sentence.verb}, to be honest.")
|
|
||||||
|
|
||||||
move: (location)->
|
location = sentence.location
|
||||||
transition = @story.currentLocation.getTransitionTo(location)
|
transition = @story.currentLocation.getTransitionTo(location)
|
||||||
if not transition
|
if not transition
|
||||||
throw new ParseError("You can't get to #{location.name} from here.")
|
throw new ParseError("You can't get to #{location.name} from here.")
|
||||||
else if transition.locked
|
|
||||||
throw new ParseError(transition.lockDescription)
|
|
||||||
|
|
||||||
@story.arrive(location)
|
@story.arrive(location)
|
||||||
|
|
||||||
restart: ->
|
listInventory: (sentence)->
|
||||||
@verbs = {}
|
|
||||||
@inventory.clear()
|
|
||||||
@score = 0
|
|
||||||
@_configureDefaultVerbs()
|
|
||||||
|
|
||||||
take: (item)->
|
|
||||||
localItems = @story.currentLocation.inventory
|
|
||||||
if not item
|
|
||||||
foundTakeableItem = false
|
|
||||||
localItems.eachItem (item)=>
|
|
||||||
if not item.fixed
|
|
||||||
@story.log.write("#{item}: ")
|
|
||||||
foundTakeableItem = true
|
|
||||||
@take(item)
|
|
||||||
|
|
||||||
if not foundTakeableItem then @story.log.writeln("There's nothing here you can take.")
|
|
||||||
else if localItems.has(item)
|
|
||||||
if item.take()
|
|
||||||
localItems.remove(item)
|
|
||||||
@inventory.add(item)
|
|
||||||
else
|
|
||||||
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
|
if @inventory.length is 0
|
||||||
@story.log.writeln("You're not carrying anything.")
|
@story.log.writeln("You're not carrying anything.")
|
||||||
else
|
else
|
||||||
@story.log.writeln("You have:")
|
@story.log.writeln("You have:")
|
||||||
@story.log.writeln(@inventory.describe(simple: true))
|
@story.log.writeln(@inventory.describe(simple: true))
|
||||||
|
|
||||||
@addVerb "look", (sentence)=>
|
take: (sentence)->
|
||||||
if sentence.has(item: 1)
|
items = sentence.items
|
||||||
@story.log.writeln(sentence.item.describe(verbose: true))
|
localItems = @story.currentLocation.inventory
|
||||||
else
|
|
||||||
@story.log.writeln(@story.currentLocation.describe(verbose: true))
|
|
||||||
|
|
||||||
@addVerb "restart", =>
|
if items.length is 0
|
||||||
@story.restart()
|
foundTakeableItem = true
|
||||||
|
for item in localItems.items
|
||||||
|
if not item.fixed
|
||||||
|
items.push(item)
|
||||||
|
|
||||||
@addVerb "take", (sentence)=>
|
if not foundTakeableItem
|
||||||
if sentence.has(item: 0)
|
@story.log.writeln("There's nothing here you can take.")
|
||||||
@take()
|
|
||||||
|
_doTake = (item)=>
|
||||||
|
if localItems.has(item)
|
||||||
|
if item.take()
|
||||||
|
localItems.remove(item)
|
||||||
|
@inventory.add(item)
|
||||||
else
|
else
|
||||||
for item in sentence.items
|
throw new ParseError("There isn't a #{item} here.")
|
||||||
@take(item)
|
|
||||||
|
if items.length > 1
|
||||||
|
for item in items
|
||||||
|
@story.log.write("#{item}: ")
|
||||||
|
_doTake(item)
|
||||||
|
else
|
||||||
|
_doTake(items[0])
|
||||||
|
|
||||||
|
reset: ->
|
||||||
|
super()
|
||||||
|
@inventory.clear()
|
||||||
|
@score = 0
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -416,6 +432,20 @@ class Sentence
|
|||||||
constructor: ->
|
constructor: ->
|
||||||
@tokens = item: [], location: [], verb: []
|
@tokens = item: [], location: [], verb: []
|
||||||
|
|
||||||
|
# Properties ###################################################################################
|
||||||
|
|
||||||
|
Object.defineProperties @prototype,
|
||||||
|
"item":
|
||||||
|
get: -> return @tokens.item[0].referant
|
||||||
|
"items":
|
||||||
|
get: -> (i.referant for i in @tokens.item)
|
||||||
|
"location":
|
||||||
|
get: -> return @tokens.location[0].referant
|
||||||
|
"verb":
|
||||||
|
get: -> return @tokens.verb[0].rawText
|
||||||
|
|
||||||
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
addWord: (wordToken)->
|
addWord: (wordToken)->
|
||||||
@tokens[wordToken.type].push(wordToken)
|
@tokens[wordToken.type].push(wordToken)
|
||||||
|
|
||||||
@@ -437,27 +467,20 @@ class Sentence
|
|||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
"item":
|
|
||||||
get: -> return @tokens.item[0].referant
|
|
||||||
"items":
|
|
||||||
get: -> (i.referant for i in @tokens.item)
|
|
||||||
"location":
|
|
||||||
get: -> return @tokens.location[0].referant
|
|
||||||
"verb":
|
|
||||||
get: -> return @tokens.verb[0].rawText
|
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
class Story
|
class Story extends Actor
|
||||||
|
|
||||||
|
constructor: (title, onRestart=(->))->
|
||||||
|
super()
|
||||||
|
|
||||||
constructor: (@title, @onRestart=(->))->
|
|
||||||
@log = new GameLog()
|
@log = new GameLog()
|
||||||
|
@onChange = (->)
|
||||||
|
@onRestart = onRestart
|
||||||
@parser = new Parser(this)
|
@parser = new Parser(this)
|
||||||
@player = new Player(this)
|
@player = new Player(this)
|
||||||
|
@title = title
|
||||||
@onChange = (->)
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@@ -472,13 +495,13 @@ class Story
|
|||||||
# Configuration Methods ###########3############################################################
|
# Configuration Methods ###########3############################################################
|
||||||
|
|
||||||
addItem: (name, options={})->
|
addItem: (name, options={})->
|
||||||
item = new Item(name, options)
|
item = new Item(this, name, options)
|
||||||
item.story = this
|
item.story = this
|
||||||
@items.push(item)
|
@items.push(item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
addLocation: (name)->
|
addLocation: (name)->
|
||||||
location = new Location(name)
|
location = new Location(this, name)
|
||||||
@locations.push(location)
|
@locations.push(location)
|
||||||
if @initialLocation is null
|
if @initialLocation is null
|
||||||
@initialLocation = location
|
@initialLocation = location
|
||||||
@@ -498,24 +521,37 @@ class Story
|
|||||||
@log.echoInput(userInput)
|
@log.echoInput(userInput)
|
||||||
@turns += 1
|
@turns += 1
|
||||||
sentence = @parser.interpret(userInput)
|
sentence = @parser.interpret(userInput)
|
||||||
@player.enact(sentence)
|
|
||||||
|
if @do(sentence) then return
|
||||||
|
if @player.do(sentence) then return
|
||||||
|
if @currentLocation.do(sentence) then return
|
||||||
|
for item in @currentLocation.inventory.items
|
||||||
|
if item.do(sentence) then return
|
||||||
|
|
||||||
|
throw new ParseError("To be honest, I'm not sure how to #{sentence.verb} anything around here.")
|
||||||
catch e
|
catch e
|
||||||
if e instanceof ParseError
|
if e instanceof ParseError
|
||||||
@log.writeln(e.message)
|
@log.writeln(e.message)
|
||||||
else
|
else
|
||||||
throw e
|
throw e
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
look: (sentence)->
|
||||||
|
if sentence.has(item: 1)
|
||||||
|
@story.log.writeln(sentence.item.describe(verbose: true))
|
||||||
|
else
|
||||||
|
@story.log.writeln(@story.currentLocation.describe(verbose: true))
|
||||||
|
|
||||||
|
reset: ->
|
||||||
|
super()
|
||||||
|
|
||||||
restart: ->
|
|
||||||
@currentLocation = null
|
@currentLocation = null
|
||||||
@items = []
|
@items = []
|
||||||
@locations = []
|
@locations = []
|
||||||
@turns = 0
|
@turns = 0
|
||||||
|
|
||||||
@log.clear()
|
@log.clear()
|
||||||
@parser.restart()
|
@parser.reset()
|
||||||
@player.restart()
|
@player.reset()
|
||||||
|
|
||||||
@parser.addDirections(
|
@parser.addDirections(
|
||||||
"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down"
|
"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest", "up", "down"
|
||||||
@@ -528,7 +564,9 @@ class Story
|
|||||||
"nw": "northwest", "s": "south", "se": "southeast", "sw": "southwest", "u": "up", "w": "west",
|
"nw": "northwest", "s": "south", "se": "southeast", "sw": "southwest", "u": "up", "w": "west",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
configureDefaults(this)
|
||||||
@onRestart()
|
@onRestart()
|
||||||
|
|
||||||
@log.writeln(@title)
|
@log.writeln(@title)
|
||||||
@log.writeln()
|
@log.writeln()
|
||||||
@arrive(@currentLocation)
|
@arrive(@currentLocation)
|
||||||
@@ -538,7 +576,7 @@ class Story
|
|||||||
|
|
||||||
class Transition
|
class Transition
|
||||||
|
|
||||||
constructor: (@direction, @toLocation, @locked=false)->
|
constructor: (@direction, @toLocation)->
|
||||||
# do nothing
|
# do nothing
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" type="text/css" href="styles.css"/>
|
<link rel="stylesheet" type="text/css" href="styles.css"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=false"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+1
-1
@@ -33,5 +33,5 @@ $(document).ready ->
|
|||||||
STORY.onChange(STORY)
|
STORY.onChange(STORY)
|
||||||
|
|
||||||
# start the story
|
# start the story
|
||||||
STORY.restart()
|
STORY.reset()
|
||||||
$input.focus()
|
$input.focus()
|
||||||
|
|||||||
+10
-7
@@ -1,6 +1,6 @@
|
|||||||
window.STORY = new Story "A Walk Through My House", ->
|
window.STORY = new Story "A Walk Through My House", ->
|
||||||
|
|
||||||
# Items ################################################################################################################
|
# Items ############################################################################################################
|
||||||
|
|
||||||
boxOfTile = @addItem "box of tiles"
|
boxOfTile = @addItem "box of tiles"
|
||||||
boxOfTile.description =
|
boxOfTile.description =
|
||||||
@@ -10,14 +10,17 @@ window.STORY = new Story "A Walk Through My House", ->
|
|||||||
@log.writeln "Oof! It's too heavy."
|
@log.writeln "Oof! It's too heavy."
|
||||||
return false
|
return false
|
||||||
|
|
||||||
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 = @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 ########################################################################################################
|
||||||
|
|
||||||
|
car = @addLocation "Your Car"
|
||||||
|
car.description =
|
||||||
|
"Your car isn't anything special, but it does get you around."
|
||||||
|
car.addVerb "leave", "drive", "start", =>
|
||||||
|
@log.writeln("Having finished your visit, you drive back home again.")
|
||||||
|
@endGame()
|
||||||
|
|
||||||
driveway = @addLocation "Driveway"
|
driveway = @addLocation "Driveway"
|
||||||
driveway.description =
|
driveway.description =
|
||||||
@@ -35,7 +38,7 @@ window.STORY = new Story "A Walk Through My House", ->
|
|||||||
can't make 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
|
||||||
|
|||||||
Reference in New Issue
Block a user