From 2b0f714ac00e03079badc02cb2b453e34849b52f Mon Sep 17 00:00:00 2001 From: Alexander Yakovlev Date: Wed, 10 Feb 2016 20:35:44 +0700 Subject: [PATCH] Proper variables and putting things --- game/begin.coffee | 8 +- game/story.coffee | 6 +- lib/character.coffee | 21 ++ lib/obj.coffee | 2 + lib/room.coffee | 5 +- lib/salet.coffee | 711 +++++++++++++++++++++---------------------- lib/view.coffee | 1 + 7 files changed, 388 insertions(+), 366 deletions(-) create mode 100644 lib/character.coffee diff --git a/game/begin.coffee b/game/begin.coffee index 6e60d9e..ddc2a91 100644 --- a/game/begin.coffee +++ b/game/begin.coffee @@ -4,10 +4,10 @@ dialogue = require('../../lib/dialogue.coffee') oneOf = require('../../lib/oneOf.coffee') Salet = require('../../lib/salet.coffee') -salet = new Salet -salet.view.init(salet) -salet.game_id = "your-game-id-here" -salet.game_version = "1.0" +salet = Salet({ + game_id: "your-game-id-here" + game_version: "1.0" +}) $(document).ready(() -> salet.beginGame() ) diff --git a/game/story.coffee b/game/story.coffee index 8acaa61..61744fc 100644 --- a/game/story.coffee +++ b/game/story.coffee @@ -86,10 +86,10 @@ room "shop-inside", salet, The insides are painted pastel white, honouring The Great Milk Spill of 1985. """ objects: [ - merchant: obj "merchant", + obj "merchant", dsc: "A {{merchant}} eyes you warily." takeable: false - act: (system) => + act: (salet) => salet.processClick("merchdialogue") return "" ] @@ -100,7 +100,7 @@ The chain of calls is very weird and this puts an object IN EVERY ROOM for God's I need someone smarter than me to fix this. ### -lamp = obj "lamp", salet, +lamp = obj "lamp", takeable: true lamp.put(salet, "shop-inside") diff --git a/lib/character.coffee b/lib/character.coffee new file mode 100644 index 0000000..7eeb145 --- /dev/null +++ b/lib/character.coffee @@ -0,0 +1,21 @@ +class Character + constructor: (spec) -> + @inventory = [] + + @take = (thing) => + @inventory.push thing + @drop = (thing) => + for i in @inventory + if i.name == thing + index = @objects.indexOf(thing) + @inventory.splice(index, 1) + + for index, value of spec + this[index] = value + return this + +character = (spec) -> + spec ?= {} + return( new Character(spec) ) + +module.exports = character diff --git a/lib/obj.coffee b/lib/obj.coffee index 872da0a..99f946b 100644 --- a/lib/obj.coffee +++ b/lib/obj.coffee @@ -40,6 +40,8 @@ class SaletObj if salet.rooms[location]? @location = location salet.rooms[location].take(this) + else + console.log("Could not find location #{location} for an object #{@name}") @delete = (salet, location = false) => if location == false location = @location diff --git a/lib/room.coffee b/lib/room.coffee index 86d3ad1..072b0d2 100644 --- a/lib/room.coffee +++ b/lib/room.coffee @@ -144,7 +144,7 @@ class SaletRoom Puts an object in this room. ### @take = (thing) => - @objects[thing.name] = thing + @objects.push(thing) @drop = (name) => for thing in @objects @@ -164,7 +164,8 @@ class SaletRoom # If it's takeable, the player can take this object. # If not, we check the "act" function. if thing.takeable - system.character.inventory.push thing + console.log system + system.character.take(thing) @drop name system.view.clearContent() @entering.fcall(this, system, @name) diff --git a/lib/salet.coffee b/lib/salet.coffee index b405533..ec7c763 100644 --- a/lib/salet.coffee +++ b/lib/salet.coffee @@ -1,6 +1,7 @@ markdown = require('./markdown.coffee') SaletView = require('./view.coffee') Random = require('./random.js') +character = require('./character.coffee') languages = require('./localize.coffee') ### @@ -17,9 +18,6 @@ String.prototype.fcall = () -> assert = (msg, assertion) -> console.assert assertion, msg -class Character - inventory: [] - ### This is the control structure, it has minimal amount of data and this data is volatile anyway (as in, it won't get saved). @@ -27,421 +25,420 @@ this data is volatile anyway (as in, it won't get saved). There is only one instance of this class. ### class Salet - # REDEFINE THIS IN YOUR GAME - game_id: null - game_version: "1.0" - autosave: true + constructor: (spec) -> + @character = character() + + # REDEFINE THIS IN YOUR GAME + @game_id = null + @game_version = "1.0" + @autosave = true + + @rnd = null + @time = 0 - rnd: null - time: 0 + # Corresponding room names to room objects. + @rooms = {} - # Corresponding room names to room objects. - rooms: {} + # The unique id of the starting room. + @start = "start" - # The unique id of the starting room. - start: "start" + # Regular expression to catch every link action. + # Salet's default is a general URL-safe expression. + @linkRe = /^([0-9A-Za-z_-]+|\.)(\/([0-9A-Za-z_-]+))?$/ - # Regular expression to catch every link action. - # Salet's default is a general URL-safe expression. - linkRe: /^([0-9A-Za-z_-]+|\.)(\/([0-9A-Za-z_-]+))?$/ + ### + This function is called at the start of the game. It is + normally overridden to provide initial character creation + (setting initial quality values, setting the + character-text. This is optional, however, as set-up + processing could also be done by the first situation's + enter function. + ### + @init = () -> - character: new Character + ### + This function is called before entering any new + situation. It is called before the corresponding situation + has its `enter` method called. + ### + @enter = (oldSituationId, newSituationId) -> - ### - This function is called at the start of the game. It is - normally overridden to provide initial character creation - (setting initial quality values, setting the - character-text. This is optional, however, as set-up - processing could also be done by the first situation's - enter function. - ### - init: () -> + ### + Hook for when the situation has already been carried out + and printed. + ### + @afterEnter = (oldSituationId, newSituationId) -> - ### - This function is called before entering any new - situation. It is called before the corresponding situation - has its `enter` method called. - ### - enter: (oldSituationId, newSituationId) -> + ### + This function is called before carrying out any action in + any situation. It is called before the corresponding + situation has its `act` method called. - ### - Hook for when the situation has already been carried out - and printed. - ### - afterEnter: (oldSituationId, newSituationId) -> + If the function returns true, then it is indicating that it + has consumed the action, and the action will not be passed + on to the situation. Note that this is the only one of + these global handlers that can consume the event. + ### + @beforeAction = (situationId, actionId) -> - ### - This function is called before carrying out any action in - any situation. It is called before the corresponding - situation has its `act` method called. + ### + This function is called after carrying out any action in + any situation. It is called after the corresponding + situation has its `act` method called. + ### + @afterAction = (situationId, actionId) -> - If the function returns true, then it is indicating that it - has consumed the action, and the action will not be passed - on to the situation. Note that this is the only one of - these global handlers that can consume the event. - ### - beforeAction: (situationId, actionId) -> + ### + This function is called after leaving any situation. It is + called after the corresponding situation has its `exit` + method called. + ### + @exit = (oldSituationId, newSituationId) -> - ### - This function is called after carrying out any action in - any situation. It is called after the corresponding - situation has its `act` method called. - ### - afterAction: (situationId, actionId) -> + ### + Returns a list of situation ids to choose from, given a set of + specifications. - ### - This function is called after leaving any situation. It is - called after the corresponding situation has its `exit` - method called. - ### - exit: (oldSituationId, newSituationId) -> + This function is a complex and powerful way of compiling + implicit situation choices. You give it a list of situation ids + and situation tags (if a single id or tag is needed just that + string can be given, it doesn't need to be wrapped in a + list). Tags should be prefixed with a hash # to differentiate + them from situation ids. The function then considers all + matching situations in descending priority order, calling their + canView functions and filtering out any that should not be + shown, given the current state. Without additional parameters + the function returns a list of the situation ids at the highest + level of priority that has any valid results. So, for example, + if a tag #places matches three situations, one with priority 2, + and two with priority 3, and all of them can be viewed in the + current context, then only the two with priority 3 will be + returned. This allows you to have high-priority situations that + trump any lower situations when they are valid, such as + situations that force the player to go to one destination if + the player is out of money, for example. - ### - Returns a list of situation ids to choose from, given a set of - specifications. + If a maxChoices value is given, then the function will not + return any more than the given number of results. If there are + more than this number of results possible, then the highest + priority resuls will be guaranteed to be returned, but the + lowest priority group will have to fight it out for the + remaining places. - This function is a complex and powerful way of compiling - implicit situation choices. You give it a list of situation ids - and situation tags (if a single id or tag is needed just that - string can be given, it doesn't need to be wrapped in a - list). Tags should be prefixed with a hash # to differentiate - them from situation ids. The function then considers all - matching situations in descending priority order, calling their - canView functions and filtering out any that should not be - shown, given the current state. Without additional parameters - the function returns a list of the situation ids at the highest - level of priority that has any valid results. So, for example, - if a tag #places matches three situations, one with priority 2, - and two with priority 3, and all of them can be viewed in the - current context, then only the two with priority 3 will be - returned. This allows you to have high-priority situations that - trump any lower situations when they are valid, such as - situations that force the player to go to one destination if - the player is out of money, for example. + Before this function returns its result, it sorts the + situations in increasing order of their displayOrder values. + ### + @getSituationIdChoices = (listOfOrOneIdsOrTags, maxChoices) => + datum = null + i = 0 - If a maxChoices value is given, then the function will not - return any more than the given number of results. If there are - more than this number of results possible, then the highest - priority resuls will be guaranteed to be returned, but the - lowest priority group will have to fight it out for the - remaining places. In this case, a random sample is chosen, - taking into account the frequency of each situation. So a - situation with a frequency of 100 will be chosen 100 times more - often than a situation with a frequency of 1, if there is one - space available. Often these frequencies have to be taken as a - guideline, and the actual probabilities will only be - approximate. Consider three situations with frequencies of 1, - 1, 100, competing for two spaces. The 100-frequency situation - will be chosen almost every time, but for the other space, one - of the 1-frequency situations must be chosen. So the actual - probabilities will be roughly 50%, 50%, 100%. When selecting - more than one result, frequencies can only be a guide. + # First check if we have a single string for the id or tag. + if (typeof(listOfOrOneIdsOrTags) == 'string') + listOfOrOneIdsOrTags = [listOfOrOneIdsOrTags] - Before this function returns its result, it sorts the - situations in increasing order of their displayOrder values. - ### - getSituationIdChoices: (listOfOrOneIdsOrTags, maxChoices) => - datum = null - i = 0 + # First we build a list of all candidate ids. + allIds = [] + for tagOrId in listOfOrOneIdsOrTags + if (tagOrId.substr(0, 1) == '#') + ids = @getRoomsTagged(tagOrId.substr(1)) + for id in ids + allIds.push(id) + else #it's an id, not a tag + allIds.push(tagOrId) - # First check if we have a single string for the id or tag. - if (typeof(listOfOrOneIdsOrTags) == 'string') - listOfOrOneIdsOrTags = [listOfOrOneIdsOrTags] + #Filter out anything that can't be viewed right now. + currentRoom = @getCurrentRoom() + viewableRoomData = [] + for roomId in allIds + room = @rooms[roomId] + assert(room, "unknown_situation".l({id:roomId})) - # First we build a list of all candidate ids. - allIds = [] - for tagOrId in listOfOrOneIdsOrTags - if (tagOrId.substr(0, 1) == '#') - ids = @getRoomsTagged(tagOrId.substr(1)) - for id in ids - allIds.push(id) - else #it's an id, not a tag - allIds.push(tagOrId) + if (room.canView.fcall(this, currentRoom)) + viewableRoomData.push({ + priority: room.priority + id: roomId + displayOrder: room.displayOrder + }) - #Filter out anything that can't be viewed right now. - currentRoom = @getCurrentRoom() - viewableRoomData = [] - for roomId in allIds - room = @rooms[roomId] - assert(room, "unknown_situation".l({id:roomId})) + # Then we sort in descending priority order. + viewableRoomData.sort((a, b) -> + return b.priority - a.priority + ) - if (room.canView.fcall(this, currentRoom)) - viewableRoomData.push({ - priority: room.priority - id: roomId - displayOrder: room.displayOrder + committed = [] + + # if we need to filter out the results + if (maxChoices? && viewableRoomData.length > maxChoices) + viewableRoomData = viewableRoomData[-maxChoices..] + + for candidateRoom in viewableRoomData + committed.push({ + id: candidateRoom.id + displayOrder: candidateRoom.displayOrder }) - # Then we sort in descending priority order. - viewableRoomData.sort((a, b) -> - return b.priority - a.priority - ) + # Now sort in ascending display order. + committed.sort((a, b) -> + return a.displayOrder - b.displayOrder + ) - committed = [] + # And return as a list of ids only. + result = [] + for i in committed + result.push(i.id) - # if we need to filter out the results - if (maxChoices? && viewableRoomData.length > maxChoices) - viewableRoomData = viewableRoomData[-maxChoices..] + return result - for candidateRoom in viewableRoomData - committed.push({ - id: candidateRoom.id - displayOrder: candidateRoom.displayOrder - }) + # This is the data on the player's progress that gets saved. + @progress = { + # A random seed string, used internally to make random + # sequences predictable. + seed: null + # Keeps track of the links clicked, and when. + sequence: [], + # The time when the progress was saved. + saveTime: null + } - # Now sort in ascending display order. - committed.sort((a, b) -> - return a.displayOrder - b.displayOrder - ) + # The Id of the current room the player is in. + @current = null - # And return as a list of ids only. - result = [] - for i in committed - result.push(i.id) + # Tracks whether we're in interactive mode or batch mode. + @interactive = true - return result + # The system time when the game was initialized. + @startTime = null - # This is the data on the player's progress that gets saved. - progress: { - # A random seed string, used internally to make random - # sequences predictable. - seed: null - # Keeps track of the links clicked, and when. - sequence: [], - # The time when the progress was saved. - saveTime: null - } + # The stack of links, resulting from the last action, still be to resolved. + @linkStack = null - # The Id of the current situation the player is in. - current: null; + @getCurrentRoom = () => + if (@current) + return @rooms[@current] + return null - # Tracks whether we're in interactive mode or batch mode. - interactive: true + # Gets the unique id used to identify saved games. + @getSaveId = (slot = "") => + return 'salet_'+@game_id+'_'+@game_version#+'_'+slot - # The system time when the game was initialized. - startTime: null + # This gets called when a link needs to be followed, regardless + # of whether it was user action that initiated it. + @processLink = (code) => + # Check if we should do this now, or if processing is already underway. + if @linkStack != null + @linkStack.push(code) + return - # The stack of links, resulting from the last action, still be to resolved. - linkStack: null + @view.mark_all_links_old + + # We're processing, so make the stack available. + @linkStack = [] - getCurrentRoom: () => - if (@current) - return @rooms[@current] - return null + # Handle each link in turn. + @processOneLink(code); + while (@linkStack.length > 0) + code = @linkStack.shift() + @processOneLink(code) - # Gets the unique id used to identify saved games. - getSaveId: (slot = "") => - return 'salet_'+@game_id+'_'+@game_version#+'_'+slot + # We're done, so remove the stack to prevent future pushes. + @linkStack = null; - # This gets called when a link needs to be followed, regardless - # of whether it was user action that initiated it. - processLink: (code) => - # Check if we should do this now, or if processing is already underway. - if @linkStack != null - @linkStack.push(code) - return + # Scroll to the top of the new content. + @view.endOutputTransaction() - @view.mark_all_links_old + # We're able to save, if we weren't already. + @view.enableSaving() - # We're processing, so make the stack available. - @linkStack = [] + @goTo = (roomId) => + return @processLink(roomId) - # Handle each link in turn. - @processOneLink(code); - while (@linkStack.length > 0) - code = @linkStack.shift() - @processOneLink(code) + ### + This gets called to actually do the work of processing a code. + When one doLink is called (or a link is clicked), this may set call + code that further calls doLink, and so on. This method processes + each one, and processLink manages this. + ### + @processOneLink = (code) => + match = code.match(@linkRe) + assert(match, "link_not_valid".l({link:code})) - # We're done, so remove the stack to prevent future pushes. - @linkStack = null; + situation = match[1] + action = match[3] - # Scroll to the top of the new content. - @view.endOutputTransaction() + # Change the situation + if situation != '.' and situation != @current_room + @doTransitionTo(situation) + else + # We should have an action if we have no situation change. + assert(action, "link_no_action".l()) - # We're able to save, if we weren't already. - @view.enableSaving() + # Carry out the action + if (action) + room = @getCurrentRoom() + if (room and @beforeAction) + # Try the global act handler + consumed = @beforeAction(room, action) + if (consumed != true) + room.act(this, action) - goTo: (roomId) => - return @processLink(roomId) + if (@afterAction) + @afterAction(this, room, action) - ### - This gets called to actually do the work of processing a code. - When one doLink is called (or a link is clicked), this may set call - code that further calls doLink, and so on. This method processes - each one, and processLink manages this. - ### - processOneLink: (code) => - match = code.match(@linkRe) - assert(match, "link_not_valid".l({link:code})) + # This gets called when the user clicks a link to carry out an action. + @processClick = (code) => + now = (new Date()).getTime() * 0.001 + @time = now - @startTime + @progress.sequence.push({link:code, when:@time}) + @processLink(code) - situation = match[1] - action = match[3] + # Transition between rooms. + @doTransitionTo = (newRoomId) => + oldRoomId = @current + oldRoom = @getCurrentRoom() + newRoom = @rooms[newRoomId] - # Change the situation - if situation != '.' and situation != @current_room - @doTransitionTo(situation) - else - # We should have an action if we have no situation change. - assert(action, "link_no_action".l()) + assert(newRoom, "unknown_situation".l({id:newRoomId})) + + # We might not have an old situation if this is the start of the game. + if (oldRoom and @exit) + @exit(oldRoomId, newRoomId) - # Carry out the action - if (action) - room = @getCurrentRoom() - if (room and @beforeAction) - # Try the global act handler - consumed = @beforeAction(room, action) - if (consumed != true) - room.act(this, action) + @current = newRoomId + + # Remove links and transient sections. + @view.removeTransient(@interactive) - if (@afterAction) - @afterAction(this, room, action) + # Notify the incoming situation. + if (@enter) + @enter(oldRoomId, newRoomId) + newRoom.entering(this, oldRoomId) - # This gets called when the user clicks a link to carry out an action. - processClick: (code) => - now = (new Date()).getTime() * 0.001 - @time = now - @startTime - @progress.sequence.push({link:code, when:@time}) - @processLink(code) + # additional hook for when the situation text has already been printed + if (@afterEnter) + @afterEnter(oldRoomId, newRoomId) - # Transitions between situations. - doTransitionTo: (newRoomId) => - oldRoomId = @current - oldRoom = @getCurrentRoom() - newRoom = @rooms[newRoomId] + ### + Erases the character in local storage. This is permanent! + To restart the game afterwards, we perform a simple page refresh. + This guarantees authors don't have to care about "tainting" the + game state across save/erase cycles, meaning that character.sandbox + no longer has to be the end-all be-all repository of game state. + ### + @eraseSave = (force = false) => + saveId = @getSaveId() # save slot + if (localStorage.getItem(saveId) and (force or confirm("erase_message".l()))) + localStorage.removeItem(saveId) + window.location.reload() - assert(newRoom, "unknown_situation".l({id:newRoomId})) + # Find and return a list of ids for all situations with the given tag. + @getRoomsTagged = (tag) => + result = [] + for id, room of @rooms + for i in room.tags + if (i == tag) + result.push(id) + break + return result - # We might not have an old situation if this is the start of the game. - if (oldRoom and @exit) - @exit(oldRoomId, newRoomId) + # Saves the character and the walking history to local storage. + @saveGame = () => + # Store when we're saving the game, to avoid exploits where a + # player loads their file to gain extra time. + now = (new Date()).getTime() * 0.001 + @progress.saveTime = now - @startTime - @current = newRoomId + # Save the game. + window.localStorage.setItem(@getSaveId(), JSON.stringify({ + progress: @progress, + character: @character + })) - # Remove links and transient sections. - @view.removeTransient(@interactive) + # Switch the button highlights. + @view.disableSaving() + @view.enableErasing() + @view.enableLoading() - # Notify the incoming situation. - if (@enter) - @enter(oldRoomId, newRoomId) - newRoom.entering(this, oldRoomId) + # Loads the game from the given data + @loadGame = (saveFile) => + @progress = saveFile.progress + @character = saveFile.character - # additional hook for when the situation text has already been printed - if (@afterEnter) - @afterEnter(oldRoomId, newRoomId) - - ### - Erases the character in local storage. This is permanent! - To restart the game afterwards, we perform a simple page refresh. - This guarantees authors don't have to care about "tainting" the - game state across save/erase cycles, meaning that character.sandbox - no longer has to be the end-all be-all repository of game state. - ### - eraseSave: (force = false) => - saveId = @getSaveId() # save slot - if (localStorage.getItem(saveId) and (force or confirm("erase_message".l()))) - localStorage.removeItem(saveId) - window.location.reload() - - # Find and return a list of ids for all situations with the given tag. - getRoomsTagged: (tag) => - result = [] - for id, room of @rooms - for i in room.tags - if (i == tag) - result.push(id) - break - return result - - # Saves the character and the walking history to local storage. - saveGame: () => - # Store when we're saving the game, to avoid exploits where a - # player loads their file to gain extra time. - now = (new Date()).getTime() * 0.001 - @progress.saveTime = now - @startTime - - # Save the game. - window.localStorage.setItem(@getSaveId(), JSON.stringify({ - progress: @progress, - character: @character - })) - - # Switch the button highlights. - @view.disableSaving() - @view.enableErasing() - @view.enableLoading() - - # Loads the game from the given data - loadGame: (saveFile) => - @progress = saveFile.progress - @character = saveFile.character - - @rnd = new Random(@progress.seed) - - # Don't load the save if it's an autosave at the first room (start). - # We don't need to clear the screen. - if saveFile.progress? and saveFile.progress.sequence.length > 1 - @view.clearContent() - - # Now play through the actions so far: - if (@init) - @init() - - # Run through all the player's history. - interactive = false - for step in @progress.sequence - # The action must be done at the recorded time. - @time = step.when - @processLink(step.link) - interactive = true - - # Reverse engineer the start time. - now = new Date().getTime() * 0.001 - startTime = now - @progress.saveTime - - view: new SaletView - - beginGame: () => - @view.fixClicks() - - # Handle storage. - saveFile = false - if (@view.hasLocalStorage()) - saveFile = localStorage.getItem(@getSaveId()) - - if (saveFile) - try - @loadGame(JSON.parse(saveFile)) - @view.disableSaving() - @view.enableErasing() - catch err - console.log "There was an error loading your save. The save is deleted." - console.error err - @eraseSave(true) - else - @progress.seed = new Date().toString() - - character = new Character() @rnd = new Random(@progress.seed) - @progress.sequence = [{link:@start, when:0}] - # Start the game - @startTime = new Date().getTime() * 0.001 + # Don't load the save if it's an autosave at the first room (start). + # We don't need to clear the screen. + if saveFile.progress? and saveFile.progress.sequence.length > 1 + @view.clearContent() + + # Now play through the actions so far: if (@init) - @init(character) + @init() - # Do the first state. - @doTransitionTo(@start) + # Run through all the player's history. + interactive = false + for step in @progress.sequence + # The action must be done at the recorded time. + @time = step.when + @processLink(step.link) + interactive = true - getRoom: (name) => - return @rooms[name] + # Reverse engineer the start time. + now = new Date().getTime() * 0.001 + startTime = now - @progress.saveTime - # Just an alias for getCurrentRoom - here: () => @getCurrentRoom() + @view = new SaletView - isVisited: (name) => - place = @getRoom(name) - if place - return Boolean place.visited - return 0 + @beginGame = () => + @view.fixClicks() -module.exports = Salet + # Handle storage. + saveFile = false + if (@view.hasLocalStorage()) + saveFile = localStorage.getItem(@getSaveId()) + + if (saveFile) + try + @loadGame(JSON.parse(saveFile)) + @view.disableSaving() + @view.enableErasing() + catch err + console.log "There was an error loading your save. The save is deleted." + console.error err + @eraseSave(true) + else + @progress.seed = new Date().toString() + + character = new Character() + @rnd = new Random(@progress.seed) + @progress.sequence = [{link:@start, when:0}] + + # Start the game + @startTime = new Date().getTime() * 0.001 + if (@init) + @init(character) + + # Do the first state. + @doTransitionTo(@start) + + @getRoom = (name) => @rooms[name] + + # Just an alias for getCurrentRoom + @here = () => @getCurrentRoom() + + @isVisited = (name) => + place = @getRoom(name) + if place + return Boolean place.visited + return 0 + + for index, value of spec + this[index] = value + return this + +salet = (spec) -> + spec ?= {} + retval = new Salet(spec) + retval.view.init(retval) + return retval + +module.exports = salet diff --git a/lib/view.coffee b/lib/view.coffee index b24dcf2..e3db114 100644 --- a/lib/view.coffee +++ b/lib/view.coffee @@ -271,6 +271,7 @@ class SaletView key: way distance: salet.rooms[way].distance }) + document.querySelector(".ways h2").style.display = "block" else document.querySelector(".ways h2").style.display = "none" document.getElementById("ways").innerHTML = content