From 828962b1ff14b7fa46ed068e1f01d47cd5679d6d Mon Sep 17 00:00:00 2001 From: Alexander Yakovlev Date: Fri, 15 Jan 2016 15:10:12 +0700 Subject: [PATCH] Documentation and API Made enter() a redefinable function. Updated Gulpfile to watch the libs. Fixed a couple of bugs in Raconteur CoffeeScript port. --- Gulpfile.coffee | 1 + game/begin.coffee | 9 +++++ game/story.coffee | 84 +++++++++++++++++++++++++++++++++++++------- html/index.html | 6 +++- lib/dialogue.coffee | 1 + lib/room.coffee | 54 ++++++++++++++++++++++------ lib/situation.coffee | 8 ++--- lib/undum.js | 20 ++++------- 8 files changed, 142 insertions(+), 41 deletions(-) diff --git a/Gulpfile.coffee b/Gulpfile.coffee index 6cae8a9..b452c44 100644 --- a/Gulpfile.coffee +++ b/Gulpfile.coffee @@ -86,6 +86,7 @@ gulp.task('serve', ['build'], () -> 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(['./build/css/main.css'], sassListener); gulp.watch( diff --git a/game/begin.coffee b/game/begin.coffee index b810ac0..f11fc17 100644 --- a/game/begin.coffee +++ b/game/begin.coffee @@ -23,6 +23,15 @@ actlink = (content, ref) -> textcycle = (content, ref) -> return "#{content}" +### +This function clears the screen. +The full backlog makes sense in dialogues but it's clunky when you exploring. +So this is manual cls +### +cls = () -> + document.getElementById("intro").innerHTML = "" + document.getElementById("content").innerHTML = "" + # usage: writemd( system, "Text to write") writemd = (system, text) -> text = markdown(text) diff --git a/game/story.coffee b/game/story.coffee index e02bcd4..3e9a98e 100644 --- a/game/story.coffee +++ b/game/story.coffee @@ -1,19 +1,79 @@ # Your game goes here -dialogue "Option 1", "start", "secretary", """ - No spoilers! - """ +dialogue "Yes", "start", "question2", """ + Good, because you'll have to read the sources. A lot. + + Salet is *not* a library or a framework you can slap something on. + It's more a game you can hack into your game. + (Like Undum, yeah.) -dialogue "Option 2", "start", "secretary", """ - No spoilers! - """ + I hope to change that in the future but for now it's a weird mix of CoffeeScript classes and Undum core Javascript functions. + And Undum is notorious for making some hardcoded calls to how your game should *look and feel*. + The point of Salet is to have some freedom to change the user interface however you like. + But you'll have to code it first. -room "university-start", - tags: ["secretary"] - ways: ["supermarket"] - optionText: "Leave the University" + For example, notice the lack of "Character" section? + The qualities and tools? + The functionality is still there but you'll have to style it yourself if you want it. + + So, that's the bad news. The good news are just a click ahead of you. +""" + +dialogue "No", "start", "question2", """ + Okay, this is going to be tough but you'll have to pick up *something* to use this. + + Salet is *not* a library or a framework you can slap something on. + It's more a game you can hack into your game. (Like Undum, yeah.) + + There is no 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. + + So, that's the bad news. The good news are just a click ahead of you. +""" + +dialogue "Dialogue functions", "question2", "world", """ + Let's start with a relatively small feature: dialogues. + + Undum's arguably not the best thing for menu-style dialogues because while it gives the author a great dialogue engine, + he has to write a lot of code for a single reply. + So I did a shortcut function. + + This is Undum's implicit choice, which you can use in you dialogues or floating modules: + + dialogue "Title", "start_tag", "end_tag", "content", "code" + + where: + + * `title` is a text the player clicks on, + * `start_tag` is a tag for *this* room (no `#` or arrays), + * `end_tag` is a tag for the choices in this room (no `#` or arrays) + * `code` is a piece of Javascript that gets executed once the player clicks on a choice. + + You still need to write a Situation for each reply, sorry. + But now you can do it faster! + + Okay, with this aside, let me show you... The World. +""" + +room "world", + tags: ["world"], + optionText: "Enter the world", + before: () -> + cls() + ways: ["university"] + content: """ + ### Rhinestone Room + + You're in a large room carved inside a giant milky rock mountain. + The floor and walls are littered with signs and signatures of the previous visitors. + + A steep narrow #{textlink("well", "well")} proceeds upward. + """ + writers: + well: "There is only one passage out. See the „Other rooms“ block popped up? Click it." + +room "university", before: () -> - document.getElementById("intro").innerHTML = "" - document.getElementById("content").innerHTML = "" undum.game.situations["supermarket"].destination() """ You leave the University. diff --git a/html/index.html b/html/index.html index e1b39f3..0dca9ab 100644 --- a/html/index.html +++ b/html/index.html @@ -23,7 +23,11 @@
-

Intro here.

+

Salet is an offspring of Undum and Raconteur.

+

The project is still a work-in-progress. This "game" will show you some of the new features.

+

It's supposed to be relatively painless for the author without sacrificing the reader's experience. + For example, the game still loads while you're reading this.

+

First of all, are you familiar with HTML and Coffeescript? As in, reading the real source code.

diff --git a/lib/dialogue.coffee b/lib/dialogue.coffee index ae6c697..f585303 100644 --- a/lib/dialogue.coffee +++ b/lib/dialogue.coffee @@ -19,6 +19,7 @@ dialogue = (title, startTag, endTag, text, effect) -> retval = room(randomid(), { optionText: title content: text + clear: false # backlog is useful in dialogues choices: "#"+endTag }) if typeof(startTag) == "string" diff --git a/lib/room.coffee b/lib/room.coffee index 7423cbd..5d46fd6 100644 --- a/lib/room.coffee +++ b/lib/room.coffee @@ -36,6 +36,7 @@ update_ways = (ways) -> content = "" distances = [] if ways + document.querySelector(".ways h2").style.display = "block" for way in ways if undum.game.situations[way]? title = undum.game.situations[way].name @@ -44,6 +45,8 @@ update_ways = (ways) -> key: way distance: undum.game.situations[way].distance }) + else + document.querySelector(".ways h2").style.display = "none" document.getElementById("ways").innerHTML = content min = Infinity min_key = [] @@ -64,16 +67,38 @@ class SaletRoom extends RaconteurSituation @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 return this objects: [] distance: Infinity # distance to the destination + clear: true # clear the screen on entering the room? ### I call SaletRoom.exit every time the player exits to another room. + Unlike @after this gets called after the section is closed. + It's a styling difference. ### exit: (character, system, to) -> + return true + ### - Undum calls Situation.enter every time a situation is entered, and + I call SaletRoom.enter every time the player enters this room but before the section is opened. + Unlike @before this gets called before the current section is opened. + It's a styling difference. + + 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) -> + return true + + ### + Salet's Undum version calls Situation.entering every time a situation is entered, and passes it three arguments; The character object, the system object, and a string referencing the previous situation, or null if there is none (ie, for the starting situation). @@ -81,11 +106,17 @@ 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. ### - enter: (character, system, f) -> - #system.clearContent() + entering: (character, system, f) -> + if @clear + system.clearContent() + if f != @name and f? @visited++ - undum.game.situations[f].exit(character, system, @name) + if undum.game.situations[f].exit? + undum.game.situations[f].exit(character, system, @name) + + if @enter + @enter character, system, f if not @extendSection classes = if @classes then ' ' + @classes.join(' ') else '' @@ -95,13 +126,17 @@ class SaletRoom extends RaconteurSituation system.write("
") if f != @name and @before? - print(@before.fcall(this, character, system, f)) + content = @before.fcall(this, character, system, f) + if content + print(content) if @look @look character, system, f if f != @name and @after? - print(@after.fcall(this, character, system, f)) + content = @after.fcall(this, character, system, f) + if content + print(content) if not @extendSection system.write("
") @@ -124,10 +159,6 @@ class SaletRoom extends RaconteurSituation You could interpret this as an EXAMINE verb or USE one, it's your call. ### act: (character, system, action) -> - # default Raconteur action - if (action.match(/^_(writer|replacer|inserter)_.+$/)) - return RaconteurSituation.prototype.act.call(this, character, system, f) - if (link = action.match(/^_act_(.+)$/)) #object action for thing in @objects if thing.name == link[1] @@ -142,6 +173,9 @@ class SaletRoom extends RaconteurSituation return print(thing.act.fcall(thing, character, system)) # 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) # Marks every room in the game with distance to this room destination: () -> diff --git a/lib/situation.coffee b/lib/situation.coffee index 567fc89..88b1c63 100644 --- a/lib/situation.coffee +++ b/lib/situation.coffee @@ -29,7 +29,7 @@ String.prototype.fcall = () -> return this #Adds the "fade" class to a htmlString. String.prototype.fade = () -> - return this.classList.add("fade") + return '
'+this+'
' ### The prototype RaconteurSituation is the basic spec for situations @@ -66,15 +66,15 @@ RaconteurSituation.prototype.act = (character, system, action) -> responses = { writer: (ref) -> - content = @writers[ref].fcall(that, character, system, action) + content = that.writers[ref].fcall(that, character, system, action) output = markdown(content).fade() system.writeInto(output, '#current-situation') replacer: (ref) -> - content = @writers[ref].fcall(that, character, system, action) + content = that.writers[ref].fcall(that, character, system, action) output = markdown(content).fade() system.replaceWith(output, '#'+ref) inserter: (ref) -> - content = @writers[ref].fcall(that, character, system, action) + content = that.writers[ref].fcall(that, character, system, action) output = markdown(content).fade() system.writeInto(output, '#'+ref) } diff --git a/lib/undum.js b/lib/undum.js index 29cd26b..ba86592 100644 --- a/lib/undum.js +++ b/lib/undum.js @@ -151,9 +151,8 @@ var assert = function(expression, message) { var Situation = function(opts) { if (opts) { - if (opts.enter) this._enter = opts.enter; + if (opts.entering) this._enter = opts.entering; if (opts.act) this._act = opts.act; - if (opts.exit) this._exit = opts.exit; // Options related to this situation being automatically // selected and displayed in a list of options. @@ -194,19 +193,14 @@ var Situation = function(opts) { * may be null if this is the starting situation. Unlike the * exit() method, this method cannot prevent the transition * happening: its return value is ignored. */ -Situation.prototype.enter = function(character, system, from) { - if (this._enter) this._enter(character, system, from); +Situation.prototype.entering = function(character, system, from) { + if (this._entering) this._entering(character, system, from); }; /* A function that takes action when we carry out some action in a * situation that isn't intended to lead to a new situation. */ Situation.prototype.act = function(character, system, action) { if (this._act) this._act(character, system, action); }; -/* A function that takes action when we exit a situation. The last - * parameter indicates the situation we are going to. */ -Situation.prototype.exit = function(character, system, to) { - if (this._exit) this._exit(character, system, to); -}; /* Determines whether this situation should be contained within a * list of options generated automatically by the given * situation. */ @@ -300,7 +294,7 @@ var SimpleSituation = function(content, opts) { this.maxChoices = opts && opts.maxChoices; }; SimpleSituation.inherits(Situation); -SimpleSituation.prototype.enter = function(character, system, from) { +SimpleSituation.prototype.entering = function(character, system, from) { if (this.heading) { if ($.isFunction(this.heading)) { system.writeHeading(this.heading()); @@ -308,7 +302,7 @@ SimpleSituation.prototype.enter = function(character, system, from) { system.writeHeading(this.heading); } } - if (this._enter) this._enter(character, system, from); + if (this._entering) this._entering(character, system, from); if (this.content) { if ($.isFunction(this.content)) { system.write(this.content()); @@ -1454,8 +1448,6 @@ var doTransitionTo = function(newSituationId) { // We might not have an old situation if this is the start of // the game. if (oldSituation) { - // Notify the exiting situation. - oldSituation.exit(character, system, newSituationId); if (game.exit) { game.exit(character, system, oldSituationId, newSituationId); } @@ -1489,7 +1481,7 @@ var doTransitionTo = function(newSituationId) { if (game.enter) { game.enter(character, system, oldSituationId, newSituationId); } - newSituation.enter(character, system, oldSituationId); + newSituation.entering(character, system, oldSituationId); // additional hook for when the situation text has already been printed if (game.afterEnter) {