diff --git a/Gulpfile.coffee b/Gulpfile.coffee index b452c44..4ad5430 100644 --- a/Gulpfile.coffee +++ b/Gulpfile.coffee @@ -51,21 +51,21 @@ opts = _.assign({}, watchify.args, { debug: true transform: [coffeify] }); -bundler = watchify(browserify(opts)); +bundler = watchify(browserify(opts)) bundle = () -> return bundler.bundle() .on('error', gutil.log.bind(gutil, 'Browserify Error')) .pipe(source('bundle.js')) - .pipe(gulp.dest('./build/game')); + .pipe(gulp.dest('./build/game')) gulp.task('concatCoffee', () -> return gulp.src(sources) .pipe(concat('./main.coffee')) - .pipe(gulp.dest('./build/game')); -); + .pipe(gulp.dest('./build/game')) +) -gulp.task('coffee', ['concatCoffee'], bundle); +gulp.task('coffee', ['concatCoffee'], bundle) bundler.on('update', bundle); bundler.on('log', gutil.log); @@ -77,21 +77,20 @@ gulp.task('serve', ['build'], () -> server: { baseDir: 'build' } - }); + }) sassListener = () -> - reload('./build/css/main.css'); + reload('./build/css/main.css') - gulp.watch(['./html/*.html'], ['html']); - gulp.watch(['./sass/*.scss'], ['sass']); - gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img']); - gulp.watch(['./game/*.coffee'], ['coffee']); - gulp.watch(['./lib/*.coffee','./lib/*.js'], ['coffee']); + gulp.watch(['./html/*.html'], ['html']) + gulp.watch(['./sass/*.scss'], ['sass']) + gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img']) + gulp.watch(['./lib/*.coffee', './lib/*.js', './game/*.coffee'], ['coffee']) - gulp.watch(['./build/css/main.css'], sassListener); + gulp.watch(['./build/css/main.css'], sassListener) gulp.watch( ['./build/game/bundle.js', './build/img/*', './build/index.html'], - browserSync.reload); + browserSync.reload) ) gulp.task('html-dist', html('./dist')); diff --git a/game/begin.coffee b/game/begin.coffee index 64cc316..ec90f2b 100644 --- a/game/begin.coffee +++ b/game/begin.coffee @@ -42,6 +42,9 @@ writemd = (system, text) -> text = markdown(text) system.write(text) +get_room = (name) -> + return undum.game.situations[name] + # The first room of the game. # For accessibility reasons the text is provided in HTML, not here. room "start", diff --git a/game/init.coffee b/game/init.coffee index 658e2ad..1bd3030 100644 --- a/game/init.coffee +++ b/game/init.coffee @@ -2,5 +2,6 @@ # All code in this file comes last, so the game is almost ready by this point. undum.game.init = (character, system) -> + bugg.put("lair") window.onload = undum.begin diff --git a/game/story.coffee b/game/story.coffee index 868128d..fcbdf0c 100644 --- a/game/story.coffee +++ b/game/story.coffee @@ -2,6 +2,8 @@ room "world", tags: ["start"], optionText: "Enter the world", ways: ["plaza"] + enter: () -> + bugg.put(@name) content: """ ### Rhinestone Room @@ -40,7 +42,7 @@ room "plaza", if character.sandbox.has_mark? return "You already talked to him, no need to bug the man twice." character.sandbox.has_mark ?= true - undum.game.situations["lair"].destination() + get_room("lair").destination() """ “Here, let me mark it on your map.” """ @@ -48,7 +50,7 @@ room "plaza", room "shop", title: "The Shop" - ways: ["plaza", "lair"] + ways: ["plaza", "shop-inside", "lair"] content: """ Being the only shop in town, this trendy establishment did not need a name. It's an open question why it had one, especially because its name was "Hung Crossing". @@ -57,8 +59,8 @@ room "shop", """ room "lair", - ways: ["shop"] title: "The Lair" + before: "Seems like you can't leave this just like that." content: """ The Lair of Yog-Sothoth is a very *n'gai* cave, full of *buggs-shoggogs* and *n'ghaa ng'aa*. """ @@ -68,7 +70,6 @@ bugg = obj "bugg-shoggog", act: () -> this.delete() return "You eat the bugg mass. Delicious and raw." -bugg.put("lair") room "shop-inside", ways: ["shop"] diff --git a/html/index.html b/html/index.html index e94eccb..730d04e 100644 --- a/html/index.html +++ b/html/index.html @@ -32,9 +32,9 @@
There are no either cool editor or clear instructions.
You'll have to copy the source code and edit the CoffeeScript files in the game
folder.
Then compile them.
This is not a technical documentation or UI tutorial, this is a game. +
This is neither a technical documentation nor UI tutorial, this is a game. You're supposed to note all the cool and curious features yourself. - If you want some technical info, there is the source code and a wiki.
+ If you want some technical info, there are the source code and a wiki.I'm just here to point out that you are playing this in the web browser, online. And you don't need to learn The Complete Javascript, Volumes I-III to write in this style.
So let me show you... The World.
diff --git a/lib/obj.coffee b/lib/obj.coffee index 204161b..f3247a6 100644 --- a/lib/obj.coffee +++ b/lib/obj.coffee @@ -5,35 +5,42 @@ objlink = (content, ref) -> Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1 -class RaconteurObj +parsedsc = (text, name) -> + window.objname = name + text = text.replace /([\s^])\{\{(.+)\}\}([\s$])/g, (str, p1, p2, p3) -> + name = window.objname + window.objname = undefined + return p1+objlink(p2, name)+p3 + return text + +# An object class. +# An object cannot be in several locations at once, you must clone the variable. +class SaletObj constructor: (spec) -> for key, value of spec this[key] ?= value level: 0 - look: (character, system, f) -> + look: (character, system, f) => if @dsc text = markdown(@dsc.fcall(this, character, system, f)) text = "" + text + "" - window.name = @name - text = text.replace /([\s^])\{\{(\w+)\}\}([\s$])/g, (str, p1, p2, p3) -> - name = window.name - window.name = undefined - return p1+objlink(p2, name)+p3 - return text - take: () -> "You take the #{@name}." # taking to inventory - act: () -> "You don't find anything extraordinary about the #{@name}." # object action - dsc: () -> "You see a {{#{@name}}} here." # object description - inv: () -> "It's a {{#{@name}.}}" # inventory description + # replace braces {{}} with link to _act_ + return parsedsc(text, @name) + take: (character, system) => "You take the #{@name}." # taking to inventory + act: (character, system) => "You don't find anything extraordinary about the #{@name}." # object action + dsc: (character, system) => "You see a {{#{@name}}} here." # object description + inv: (character, system) => "It's a {{#{@name}.}}" # inventory description location: "" - put: (location) -> + put: (location) => @level = 0 # this is scenery - undum.game.situations[location].objects[@name] = this - @location = location - delete: () -> + if undum.game.situations[location]? + undum.game.situations[location].take(this) + @location = location + delete: () => undum.game.situations[@location].objects.remove(this) obj = (name, spec) -> spec ?= {} spec.name = name - return new RaconteurObj(spec) + return new SaletObj(spec) module.exports = obj diff --git a/lib/room.coffee b/lib/room.coffee index d2d011f..c601499 100644 --- a/lib/room.coffee +++ b/lib/room.coffee @@ -31,6 +31,10 @@ addClass = (element, className) -> here = () -> return undum.game.situations[document.getElementById("current-situation").getAttribute("data-situation")] +cls = (system) -> + system.clearContent() + system.clearContent("#intro") + update_ways = (ways, name) -> content = "" distances = [] @@ -62,23 +66,11 @@ update_ways = (ways, name) -> class SaletRoom extends RaconteurSituation constructor: (spec) -> RaconteurSituation.call(this, spec) - if spec.objects? - @objects = spec.objects - if spec.exit? - @exit = spec.exit - if spec.enter? - @enter = spec.enter - if spec.clear? - @clear = spec.clear - if spec.writers? - @writers = spec.writers - if spec.extendSection? - @extendSection = spec.extendSection - if spec.title? - @title = spec.title + for index, value of spec + this[index] = value return this title: "Room" - objects: [] + objects: {} extendSection: false distance: Infinity # distance to the destination clear: true # clear the screen on entering the room? @@ -88,7 +80,7 @@ class SaletRoom extends RaconteurSituation Unlike @after this gets called after the section is closed. It's a styling difference. ### - exit: (character, system, to) -> + exit: (character, system, to) => return true ### @@ -99,7 +91,7 @@ class SaletRoom extends RaconteurSituation The upstream Undum version does not allow you to redefine @enter function easily but allows custom @exit one. It was renamed as @entering to achieve API consistency. ### - enter: (character, system, from) -> + enter: (character, system, from) => return true ### @@ -111,10 +103,9 @@ class SaletRoom extends RaconteurSituation My version of `enter` splits the location description from the effects. Also if f == this.name (we're in the same location) the `before` and `after` callbacks are ignored. ### - entering: (character, system, f) -> + entering: (character, system, f) => if @clear and f? - system.clearContent() - system.clearContent("#intro") + cls(system) if f != @name and f? @visited++ @@ -138,8 +129,7 @@ class SaletRoom extends RaconteurSituation if f != @name and @before? current_situation += markdown(@before.fcall(this, character, system, f)) - if @look - current_situation += @look character, system, f + current_situation += @look character, system, f if f != @name and @after? current_situation += markdown(@after.fcall(this, character, system, f)) @@ -152,7 +142,11 @@ class SaletRoom extends RaconteurSituation if @choices system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices)) - look: (character, system, f) -> + ### + An internal function to get the room's description and the descriptions of + every object in this room. + ### + look: (character, system, f) => update_ways(@ways, @name) retval = "" @@ -160,25 +154,37 @@ class SaletRoom extends RaconteurSituation if @content retval += markdown(@content.fcall(this, character, system, f)) - if @objects? then for thing in @objects + console.log @objects + for name, thing of @objects retval += thing.look() return retval + ### + Puts an object in this room. + ### + take: (thing) => + @objects[thing.name] = thing + # BUG: for some really weird reason if the call is made in init function or + # during the initialization, this ALSO puts the thing in the start room. + undum.game.situations["start"].objects = {} + ### Object action. A function or a string which comes when you click on the object link. You could interpret this as an EXAMINE verb or USE one, it's your call. ### - act: (character, system, action) -> + act: (character, system, action) => if (link = action.match(/^_act_(.+)$/)) #object action - for thing in @objects - if thing.name == link[1] + for name, thing of @objects + if name == link[1] # We check the "take" function. If it exists, the player can take this object. # If not, we check the "act" function. if thing.take - @objects.remove(thing) character.sandbox.inventory.push thing - @enter(character, system, @name) + delete @objects[name] + console.log @objects + 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)) @@ -189,7 +195,7 @@ class SaletRoom extends RaconteurSituation return RaconteurSituation.prototype.act.call(this, character, system, action) # Marks every room in the game with distance to this room - destination: () -> + destination: () => @distance = 0 candidates = [this] @@ -202,9 +208,9 @@ class SaletRoom extends RaconteurSituation candidates.push(node) room = (name, spec) -> - if spec - spec.name = name - retval = new SaletRoom(spec) - return retval.register() + spec ?= {} + spec.name = name + retval = new SaletRoom(spec) + return retval.register() module.exports = room diff --git a/lib/situation.coffee b/lib/situation.coffee index 08dc7f4..3c3cb1c 100644 --- a/lib/situation.coffee +++ b/lib/situation.coffee @@ -17,16 +17,6 @@ all copies or substantial portions of the Software. undum = require('./undum.js') markdown = require('./markdown.coffee') -### - fcall() (by analogy with fmap) is added to the prototypes of both String and - Function. When called on a Function, it's an - alias for Function#call(); when called on a String, it only returns the - string itself, discarding any input. -### - -Function.prototype.fcall = Function.prototype.call; -String.prototype.fcall = () -> return this - ### The prototype RaconteurSituation is the basic spec for situations created with Raconteur. It should be able to handle any use case for Undum. @@ -56,7 +46,7 @@ RaconteurSituation.inherits(undum.Situation) Undum API. ### -RaconteurSituation.prototype.act = (character, system, action) -> +RaconteurSituation.prototype.act = (character, system, action) => actionClass = action.match(/^_(\w+)_(.+)$/) that = this diff --git a/lib/undum.js b/lib/undum.js index fa67a76..7b15ac3 100644 --- a/lib/undum.js +++ b/lib/undum.js @@ -87,7 +87,7 @@ var assert = function(expression, message) { * parameter is an object. So you could write: * * var situation = Situation({ - * enter: function(character, system, from) { + * entering: function(character, system, from) { * ... your implementation ... * } * }); @@ -151,7 +151,7 @@ var assert = function(expression, message) { var Situation = function(opts) { if (opts) { - if (opts.entering) this._enter = opts.entering; + if (opts.entering) this._entering = opts.entering; if (opts.act) this._act = opts.act; // Options related to this situation being automatically @@ -691,7 +691,7 @@ System.prototype.writeChoices = function(listOfIds, elementSelector) { continue; } - var optionText = situation.optionText(character, this, + var optionText = situation.optionText.fcall(this, character, this, currentSituation); if (!optionText) optionText = "choice".l({number:i+1}); var $option = $("