From 0ed470dae6547b65fb67ef9dc568ca907fa02eef Mon Sep 17 00:00:00 2001 From: Alexander Yakovlev Date: Tue, 19 Jan 2016 10:03:01 +0700 Subject: [PATCH] Cycling rewrite, Raconteur's purge - Rewrote cycling shortcuts into the library. It's more object-oriented now (and slightly less obvious, but we can get around that). - Removed Raconteur situations entirely. Their functionality is 100% integrated now. --- game/begin.coffee | 14 +------ game/story.coffee | 13 ++----- lib/cycle.coffee | 19 ++++++++++ lib/room.coffee | 79 +++++++++++++++++++++++++++++++--------- lib/situation.coffee | 87 -------------------------------------------- 5 files changed, 85 insertions(+), 127 deletions(-) create mode 100644 lib/cycle.coffee delete mode 100644 lib/situation.coffee diff --git a/game/begin.coffee b/game/begin.coffee index 18354db..f39dda7 100644 --- a/game/begin.coffee +++ b/game/begin.coffee @@ -22,21 +22,11 @@ actlink = (content, ref) -> return "#{content}" textcycle = (content, ref) -> return "#{content}" + +# TODO: hide this somewhere in exports or autoreplacer shortcuts cyclelink = (content) -> return "#{content}" -# Cycling link. It's implied there can be only one per situation. -# You are welcome to improve this code. -cycle = (obj, character) -> - responses = obj.cycle_gallery() - character.sandbox.cycle_index ?= [] # initialize with empty array - character.sandbox.cycle_index[obj.name] ?= 0 # initialize with 0 - response = responses[character.sandbox.cycle_index[obj.name]] - character.sandbox.cycle_index[obj.name]++ - if character.sandbox.cycle_index[obj.name] == responses.length - character.sandbox.cycle_index[obj.name] = 0 - return textcycle(response, 'cyclewriter') - # usage: writemd( system, "Text to write") writemd = (system, text) -> text = markdown(text) diff --git a/game/story.coffee b/game/story.coffee index c900769..d0360fa 100644 --- a/game/story.coffee +++ b/game/story.coffee @@ -19,10 +19,7 @@ room "plaza", return "Upwards" else return "Town plaza" - cycle_gallery: () -> # it needs to be a function if you want localization - return [ - "quirky", "distinct", "kooky", "crazy", "quaint" - ] + cycle: ["quirky", "distinct", "kooky", "crazy", "quaint"] ways: ["shop"] before: (character, system, from) -> if from == 'world' @@ -32,8 +29,6 @@ room "plaza", """ else "You quickly find the central plaza." - writers: - cyclewriter: (character) -> cycle(this, character) objects: policeman: obj "policeman", dsc: "There is a policeman nearby. You could ask him {{for directions.}}" @@ -65,14 +60,13 @@ room "lair", dsc: """ The Lair of Yog-Sothoth is a very *n'gai* cave, full of *buggs-shoggogs* and *n'ghaa ng'aa*. """ - objects: { + objects: bugg: obj "bugg", dsc: "You see a particularly beautiful slimy {{bugg.}}" takeable: false act: () => here().drop(@name) return "You eat the bugg mass. Delicious and raw. Perhaps it's a good lair to live in." - } dialogue "Yes", "merchant", "merchant", """ Yes. @@ -86,14 +80,13 @@ room "shop-inside", dsc: """ The insides are painted pastel white, honouring The Great Milk Spill of 1985. """ - objects: { + objects: merchant: obj "merchant", dsc: "A {{merchant}} eyes you warily." takeable: false act: (character, system) => undum.processClick("merchdialogue") return "" - } ### I want to be able to do this but I can't because I'm lost in all the `this` and @objects and `new`. diff --git a/lib/cycle.coffee b/lib/cycle.coffee new file mode 100644 index 0000000..6d3f94f --- /dev/null +++ b/lib/cycle.coffee @@ -0,0 +1,19 @@ +# Cycling interface. +# Rooms: cycle through this.cycle_gallery +# Objects: cycle through this.cycle_gallery + +cyclelink = (content) -> + return "#{content}" + +cycle = (responses, name, character) -> + if typeof responses == "function" + responses = responses() + character.sandbox.cycle_index ?= [] # initialize with empty array + character.sandbox.cycle_index[name] ?= 0 # initialize with 0 + response = responses[character.sandbox.cycle_index[name]] + character.sandbox.cycle_index[name]++ + if character.sandbox.cycle_index[name] == responses.length + character.sandbox.cycle_index[name] = 0 + return cyclelink(response) + +module.exports = cycle diff --git a/lib/room.coffee b/lib/room.coffee index 8809999..0f00c3c 100644 --- a/lib/room.coffee +++ b/lib/room.coffee @@ -1,9 +1,9 @@ # I confess that this world model heavily borrows from INSTEAD engine. - A.Y. undum = require('./undum.js') -RaconteurSituation = require('./situation.coffee') obj = require('./obj.coffee') markdown = require('./markdown.coffee') +cycle = require('./cycle.coffee') way_to = (content, ref) -> return "#{content}" @@ -60,12 +60,13 @@ update_ways = (ways, name) -> for node in min_key addClass(document.getElementById("waylink-#{node}"), "destination") -class SaletRoom extends RaconteurSituation +class SaletRoom extends undum.Situation constructor: (spec) -> - RaconteurSituation.call(this, spec) + undum.Situation.call(this, spec) for index, value of spec this[index] = value return this + visited: 0 title: "Room" objects: {} extendSection: false @@ -143,7 +144,7 @@ class SaletRoom extends RaconteurSituation An internal function to get the room's description and the descriptions of every object in this room. ### - look: (character, system, f) -> + look: (character, system, f) => update_ways(@ways, @name) retval = "" @@ -173,24 +174,55 @@ class SaletRoom extends RaconteurSituation You could interpret this as an EXAMINE verb or USE one, it's your call. ### act: (character, system, action) => - if (link = action.match(/^_act_(.+)$/)) #object action + if (link = action.match(/^_(act|cycle)_(.+)$/)) #object action for name, thing of @objects - if name == link[1] - # If it's takeable, the player can take this object. - # If not, we check the "act" function. - if thing.takeable - character.sandbox.inventory.push thing - @drop name - cls(system) - @entering.fcall(this, character, system, @name) - return print(thing.take.fcall(thing, character, system)) - if thing.act - return print(thing.act.fcall(thing, character, system)) + if name == link[2] + if link[1] == "act" + # If it's takeable, the player can take this object. + # If not, we check the "act" function. + if thing.takeable + character.sandbox.inventory.push thing + @drop name + cls(system) + @entering.fcall(this, character, system, @name) + return print(thing.take.fcall(thing, character, system)) + if thing.act + return print(thing.act.fcall(thing, character, system)) + elseif link[1] == "cycle" + # TODO object cyclewriter # the loop is done but no return came - match not found console.error("Could not find #{link[1]} in current room.") - # default Raconteur action - return RaconteurSituation.prototype.act.call(this, character, system, action) + # we're done with objects, now check the regular actions + actionClass = action.match(/^_(\w+)_(.+)$/) + that = this + + responses = { + writer: (ref) -> + content = that.writers[ref].fcall(that, character, system, action) + output = markdown(content) + system.writeInto(output, '#current-situation') + replacer: (ref) -> + content = that.writers[ref].fcall(that, character, system, action) + output = ""+content+"" #

tags are usually bad for replacers + system.replaceWith(output, '#'+ref) + inserter: (ref) -> + content = that.writers[ref].fcall(that, character, system, action) + output = markdown(content) + system.writeInto(output, '#'+ref) + } + + if (actionClass) + # Matched a special action class + [responder, ref] = [actionClass[1], actionClass[2]] + + if(!@writers.hasOwnProperty(actionClass[2])) + throw new Error("Tried to call undefined writer: #{action}"); + responses[responder](ref); + else if (@actions.hasOwnProperty(action)) + @actions[action].call(this, character, system, action); + else + throw new Error("Tried to call undefined action: #{action}"); # Marks every room in the game with distance to this room destination: () => @@ -205,6 +237,17 @@ class SaletRoom extends RaconteurSituation node.distance = current_room.distance + 1 candidates.push(node) + register: () => + if not @name? + console.error("Situation has no name") + return this + undum.game.situations[@name] = this + return this + + writers: + cyclewriter: (character) -> + cycle(this.cycle, this.name, character) + room = (name, spec) -> spec ?= {} spec.name = name diff --git a/lib/situation.coffee b/lib/situation.coffee deleted file mode 100644 index a433ae4..0000000 --- a/lib/situation.coffee +++ /dev/null @@ -1,87 +0,0 @@ -### -This file is built on top of Raconteur. -Raconteur is copyright (c) 2015 Bruno Dias -This file is copyright (c) 2016 Alexander Yakovlev - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. -### - -undum = require('./undum.js') -markdown = require('./markdown.coffee') - -### - The prototype RaconteurSituation is the basic spec for situations - created with Raconteur. It should be able to handle any use case for Undum. - This prototype is fairly complex; see the API documentation. -### - -RaconteurSituation = (spec) -> - if RaconteurSituation.arguments.length == 0 - return - undum.Situation.call(this, spec) - - for key, value of spec - this[key] ?= value - - @visited = 0 - return this -RaconteurSituation.inherits(undum.Situation) - -### - Situation.prototype.act() is called by Undum whenever an action link - (Ie, a link that doesn't point at another situation or an external URL) is - clicked. - - Raconteur's version of act() is set up to implement commonly used - functionality: "writer" links, "replacer" links, "inserter" links, and - generic "action" links that call functions which access the underlying - Undum API. -### - -RaconteurSituation.prototype.act = (character, system, action) -> - actionClass = action.match(/^_(\w+)_(.+)$/) - that = this - - responses = { - writer: (ref) -> - content = that.writers[ref].fcall(that, character, system, action) - output = markdown(content) - system.writeInto(output, '#current-situation') - replacer: (ref) -> - content = that.writers[ref].fcall(that, character, system, action) - output = ""+content+"" #

tags are usually bad for replacers - system.replaceWith(output, '#'+ref) - inserter: (ref) -> - content = that.writers[ref].fcall(that, character, system, action) - output = markdown(content) - system.writeInto(output, '#'+ref) - } - - if (actionClass) - # Matched a special action class - [responder, ref] = [actionClass[1], actionClass[2]] - - if(!@writers.hasOwnProperty(actionClass[2])) - throw new Error("Tried to call undefined writer: #{action}"); - responses[responder](ref); - else if (@actions.hasOwnProperty(action)) - @actions[action].call(this, character, system, action); - else - throw new Error("Tried to call undefined action: #{action}"); - -RaconteurSituation.prototype.register = () -> - if not @name? - console.error("Situation has no name") - return this - undum.game.situations[@name] = this - return this - -module.exports = RaconteurSituation