1
0
Fork 0
mirror of https://gitlab.com/Oreolek/salet.git synced 2024-07-07 01:04:25 +03:00

Object manipulation - WIP

I'm stuck because of a COFFEESCRIPT bug or whatever.
This commit is contained in:
Alexander Yakovlev 2016-01-16 23:24:23 +07:00
parent 8ad06c0d0a
commit 0ed4f98098
9 changed files with 105 additions and 87 deletions

View file

@ -51,21 +51,21 @@ opts = _.assign({}, watchify.args, {
debug: true debug: true
transform: [coffeify] transform: [coffeify]
}); });
bundler = watchify(browserify(opts)); bundler = watchify(browserify(opts))
bundle = () -> bundle = () ->
return bundler.bundle() return bundler.bundle()
.on('error', gutil.log.bind(gutil, 'Browserify Error')) .on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js')) .pipe(source('bundle.js'))
.pipe(gulp.dest('./build/game')); .pipe(gulp.dest('./build/game'))
gulp.task('concatCoffee', () -> gulp.task('concatCoffee', () ->
return gulp.src(sources) return gulp.src(sources)
.pipe(concat('./main.coffee')) .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('update', bundle);
bundler.on('log', gutil.log); bundler.on('log', gutil.log);
@ -77,21 +77,20 @@ gulp.task('serve', ['build'], () ->
server: { server: {
baseDir: 'build' baseDir: 'build'
} }
}); })
sassListener = () -> sassListener = () ->
reload('./build/css/main.css'); reload('./build/css/main.css')
gulp.watch(['./html/*.html'], ['html']); gulp.watch(['./html/*.html'], ['html'])
gulp.watch(['./sass/*.scss'], ['sass']); gulp.watch(['./sass/*.scss'], ['sass'])
gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img']); gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img'])
gulp.watch(['./game/*.coffee'], ['coffee']); gulp.watch(['./lib/*.coffee', './lib/*.js', './game/*.coffee'], ['coffee'])
gulp.watch(['./lib/*.coffee','./lib/*.js'], ['coffee']);
gulp.watch(['./build/css/main.css'], sassListener); gulp.watch(['./build/css/main.css'], sassListener)
gulp.watch( gulp.watch(
['./build/game/bundle.js', './build/img/*', './build/index.html'], ['./build/game/bundle.js', './build/img/*', './build/index.html'],
browserSync.reload); browserSync.reload)
) )
gulp.task('html-dist', html('./dist')); gulp.task('html-dist', html('./dist'));

View file

@ -42,6 +42,9 @@ writemd = (system, text) ->
text = markdown(text) text = markdown(text)
system.write(text) system.write(text)
get_room = (name) ->
return undum.game.situations[name]
# The first room of the game. # The first room of the game.
# For accessibility reasons the text is provided in HTML, not here. # For accessibility reasons the text is provided in HTML, not here.
room "start", room "start",

View file

@ -2,5 +2,6 @@
# All code in this file comes last, so the game is almost ready by this point. # All code in this file comes last, so the game is almost ready by this point.
undum.game.init = (character, system) -> undum.game.init = (character, system) ->
bugg.put("lair")
window.onload = undum.begin window.onload = undum.begin

View file

@ -2,6 +2,8 @@ room "world",
tags: ["start"], tags: ["start"],
optionText: "Enter the world", optionText: "Enter the world",
ways: ["plaza"] ways: ["plaza"]
enter: () ->
bugg.put(@name)
content: """ content: """
### Rhinestone Room ### Rhinestone Room
@ -40,7 +42,7 @@ room "plaza",
if character.sandbox.has_mark? if character.sandbox.has_mark?
return "You already talked to him, no need to bug the man twice." return "You already talked to him, no need to bug the man twice."
character.sandbox.has_mark ?= true character.sandbox.has_mark ?= true
undum.game.situations["lair"].destination() get_room("lair").destination()
""" """
Here, let me mark it on your map. Here, let me mark it on your map.
""" """
@ -48,7 +50,7 @@ room "plaza",
room "shop", room "shop",
title: "The Shop" title: "The Shop"
ways: ["plaza", "lair"] ways: ["plaza", "shop-inside", "lair"]
content: """ content: """
Being the only shop in town, this trendy establishment did not need a name. 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". It's an open question why it had one, especially because its name was "Hung Crossing".
@ -57,8 +59,8 @@ room "shop",
""" """
room "lair", room "lair",
ways: ["shop"]
title: "The Lair" title: "The Lair"
before: "Seems like you can't leave this just like that."
content: """ content: """
The Lair of Yog-Sothoth is a very *n'gai* cave, full of *buggs-shoggogs* and *n'ghaa ng'aa*. 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: () -> act: () ->
this.delete() this.delete()
return "You eat the bugg mass. Delicious and raw." return "You eat the bugg mass. Delicious and raw."
bugg.put("lair")
room "shop-inside", room "shop-inside",
ways: ["shop"] ways: ["shop"]

View file

@ -32,9 +32,9 @@
<p>There are no either cool editor or clear instructions. <p>There are no either cool editor or clear instructions.
You'll have to copy the source code and edit the CoffeeScript files in the <code>game</code> folder. You'll have to copy the source code and edit the CoffeeScript files in the <code>game</code> folder.
Then <em>compile</em> them.</p> Then <em>compile</em> them.</p>
<p>This is not a technical documentation or UI tutorial, this is a <em>game.</em> <p>This is neither a technical documentation nor UI tutorial, this is a <em>game.</em>
You're supposed to note all the cool and curious features yourself. You're supposed to note all the cool and curious features yourself.
If you want some technical info, there is the <a href="http://git.oreolek.ru/oreolek/salet">source code</a> and a <a href="http://git.oreolek.ru/oreolek/salet/wikis/home">wiki.</a></p> If you want some technical info, there are the <a href="http://git.oreolek.ru/oreolek/salet">source code</a> and a <a href="http://git.oreolek.ru/oreolek/salet/wikis/home">wiki.</a></p>
<p>I'm just here to point out that you are playing this in the web browser, online. <p>I'm just here to point out that you are playing this in the web browser, online.
And you don't need to learn <em>The Complete Javascript, Volumes I-III</em> to write in this style.</p> And you don't need to learn <em>The Complete Javascript, Volumes I-III</em> to write in this style.</p>
<p>So let me show you... The World.</p> <p>So let me show you... The World.</p>

View file

@ -5,35 +5,42 @@ objlink = (content, ref) ->
Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1 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) -> constructor: (spec) ->
for key, value of spec for key, value of spec
this[key] ?= value this[key] ?= value
level: 0 level: 0
look: (character, system, f) -> look: (character, system, f) =>
if @dsc if @dsc
text = markdown(@dsc.fcall(this, character, system, f)) text = markdown(@dsc.fcall(this, character, system, f))
text = "<span class='look lvl#{@level}'>" + text + "</span>" text = "<span class='look lvl#{@level}'>" + text + "</span>"
window.name = @name # replace braces {{}} with link to _act_
text = text.replace /([\s^])\{\{(\w+)\}\}([\s$])/g, (str, p1, p2, p3) -> return parsedsc(text, @name)
name = window.name take: (character, system) => "You take the #{@name}." # taking to inventory
window.name = undefined act: (character, system) => "You don't find anything extraordinary about the #{@name}." # object action
return p1+objlink(p2, name)+p3 dsc: (character, system) => "You see a {{#{@name}}} here." # object description
return text inv: (character, system) => "It's a {{#{@name}.}}" # inventory description
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
location: "" location: ""
put: (location) -> put: (location) =>
@level = 0 # this is scenery @level = 0 # this is scenery
undum.game.situations[location].objects[@name] = this if undum.game.situations[location]?
@location = location undum.game.situations[location].take(this)
delete: () -> @location = location
delete: () =>
undum.game.situations[@location].objects.remove(this) undum.game.situations[@location].objects.remove(this)
obj = (name, spec) -> obj = (name, spec) ->
spec ?= {} spec ?= {}
spec.name = name spec.name = name
return new RaconteurObj(spec) return new SaletObj(spec)
module.exports = obj module.exports = obj

View file

@ -31,6 +31,10 @@ addClass = (element, className) ->
here = () -> here = () ->
return undum.game.situations[document.getElementById("current-situation").getAttribute("data-situation")] return undum.game.situations[document.getElementById("current-situation").getAttribute("data-situation")]
cls = (system) ->
system.clearContent()
system.clearContent("#intro")
update_ways = (ways, name) -> update_ways = (ways, name) ->
content = "" content = ""
distances = [] distances = []
@ -62,23 +66,11 @@ update_ways = (ways, name) ->
class SaletRoom extends RaconteurSituation class SaletRoom extends RaconteurSituation
constructor: (spec) -> constructor: (spec) ->
RaconteurSituation.call(this, spec) RaconteurSituation.call(this, spec)
if spec.objects? for index, value of spec
@objects = spec.objects this[index] = value
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
return this return this
title: "Room" title: "Room"
objects: [] objects: {}
extendSection: false extendSection: false
distance: Infinity # distance to the destination distance: Infinity # distance to the destination
clear: true # clear the screen on entering the room? 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. Unlike @after this gets called after the section is closed.
It's a styling difference. It's a styling difference.
### ###
exit: (character, system, to) -> exit: (character, system, to) =>
return true 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. 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. It was renamed as @entering to achieve API consistency.
### ###
enter: (character, system, from) -> enter: (character, system, from) =>
return true return true
### ###
@ -111,10 +103,9 @@ class SaletRoom extends RaconteurSituation
My version of `enter` splits the location description from the effects. 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. 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? if @clear and f?
system.clearContent() cls(system)
system.clearContent("#intro")
if f != @name and f? if f != @name and f?
@visited++ @visited++
@ -138,8 +129,7 @@ class SaletRoom extends RaconteurSituation
if f != @name and @before? if f != @name and @before?
current_situation += markdown(@before.fcall(this, character, system, f)) 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? if f != @name and @after?
current_situation += markdown(@after.fcall(this, character, system, f)) current_situation += markdown(@after.fcall(this, character, system, f))
@ -152,7 +142,11 @@ class SaletRoom extends RaconteurSituation
if @choices if @choices
system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices)) 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) update_ways(@ways, @name)
retval = "" retval = ""
@ -160,25 +154,37 @@ class SaletRoom extends RaconteurSituation
if @content if @content
retval += markdown(@content.fcall(this, character, system, f)) 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() retval += thing.look()
return retval 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. 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. 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 if (link = action.match(/^_act_(.+)$/)) #object action
for thing in @objects for name, thing of @objects
if thing.name == link[1] if name == link[1]
# We check the "take" function. If it exists, the player can take this object. # We check the "take" function. If it exists, the player can take this object.
# If not, we check the "act" function. # If not, we check the "act" function.
if thing.take if thing.take
@objects.remove(thing)
character.sandbox.inventory.push 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)) return print(thing.take.fcall(thing, character, system))
if thing.act if thing.act
return print(thing.act.fcall(thing, character, system)) 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) return RaconteurSituation.prototype.act.call(this, character, system, action)
# Marks every room in the game with distance to this room # Marks every room in the game with distance to this room
destination: () -> destination: () =>
@distance = 0 @distance = 0
candidates = [this] candidates = [this]
@ -202,9 +208,9 @@ class SaletRoom extends RaconteurSituation
candidates.push(node) candidates.push(node)
room = (name, spec) -> room = (name, spec) ->
if spec spec ?= {}
spec.name = name spec.name = name
retval = new SaletRoom(spec) retval = new SaletRoom(spec)
return retval.register() return retval.register()
module.exports = room module.exports = room

View file

@ -17,16 +17,6 @@ all copies or substantial portions of the Software.
undum = require('./undum.js') undum = require('./undum.js')
markdown = require('./markdown.coffee') 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 The prototype RaconteurSituation is the basic spec for situations
created with Raconteur. It should be able to handle any use case for Undum. created with Raconteur. It should be able to handle any use case for Undum.
@ -56,7 +46,7 @@ RaconteurSituation.inherits(undum.Situation)
Undum API. Undum API.
### ###
RaconteurSituation.prototype.act = (character, system, action) -> RaconteurSituation.prototype.act = (character, system, action) =>
actionClass = action.match(/^_(\w+)_(.+)$/) actionClass = action.match(/^_(\w+)_(.+)$/)
that = this that = this

View file

@ -87,7 +87,7 @@ var assert = function(expression, message) {
* parameter is an object. So you could write: * parameter is an object. So you could write:
* *
* var situation = Situation({ * var situation = Situation({
* enter: function(character, system, from) { * entering: function(character, system, from) {
* ... your implementation ... * ... your implementation ...
* } * }
* }); * });
@ -151,7 +151,7 @@ var assert = function(expression, message) {
var Situation = function(opts) { var Situation = function(opts) {
if (opts) { if (opts) {
if (opts.entering) this._enter = opts.entering; if (opts.entering) this._entering = opts.entering;
if (opts.act) this._act = opts.act; if (opts.act) this._act = opts.act;
// Options related to this situation being automatically // Options related to this situation being automatically
@ -691,7 +691,7 @@ System.prototype.writeChoices = function(listOfIds, elementSelector) {
continue; continue;
} }
var optionText = situation.optionText(character, this, var optionText = situation.optionText.fcall(this, character, this,
currentSituation); currentSituation);
if (!optionText) optionText = "choice".l({number:i+1}); if (!optionText) optionText = "choice".l({number:i+1});
var $option = $("<li>"); var $option = $("<li>");
@ -708,6 +708,15 @@ System.prototype.writeChoices = function(listOfIds, elementSelector) {
doWrite($options, elementSelector, 'append', 'after'); doWrite($options, elementSelector, 'append', 'after');
}; };
/*
* 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 = function() { return this; }
/* Returns a list of situation ids to choose from, given a set of /* Returns a list of situation ids to choose from, given a set of
* specifications. * specifications.
* *
@ -961,7 +970,9 @@ System.prototype.setQuality = function(quality, newValue) {
*/ */
var Character = function() { var Character = function() {
this.qualities = {}; this.qualities = {};
this.sandbox = {}; this.sandbox = {
inventory: []
};
}; };
/* The data structure holding the content for the game. By default /* The data structure holding the content for the game. By default
@ -1021,7 +1032,7 @@ var game = {
* *
* function(character, system, oldSituationId, newSituationId); * function(character, system, oldSituationId, newSituationId);
*/ */
enter: null, entering: null,
/* Hook for when the situation has already been carried out /* Hook for when the situation has already been carried out
* and printed. The signature is: * and printed. The signature is: