mirror of
https://gitlab.com/Oreolek/black_phone.git
synced 2024-05-05 10:38:45 +03:00
Salet conversion WIP
This commit is contained in:
parent
0d2f825662
commit
f497b78e27
131
Gulpfile.coffee
Normal file
131
Gulpfile.coffee
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
watchify = require('watchify')
|
||||||
|
browserify = require('browserify')
|
||||||
|
browserSync = require('browser-sync')
|
||||||
|
gulp = require('gulp')
|
||||||
|
source = require('vinyl-source-stream')
|
||||||
|
gutil = require('gulp-util')
|
||||||
|
coffeify = require('coffeeify')
|
||||||
|
coffee = require("gulp-coffee")
|
||||||
|
sass = require('gulp-sass')
|
||||||
|
uglify = require('gulp-uglify')
|
||||||
|
buffer = require('vinyl-buffer')
|
||||||
|
zip = require('gulp-zip')
|
||||||
|
_ = require('lodash')
|
||||||
|
concat = require('gulp-concat')
|
||||||
|
|
||||||
|
reload = browserSync.reload
|
||||||
|
|
||||||
|
html = (target) ->
|
||||||
|
return () ->
|
||||||
|
return gulp.src(['html/index.html','html/en.html'])
|
||||||
|
.pipe(gulp.dest(target));
|
||||||
|
|
||||||
|
img = (target) ->
|
||||||
|
return () ->
|
||||||
|
return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg'])
|
||||||
|
.pipe(gulp.dest(target));
|
||||||
|
|
||||||
|
audio = (target) ->
|
||||||
|
return () ->
|
||||||
|
return gulp.src(['audio/*.mp3'])
|
||||||
|
.pipe(gulp.dest(target));
|
||||||
|
|
||||||
|
gulp.task('html', html('./build'))
|
||||||
|
gulp.task('img', img('./build/img'))
|
||||||
|
gulp.task('audio', audio('./build/audio'))
|
||||||
|
|
||||||
|
gulp.task('sass', () ->
|
||||||
|
gulp.src('sass/main.scss')
|
||||||
|
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
|
||||||
|
.pipe(gulp.dest('./build/css'));
|
||||||
|
)
|
||||||
|
|
||||||
|
sources = [
|
||||||
|
'./game/begin.coffee',
|
||||||
|
'./game/story.coffee',
|
||||||
|
'./game/end.coffee',
|
||||||
|
]
|
||||||
|
|
||||||
|
opts = _.assign({}, watchify.args, {
|
||||||
|
entries: ["./build/game/main.coffee"]
|
||||||
|
debug: true
|
||||||
|
transform: [coffeify]
|
||||||
|
});
|
||||||
|
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'))
|
||||||
|
|
||||||
|
gulp.task('concatCoffee', () ->
|
||||||
|
return gulp.src(sources)
|
||||||
|
.pipe(concat('./main.coffee'))
|
||||||
|
.pipe(gulp.dest('./build/game'))
|
||||||
|
)
|
||||||
|
|
||||||
|
gulp.task('coffee', ['concatCoffee'], bundle)
|
||||||
|
|
||||||
|
bundler.on('update', bundle);
|
||||||
|
bundler.on('log', gutil.log);
|
||||||
|
|
||||||
|
gulp.task('build', ['html', 'img', 'sass', 'coffee', 'audio'])
|
||||||
|
|
||||||
|
gulp.task('serve', ['build'], () ->
|
||||||
|
browserSync({
|
||||||
|
server: {
|
||||||
|
baseDir: 'build'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
sassListener = () ->
|
||||||
|
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(['./lib/*.coffee', './lib/*.js', './game/*.coffee'], ['coffee'])
|
||||||
|
|
||||||
|
gulp.watch(['./build/css/main.css'], sassListener)
|
||||||
|
gulp.watch(
|
||||||
|
['./build/game/bundle.js', './build/img/*', './build/index.html'],
|
||||||
|
browserSync.reload)
|
||||||
|
)
|
||||||
|
|
||||||
|
gulp.task('html-dist', html('./dist'));
|
||||||
|
gulp.task('img-dist', img('./dist/img'));
|
||||||
|
gulp.task('audio-dist', audio('./dist/audio'));
|
||||||
|
gulp.task('legal-dist', () ->
|
||||||
|
return gulp.src(['LICENSE.txt'])
|
||||||
|
.pipe(gulp.dest("./dist"));
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task('sass-dist', () ->
|
||||||
|
return gulp.src('./sass/main.scss')
|
||||||
|
.pipe(sass({outputStyle: 'compressed'}))
|
||||||
|
.pipe(gulp.dest('./dist/css'));
|
||||||
|
);
|
||||||
|
|
||||||
|
distBundler = browserify({
|
||||||
|
debug: false,
|
||||||
|
entries: ['./build/game/main.coffee'],
|
||||||
|
transform: ['coffeeify']
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('coffee-dist', ['concatCoffee'], () ->
|
||||||
|
return distBundler.bundle()
|
||||||
|
.pipe(source('bundle.js'))
|
||||||
|
.pipe(buffer())
|
||||||
|
.pipe(uglify())
|
||||||
|
.on('error', gutil.log)
|
||||||
|
.pipe(gulp.dest('./dist/game'));
|
||||||
|
);
|
||||||
|
|
||||||
|
gulp.task('dist', ['html-dist', 'img-dist', 'sass-dist', 'coffee-dist', 'audio-dist', 'legal-dist']);
|
||||||
|
|
||||||
|
gulp.task('zip', ['dist'], () ->
|
||||||
|
return gulp.src('dist/**')
|
||||||
|
.pipe(zip('dist.zip'))
|
||||||
|
.pipe(gulp.dest('.'));
|
||||||
|
);
|
184
Gulpfile.js
184
Gulpfile.js
|
@ -1,184 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/* Imports */
|
|
||||||
var watchify = require('watchify'),
|
|
||||||
browserify = require('browserify'),
|
|
||||||
browserSync = require('browser-sync'),
|
|
||||||
gulp = require('gulp'),
|
|
||||||
source = require('vinyl-source-stream'),
|
|
||||||
gutil = require('gulp-util'),
|
|
||||||
coffeify = require('coffeeify'),
|
|
||||||
sass = require('gulp-sass'),
|
|
||||||
minifyCSS = require('gulp-minify-css'),
|
|
||||||
uglify = require('gulp-uglify'),
|
|
||||||
buffer = require('vinyl-buffer'),
|
|
||||||
zip = require('gulp-zip'),
|
|
||||||
_ = require('lodash'),
|
|
||||||
concat = require('gulp-concat');
|
|
||||||
|
|
||||||
var reload = browserSync.reload;
|
|
||||||
|
|
||||||
/* Tasks */
|
|
||||||
|
|
||||||
/* Trivial file copies */
|
|
||||||
|
|
||||||
function html (target) {
|
|
||||||
return function () {
|
|
||||||
return gulp.src(['html/index.html','html/en.html'])
|
|
||||||
.pipe(gulp.dest(target));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function img (target) {
|
|
||||||
return function () {
|
|
||||||
return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg'])
|
|
||||||
.pipe(gulp.dest(target));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function audio (target) {
|
|
||||||
return function () {
|
|
||||||
return gulp.src(['audio/*.mp3'])
|
|
||||||
.pipe(gulp.dest(target));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('html', html('./build'));
|
|
||||||
gulp.task('img', img('./build/img'));
|
|
||||||
gulp.task('audio', audio('./build/audio'));
|
|
||||||
|
|
||||||
/* Less */
|
|
||||||
|
|
||||||
gulp.task('sass', function () {
|
|
||||||
gulp.src('sass/main.scss')
|
|
||||||
.pipe(sass().on('error', sass.logError))
|
|
||||||
.pipe(gulp.dest('./build/css'));
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Bundle libraries */
|
|
||||||
|
|
||||||
var undumBundler = browserify({debug: true});
|
|
||||||
undumBundler.require('undum-commonjs');
|
|
||||||
|
|
||||||
gulp.task('buildUndum', function () {
|
|
||||||
return undumBundler.bundle().pipe(source('undum.js')).pipe(gulp.dest('./build/game'));
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Generate JavaScript with browser sync. */
|
|
||||||
|
|
||||||
var sources = [
|
|
||||||
'./game/begin.coffee',
|
|
||||||
'./game/story.coffee',
|
|
||||||
'./game/end.coffee',
|
|
||||||
]
|
|
||||||
|
|
||||||
var opts = _.assign({}, watchify.args, {
|
|
||||||
entries: ["./build/game/main.coffee"],
|
|
||||||
debug: true,
|
|
||||||
transform: [coffeify]
|
|
||||||
});
|
|
||||||
var bundler = watchify(browserify(opts));
|
|
||||||
bundler.external('undum-commonjs');
|
|
||||||
|
|
||||||
function bundle () {
|
|
||||||
return bundler.bundle()
|
|
||||||
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
|
|
||||||
.pipe(source('bundle.js'))
|
|
||||||
.pipe(gulp.dest('./build/game'));
|
|
||||||
};
|
|
||||||
|
|
||||||
gulp.task('concatCoffee', function() {
|
|
||||||
return gulp.src(sources)
|
|
||||||
.pipe(concat('./main.coffee'))
|
|
||||||
.pipe(gulp.dest('./build/game'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// `gulp coffee` will generate bundle
|
|
||||||
gulp.task('coffee', ['buildUndum', 'concatCoffee'], bundle);
|
|
||||||
|
|
||||||
bundler.on('update', bundle); // Re-bundle on dep updates
|
|
||||||
bundler.on('log', gutil.log); // Output build logs to terminal
|
|
||||||
|
|
||||||
/* Make a development build */
|
|
||||||
|
|
||||||
gulp.task('build', ['html', 'img', 'sass', 'coffee', 'audio'], function () {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Start a development server */
|
|
||||||
|
|
||||||
gulp.task('serve', ['build'], function () {
|
|
||||||
browserSync({
|
|
||||||
server: {
|
|
||||||
baseDir: 'build'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var sassListener = function () {
|
|
||||||
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(['./build/css/main.css'], sassListener);
|
|
||||||
gulp.watch(
|
|
||||||
['./build/game/bundle.js', './build/img/*', './build/index.html'],
|
|
||||||
browserSync.reload);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Distribution tasks */
|
|
||||||
|
|
||||||
var undumDistBundler = browserify();
|
|
||||||
undumDistBundler.require('undum-commonjs');
|
|
||||||
|
|
||||||
gulp.task('undum-dist', function () {
|
|
||||||
return undumDistBundler.bundle().pipe(source('undum.js'))
|
|
||||||
.pipe(buffer())
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(gulp.dest('./dist/game'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('html-dist', html('./dist'));
|
|
||||||
gulp.task('img-dist', img('./dist/img'));
|
|
||||||
gulp.task('audio-dist', audio('./dist/audio'));
|
|
||||||
gulp.task('legal-dist', function () {
|
|
||||||
return gulp.src(['LICENSE.txt'])
|
|
||||||
.pipe(gulp.dest("./dist"));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('sass-dist', function () {
|
|
||||||
return gulp.src('./sass/main.scss')
|
|
||||||
.pipe(sass({outputStyle: 'compressed'}))
|
|
||||||
.pipe(gulp.dest('./dist/css'));
|
|
||||||
});
|
|
||||||
|
|
||||||
var distBundler = browserify({
|
|
||||||
debug: false,
|
|
||||||
entries: ['./build/game/main.coffee'],
|
|
||||||
transform: ['coffeeify']
|
|
||||||
});
|
|
||||||
|
|
||||||
distBundler.external('undum-commonjs');
|
|
||||||
|
|
||||||
gulp.task('coffee-dist', ['undum-dist', 'concatCoffee'], function () {
|
|
||||||
return distBundler.bundle()
|
|
||||||
.pipe(source('bundle.js'))
|
|
||||||
.pipe(buffer())
|
|
||||||
.pipe(uglify())
|
|
||||||
.on('error', gutil.log)
|
|
||||||
.pipe(gulp.dest('./dist/game'));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('dist', ['html-dist', 'img-dist', 'sass-dist', 'coffee-dist', 'audio-dist', 'legal-dist'],
|
|
||||||
function () {
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('zip', ['dist'], function () {
|
|
||||||
return gulp.src('dist/**')
|
|
||||||
.pipe(zip('dist.zip'))
|
|
||||||
.pipe(gulp.dest('.'));
|
|
||||||
});
|
|
|
@ -1,4 +1,6 @@
|
||||||
Copyright (c) 2015-2016 Alexander Yakovlev, https://oreolek.ru/
|
Copyright (c) 2016 Alexander Yakovlev, https://oreolek.ru/
|
||||||
|
Raconteur is copyright (c) 2015 Bruno Dias, released under the similar license terms.
|
||||||
|
Undum is copyright (c) 2009-2015 Ian Millington, released under the similar license terms.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|
|
@ -2,54 +2,32 @@
|
||||||
# This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
|
# This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||||
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0
|
# To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0
|
||||||
|
|
||||||
situation = require('raconteur')
|
markdown = require('../../lib/markdown.coffee')
|
||||||
undum = require('undum-commonjs')
|
room = require("../../lib/room.coffee")
|
||||||
oneOf = require('raconteur/lib/oneOf.js')
|
obj = require('../../lib/obj.coffee')
|
||||||
qualities = require('raconteur/lib/qualities.js')
|
dialogue = require('../../lib/dialogue.coffee')
|
||||||
$ = require("jquery")
|
oneOf = require('../../lib/oneOf.coffee')
|
||||||
|
require('../../lib/interface.coffee')
|
||||||
Array.prototype.oneOf = () ->
|
undum = require('../../lib/undum.js')
|
||||||
oneOf.apply(null, this)
|
|
||||||
|
|
||||||
md = require('markdown-it')
|
|
||||||
markdown = new md({
|
|
||||||
typographer: true,
|
|
||||||
html: true
|
|
||||||
})
|
|
||||||
|
|
||||||
shortid = require('shortid')
|
|
||||||
# you have to alter linkRe in Undum core to use that.
|
|
||||||
# Undum doesn't allow using uppercase letters in situation names by default.
|
|
||||||
|
|
||||||
undum.game.id = "6a9909a4-586a-4089-bd18-26da684d1c8d"
|
undum.game.id = "6a9909a4-586a-4089-bd18-26da684d1c8d"
|
||||||
undum.game.version = "1.0"
|
undum.game.version = "2.0"
|
||||||
|
|
||||||
|
way_to = (content, ref) ->
|
||||||
|
return "<a href='#{ref}' class='way'>#{content}</a>"
|
||||||
|
textlink = (content, ref) ->
|
||||||
|
return "<a href='./_writer_#{ref}' class='once'>#{content}</a>"
|
||||||
|
actlink = (content, ref) ->
|
||||||
|
return "<a href='./#{ref}' class='once'>#{content}</a>"
|
||||||
|
textcycle = (content, ref) ->
|
||||||
|
return "<a href='./_replacer_#{ref}' class='cycle' id='#{ref}'>#{content}</a>"# usage: writemd( system, "Text to write")
|
||||||
|
|
||||||
a = require('raconteur/lib/elements.js').a
|
|
||||||
way_to = (content, ref) -> a(content).class('way').ref(ref)
|
|
||||||
textlink = (content, ref) -> a(content).once().writer(ref)
|
|
||||||
actlink = (content, ref) -> a(content).once().action(ref)
|
|
||||||
textcycle = (content, ref) -> a(content).replacer(ref).class("cycle").id(ref).toString()
|
|
||||||
# usage: writemd( system, "Text to write")
|
|
||||||
writemd = (system, text) ->
|
writemd = (system, text) ->
|
||||||
if typeof text is Function
|
text = markdown(text)
|
||||||
text = text()
|
|
||||||
text = markdown.render(text)
|
|
||||||
system.write(text)
|
system.write(text)
|
||||||
|
|
||||||
preparemd = (text, mark) ->
|
money = (character, amount) ->
|
||||||
if typeof text is Function
|
character.sandbox.money = character.sandbox.money + amount
|
||||||
text = text()
|
|
||||||
text = markdown.render(text)
|
|
||||||
if mark?
|
|
||||||
text = """
|
|
||||||
<div class="#{mark}">
|
|
||||||
#{text}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
return text
|
|
||||||
|
|
||||||
money = (character, system, amount) ->
|
|
||||||
system.setQuality("money", character.qualities.money + amount)
|
|
||||||
|
|
||||||
code_can_input = (character) ->
|
code_can_input = (character) ->
|
||||||
return character.sandbox.code.length < 8
|
return character.sandbox.code.length < 8
|
||||||
|
@ -108,40 +86,12 @@ code_check = (character, system) ->
|
||||||
|
|
||||||
character.sandbox.code = ""
|
character.sandbox.code = ""
|
||||||
|
|
||||||
update_ways = (ways) ->
|
room "start",
|
||||||
content = ""
|
dsc: """
|
||||||
for way in ways
|
|
||||||
if undum.game.situations[way]?
|
|
||||||
content += way_to(undum.game.situations[way].title, way)
|
|
||||||
$("#ways").html(content)
|
|
||||||
|
|
||||||
situation "start",
|
|
||||||
content: """
|
|
||||||
Peter had so much trouble sleeping he had to drown his pills in at least an hour of thoughts.
|
|
||||||
|
|
||||||
A violent ringing of the bell awakened him.
|
|
||||||
He rose from the bed, grumbling:
|
|
||||||
“Crazy neighbors and their guests. It must be three o'clock!”
|
|
||||||
|
|
||||||
The visitor entered the hallway.
|
|
||||||
It was him ringing the bell, but he was not going to meet Peter.
|
|
||||||
In fact, he wasn't looking for meeting anybody here.
|
|
||||||
|
|
||||||
Fourth floor, apartment 406.
|
|
||||||
There, he tried two keys.
|
|
||||||
The second of them fitted the lock.
|
|
||||||
|
|
||||||
Burglary is a curious line of employment.
|
|
||||||
Befittedly, Ronald Chernoff was very curious about a black phone behind the door of apartment 406 in a wooden box on a small table no farther than two meters from the bed.
|
|
||||||
A gift, a prototype, a valuable treasure left by Anastasia Kozlowa when she fled the country.
|
|
||||||
Of course, one had to be reasonably au fait with her *Instagram* to notice that.
|
|
||||||
|
|
||||||
Peter opened his door to find an empty silent corridor.
|
Peter opened his door to find an empty silent corridor.
|
||||||
He went to the neighbor's door and met a closed door.
|
He went to the neighbor's door and met a closed door.
|
||||||
Ronald was working inside, quietly walking around the apartment.
|
Ronald was working inside, quietly walking around the apartment.
|
||||||
He began the inspection from [the living room.](living-room)
|
He began the inspection from #{way_to('the living room.', 'living-room')}
|
||||||
|
|
||||||
<hr>
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
is_visited = (situation) ->
|
is_visited = (situation) ->
|
||||||
|
@ -153,7 +103,7 @@ is_visited = (situation) ->
|
||||||
# N-th level examine function
|
# N-th level examine function
|
||||||
level = (text, mark) ->
|
level = (text, mark) ->
|
||||||
$("#content .#{mark}").fadeOut()
|
$("#content .#{mark}").fadeOut()
|
||||||
return preparemd(text, mark)
|
return markdown(text, mark)
|
||||||
|
|
||||||
lvl1 = (text) ->
|
lvl1 = (text) ->
|
||||||
$("#content .lvl2").fadeOut()
|
$("#content .lvl2").fadeOut()
|
||||||
|
|
|
@ -1,24 +1,8 @@
|
||||||
qualities
|
|
||||||
general:
|
|
||||||
money: qualities.integer('Money'),
|
|
||||||
|
|
||||||
undum.game.init = (character, system) ->
|
undum.game.init = (character, system) ->
|
||||||
$("#ways").on("click", "a", (event) ->
|
|
||||||
event.preventDefault()
|
|
||||||
undum.processClick($(this).attr("href"))
|
|
||||||
)
|
|
||||||
_paq.push(['setCustomDimension', 1, false])
|
_paq.push(['setCustomDimension', 1, false])
|
||||||
# If you use only once() links you can use this "hack":
|
|
||||||
document.onmousedown = (e) ->
|
|
||||||
e.target.click()
|
|
||||||
# It makes every click slightly faster because the game responds after the user presses the mouse button,
|
|
||||||
# not after he presses and releases it.
|
|
||||||
#
|
|
||||||
# Another thing to bear in mind: this game is not a typical Undum game.
|
|
||||||
# It deliberately repeats the situations, so you can repeat "once" links once you re-enter the situation.
|
|
||||||
# So this game has no need in repeatable links at all and this hack is useful.
|
|
||||||
|
|
||||||
character.sandbox.view_smash = 1
|
character.sandbox.view_smash = 1
|
||||||
|
character.sandbox.money = 0
|
||||||
character.sandbox.code = ""
|
character.sandbox.code = ""
|
||||||
character.sandbox.knows_the_code = 0
|
character.sandbox.knows_the_code = 0
|
||||||
character.sandbox.box_opened = 0
|
character.sandbox.box_opened = 0
|
||||||
|
|
|
@ -1,44 +1,40 @@
|
||||||
situation "living-room",
|
room "living-room",
|
||||||
title: "Living room"
|
title: "Living room"
|
||||||
before: () ->
|
enter: (character, system, from) ->
|
||||||
if not $(".ways h2").is(':visible')
|
if (from == "start")
|
||||||
$(".ways h2").fadeIn()
|
audio = document.getElementById("bgsound")
|
||||||
update_ways(this.ways)
|
audio.currentTime=0
|
||||||
audio = document.getElementById("bgsound")
|
audio.volume = 0.5
|
||||||
audio.currentTime=0
|
audio.play()
|
||||||
audio.volume = 0.5
|
|
||||||
audio.play()
|
|
||||||
ways: ["bedroom", "kitchen", "balcony"]
|
ways: ["bedroom", "kitchen", "balcony"]
|
||||||
content: """
|
dsc: """
|
||||||
Ronald is standing in a dark room with a big #{textlink("window.", "window")}
|
|
||||||
|
|
||||||
#{textlink("The walls", "walls")} are covered with a dingy rose wallpaper.
|
|
||||||
|
|
||||||
#{textlink("A book stand", "bookcase")} is hanging above #{textlink("a television set.", "tv")}
|
#{textlink("A book stand", "bookcase")} is hanging above #{textlink("a television set.", "tv")}
|
||||||
|
|
||||||
Oh, and #{actlink("the door Ronald came into", "door")} the apartment is there, too.
|
|
||||||
"""
|
"""
|
||||||
actions:
|
objects:
|
||||||
door: (character, system) ->
|
window: obj "window",
|
||||||
if character.sandbox.box_opened == 0
|
act: """
|
||||||
writemd(system, lvl1("Ronald has a job here. It's still on."))
|
The moon is full today.
|
||||||
else
|
It illuminates the apartment, makes the things stand out in some weird angles.
|
||||||
system.doLink("exitdoor")
|
"""
|
||||||
writers:
|
dsc: "Ronald is standing in a dark room with a big {{window}}"
|
||||||
walls: (character, system) ->
|
walls: obj "walls",
|
||||||
lvl1("""
|
dsc: "{{The walls}} are covered with a dingy rose wallpaper."
|
||||||
|
act: """
|
||||||
There are colorful photographs on the walls.
|
There are colorful photographs on the walls.
|
||||||
A wooden house in a forest.
|
A wooden house in a forest.
|
||||||
A village on a mountaintop.
|
A village on a mountaintop.
|
||||||
A family sitting around a fire.
|
A family sitting around a fire.
|
||||||
A sunset burning in a vast ocean.
|
A sunset burning in a vast ocean.
|
||||||
A black monolith standing on sand dunes.
|
A black monolith standing on sand dunes.
|
||||||
""")
|
"""
|
||||||
window: (character, system) ->
|
door: obj "door",
|
||||||
lvl1("""
|
dsc: "Oh, and {{the door Ronald came into}} the apartment is there, too."
|
||||||
The moon is full today.
|
act: (character, system) ->
|
||||||
It illuminates the apartment, makes the things stand out in some weird angles.
|
if character.sandbox.box_opened == 0
|
||||||
""")
|
writemd(system, lvl1("Ronald has a job here. It's still on."))
|
||||||
|
else
|
||||||
|
system.doLink("exitdoor")
|
||||||
|
writers:
|
||||||
bookcase: (character, system) ->
|
bookcase: (character, system) ->
|
||||||
lvl1("""
|
lvl1("""
|
||||||
Either Anastasia has a very conflicting taste in books, or she has no taste at all. Let's see...
|
Either Anastasia has a very conflicting taste in books, or she has no taste at all. Let's see...
|
||||||
|
@ -88,7 +84,7 @@ situation "living-room",
|
||||||
No need to read it, not a bit.
|
No need to read it, not a bit.
|
||||||
""")
|
""")
|
||||||
else
|
else
|
||||||
money(character, system, 20000)
|
money(character, 20000)
|
||||||
lvl2("""
|
lvl2("""
|
||||||
Nietsche's four-part novel about The Man, The Superman and everything in-between.
|
Nietsche's four-part novel about The Man, The Superman and everything in-between.
|
||||||
It's surprisingly worn down.
|
It's surprisingly worn down.
|
||||||
|
@ -117,12 +113,10 @@ situation "living-room",
|
||||||
An expensive 40-something inch TV standing on a stylish black stand. The room looks kinda small for that monster.
|
An expensive 40-something inch TV standing on a stylish black stand. The room looks kinda small for that monster.
|
||||||
""")
|
""")
|
||||||
|
|
||||||
situation "bedroom",
|
room "bedroom",
|
||||||
before: () ->
|
|
||||||
update_ways(this.ways)
|
|
||||||
title: "Bedroom"
|
title: "Bedroom"
|
||||||
ways: ["living-room", "kitchen", "bathroom"]
|
ways: ["living-room", "kitchen", "bathroom"]
|
||||||
content: (character, system) ->
|
dsc: (character, system) ->
|
||||||
return """
|
return """
|
||||||
The bedroom is spacious; its walls are lavender green, almost white in the moonlight.
|
The bedroom is spacious; its walls are lavender green, almost white in the moonlight.
|
||||||
|
|
||||||
|
@ -188,7 +182,7 @@ situation "bedroom",
|
||||||
""")
|
""")
|
||||||
else
|
else
|
||||||
character.sandbox.seen_coat = 1
|
character.sandbox.seen_coat = 1
|
||||||
money(character, system, 4000)
|
money(character, 4000)
|
||||||
return lvl2("""
|
return lvl2("""
|
||||||
A warm coat.. hey, what's this?
|
A warm coat.. hey, what's this?
|
||||||
One of the pockets is loaded with cash!
|
One of the pockets is loaded with cash!
|
||||||
|
@ -264,7 +258,7 @@ situation "bedroom",
|
||||||
""")
|
""")
|
||||||
money: (character, system) ->
|
money: (character, system) ->
|
||||||
character.sandbox.seen_safe = 1
|
character.sandbox.seen_safe = 1
|
||||||
money(character, system, 50000)
|
money(character, 50000)
|
||||||
lvl4("""
|
lvl4("""
|
||||||
It's a big cash.
|
It's a big cash.
|
||||||
Odd that she didn't take this when she left.
|
Odd that she didn't take this when she left.
|
||||||
|
@ -278,12 +272,10 @@ situation "bedroom",
|
||||||
The sketch is signed: *"L. Y. - 2017"*
|
The sketch is signed: *"L. Y. - 2017"*
|
||||||
""")
|
""")
|
||||||
|
|
||||||
situation "kitchen",
|
room "kitchen",
|
||||||
before: () ->
|
|
||||||
update_ways(this.ways)
|
|
||||||
title: "Kitchen"
|
title: "Kitchen"
|
||||||
ways: ["living-room", "bedroom"]
|
ways: ["living-room", "bedroom"]
|
||||||
content: """
|
dsc: """
|
||||||
The white, perfectly clean kitchen could be called spartan: #{textlink("a fridge,", "fridge")} a microwave and #{textlink("a big table", "table")} where one can eat whatever she "cooked" that way.
|
The white, perfectly clean kitchen could be called spartan: #{textlink("a fridge,", "fridge")} a microwave and #{textlink("a big table", "table")} where one can eat whatever she "cooked" that way.
|
||||||
"""
|
"""
|
||||||
writers:
|
writers:
|
||||||
|
@ -318,22 +310,19 @@ situation "kitchen",
|
||||||
He's sure it's recent (`24.03.2018`) and it's about something-something QUANTUM AUDIO.. armement?
|
He's sure it's recent (`24.03.2018`) and it's about something-something QUANTUM AUDIO.. armement?
|
||||||
""")
|
""")
|
||||||
|
|
||||||
situation "bathroom",
|
room "bathroom",
|
||||||
before: (character,system) ->
|
before: (character,system) ->
|
||||||
writemd(system,"Ronald doesn't want to search the bathroom. It's too private a room to enter.")
|
writemd(system,"Ronald doesn't want to search the bathroom. It's too private a room to enter.")
|
||||||
index = undum.game.situations["bedroom"].ways.indexOf("bathroom")
|
index = undum.game.situations["bedroom"].ways.indexOf("bathroom")
|
||||||
undum.game.situations["bedroom"].ways.splice(index, 1)
|
undum.game.situations["bedroom"].ways.splice(index, 1)
|
||||||
update_ways(undum.game.situations["bedroom"].ways)
|
|
||||||
return false
|
return false
|
||||||
title: "Bathroom"
|
title: "Bathroom"
|
||||||
ways: ["bedroom"]
|
ways: ["bedroom"]
|
||||||
|
|
||||||
situation "balcony",
|
room "balcony",
|
||||||
before: () ->
|
|
||||||
update_ways(this.ways)
|
|
||||||
title: "Balcony"
|
title: "Balcony"
|
||||||
ways: ["living-room"]
|
ways: ["living-room"]
|
||||||
content: """
|
dsc: """
|
||||||
A small glazed-in empty balcony.
|
A small glazed-in empty balcony.
|
||||||
It's an amazing night.
|
It's an amazing night.
|
||||||
The whole town is lit by moonlight, standing perfectly still.
|
The whole town is lit by moonlight, standing perfectly still.
|
||||||
|
@ -359,12 +348,10 @@ situation "balcony",
|
||||||
*L. Y.*
|
*L. Y.*
|
||||||
""")
|
""")
|
||||||
|
|
||||||
situation "box",
|
room "box",
|
||||||
before: () ->
|
|
||||||
update_ways(this.ways)
|
|
||||||
ways: ["bedroom"]
|
ways: ["bedroom"]
|
||||||
choices: "#box"
|
choices: "#box"
|
||||||
content: (character, system) ->
|
dsc: (character, system) ->
|
||||||
return """
|
return """
|
||||||
It's a red wood, very expensive.
|
It's a red wood, very expensive.
|
||||||
And this box is locked with a digital code key.
|
And this box is locked with a digital code key.
|
||||||
|
@ -379,8 +366,7 @@ situation "box",
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# no need to call update_ways, it's the same location
|
room "smash",
|
||||||
situation "smash",
|
|
||||||
canView: (character) ->
|
canView: (character) ->
|
||||||
character.sandbox.view_smash == 1
|
character.sandbox.view_smash == 1
|
||||||
optionText: "Smash the box"
|
optionText: "Smash the box"
|
||||||
|
@ -388,10 +374,10 @@ situation "smash",
|
||||||
character.sandbox.view_smash = 0
|
character.sandbox.view_smash = 0
|
||||||
choices: "#box"
|
choices: "#box"
|
||||||
tags: ["box"]
|
tags: ["box"]
|
||||||
content: "Ronald still needs the phone in this box. A very high-tech fragile phone. Smashing isn't an option."
|
dsc: "Ronald still needs the phone in this box. A very high-tech fragile phone. Smashing isn't an option."
|
||||||
|
|
||||||
safe_button = (number) ->
|
safe_button = (number) ->
|
||||||
situation "put#{number}",
|
room "put#{number}",
|
||||||
choices: "#box"
|
choices: "#box"
|
||||||
tags: ["box"]
|
tags: ["box"]
|
||||||
optionText: "Enter #{number}"
|
optionText: "Enter #{number}"
|
||||||
|
@ -401,7 +387,7 @@ safe_button = (number) ->
|
||||||
code_can_input(character)
|
code_can_input(character)
|
||||||
after: (character, system) ->
|
after: (character, system) ->
|
||||||
code_check(character, system)
|
code_check(character, system)
|
||||||
content: (character) -> """
|
dsc: (character) -> """
|
||||||
Ronald presses button #{number}. The display is #{code_print(character)} now.
|
Ronald presses button #{number}. The display is #{code_print(character)} now.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -410,22 +396,20 @@ safe_button(2)
|
||||||
safe_button(7)
|
safe_button(7)
|
||||||
safe_button(0)
|
safe_button(0)
|
||||||
|
|
||||||
situation "reset",
|
room "reset",
|
||||||
choices: "#box"
|
choices: "#box"
|
||||||
tags: ["box"]
|
tags: ["box"]
|
||||||
optionText: "Reset the display"
|
optionText: "Reset the display"
|
||||||
before: (character) ->
|
before: (character) ->
|
||||||
code_reset(character)
|
code_reset(character)
|
||||||
content: """
|
dsc: """
|
||||||
Ronald presses Backspace until the display is empty.
|
Ronald presses Backspace until the display is empty.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
situation "exitdoor",
|
room "exitdoor",
|
||||||
before: () ->
|
|
||||||
update_ways(this.ways)
|
|
||||||
ways: ["living-room"]
|
ways: ["living-room"]
|
||||||
choices: "#door"
|
choices: "#door"
|
||||||
content: """
|
dsc: """
|
||||||
Ronald is ready to go.
|
Ronald is ready to go.
|
||||||
Maybe he's satisfied with his explorations or just wants to finish this.
|
Maybe he's satisfied with his explorations or just wants to finish this.
|
||||||
But then a new problem arrives.
|
But then a new problem arrives.
|
||||||
|
@ -433,15 +417,14 @@ situation "exitdoor",
|
||||||
Someone's shadow is under the doorframe.
|
Someone's shadow is under the doorframe.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
situation "finale",
|
room "finale",
|
||||||
before: () ->
|
before: () ->
|
||||||
_paq.push(['setCustomDimension', 1, true])
|
_paq.push(['setCustomDimension', 1, true])
|
||||||
$("#tools_wrapper").hide()
|
$("#tools_wrapper").hide()
|
||||||
update_ways(this.ways)
|
|
||||||
optionText: "Use the Phone"
|
optionText: "Use the Phone"
|
||||||
tags: ["door"]
|
tags: ["door"]
|
||||||
ways: []
|
ways: []
|
||||||
content: (character, system) -> """
|
dsc: (character, system) -> """
|
||||||
"LOADING... 100%"
|
"LOADING... 100%"
|
||||||
|
|
||||||
Ronald opens the door and presses his finger to the phone screen.
|
Ronald opens the door and presses his finger to the phone screen.
|
||||||
|
@ -460,8 +443,8 @@ situation "finale",
|
||||||
|
|
||||||
“Well, that was a good night.”
|
“Well, that was a good night.”
|
||||||
|
|
||||||
#{if character.qualities.money > 0
|
#{if character.sandbox.money > 0
|
||||||
"The pocket is heavy with #{character.qualities.money} rubles and the phone."
|
"The pocket is heavy with #{character.sandbox.money} rubles and the phone."
|
||||||
else
|
else
|
||||||
"The phone is heavy in the pocket."
|
"The phone is heavy in the pocket."
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,37 @@
|
||||||
<noscript>
|
<noscript>
|
||||||
<p class="noscript_message">This game requires Javascript.</p>
|
<p class="noscript_message">This game requires Javascript.</p>
|
||||||
</noscript>
|
</noscript>
|
||||||
<p class="click_message">click to begin</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="content_wrapper" class="row">
|
<div id="content_wrapper" class="row">
|
||||||
<div id="content">
|
<div id="intro" class="content">
|
||||||
|
<section>
|
||||||
|
<p>Peter had so much trouble sleeping he had to drown his pills in at least an hour of thoughts.</p>
|
||||||
|
|
||||||
|
<p>A violent ringing of the bell awakened him.
|
||||||
|
He rose from the bed, grumbling:
|
||||||
|
“Crazy neighbors and their guests. It must be three o'clock!”</p>
|
||||||
|
|
||||||
|
<p>The visitor entered the hallway.
|
||||||
|
It was him ringing the bell, but he was not going to meet Peter.
|
||||||
|
In fact, he wasn't looking for meeting anybody here.</p>
|
||||||
|
|
||||||
|
<p>Fourth floor, apartment 406.
|
||||||
|
There, he tried two keys.
|
||||||
|
The second of them fitted the lock.</p>
|
||||||
|
|
||||||
|
<p>Burglary is a curious line of employment.
|
||||||
|
Befittedly, Ronald Chernoff was very curious about a black phone behind the door of apartment 406 in a wooden box on a small table no farther than two meters from the bed.
|
||||||
|
A gift, a prototype, a valuable treasure left by Anastasia Kozlowa when she fled the country.
|
||||||
|
Of course, one had to be reasonably au fait with her <em>Instagram</em> to notice that.</p>
|
||||||
|
<noscript>You need to turn on Javascript to play this game.</noscript>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="content" class="content"></div>
|
||||||
<a name="end_of_content"></a>
|
<a name="end_of_content"></a>
|
||||||
</div>
|
</div>
|
||||||
<div id="tools_wrapper" class="row">
|
<div id="tools_wrapper" class="row">
|
||||||
<div id="character_panel" class="tools">
|
|
||||||
<div id="character">
|
|
||||||
<div id="character_text">
|
|
||||||
<div id="character_text_content"></div>
|
|
||||||
</div>
|
|
||||||
<!-- <div id="qualities"></div> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='ways'>
|
<div class='ways'>
|
||||||
<h2>Other rooms</h2>
|
<h2>Other rooms</h2>
|
||||||
<div id="ways"></div>
|
<div id="ways"></div>
|
||||||
|
@ -46,9 +59,8 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="legal">
|
<div id="legal">
|
||||||
<div id="footleft">
|
<div id="footleft">
|
||||||
<p>The game was written by <b><a href="http://en.oreolek.ru/" target="_blank">Oreolek.</a></b></p>
|
<p>The game was written by <a href="http://en.oreolek.ru/" target="_blank">Oreolek</a> using <a href="http://git.oreolek.ru/oreolek/salet" target="_blank">Salet.</a></p>
|
||||||
<p>Approximate play time: five minutes.</p>
|
<p>Approximate play time: five minutes.</p>
|
||||||
<p>Written using <a href="http://undum.com" target="_blank">Undum</a> and <a href="http://sequitur.github.io/raconteur/" target="_blank">Raconteur</a>.</p>
|
|
||||||
<p>Betatesting credit: <a href="https://verityvirtue.wordpress.com/">Verity Virtue</a></p>
|
<p>Betatesting credit: <a href="https://verityvirtue.wordpress.com/">Verity Virtue</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div id="footright">
|
<div id="footright">
|
||||||
|
@ -75,7 +87,10 @@
|
||||||
|
|
||||||
<div id="content_library"></div>
|
<div id="content_library"></div>
|
||||||
<audio id="bgsound" preload="auto"><source src="audio/bgr.mp3" type='audio/mpeg; codecs="mp3"'></audio>
|
<audio id="bgsound" preload="auto"><source src="audio/bgr.mp3" type='audio/mpeg; codecs="mp3"'></audio>
|
||||||
<script type="text/javascript" src="game/undum.js"></script>
|
<!-- CDN JS Libraries -->
|
||||||
|
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js"></script>
|
||||||
|
<script type="text/javascript" src="//code.jquery.com/jquery-2.2.0.min.js"></script>
|
||||||
|
<!-- Game code -->
|
||||||
<script type="text/javascript" src="game/bundle.js"></script>
|
<script type="text/javascript" src="game/bundle.js"></script>
|
||||||
<!-- Piwik -->
|
<!-- Piwik -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
19
lib/cycle.coffee
Normal file
19
lib/cycle.coffee
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Cycling interface.
|
||||||
|
# Rooms: cycle through this.cycle_gallery
|
||||||
|
# Objects: cycle through this.cycle_gallery
|
||||||
|
|
||||||
|
cyclelink = (content) ->
|
||||||
|
return "<a href='./_replacer_cyclewriter' class='cycle' id='cyclewriter'>#{content}</a>"
|
||||||
|
|
||||||
|
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
|
34
lib/dialogue.coffee
Normal file
34
lib/dialogue.coffee
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
room = require("./room.coffee")
|
||||||
|
|
||||||
|
randomid = () ->
|
||||||
|
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" # see the dreaded linkRe expression in Undum
|
||||||
|
rndstr = []
|
||||||
|
for i in [1..10]
|
||||||
|
rndstr.push alphabet.charAt(Math.floor(Math.random() * alphabet.length))
|
||||||
|
return rndstr.join('').toString()
|
||||||
|
|
||||||
|
###
|
||||||
|
A dialogue shortcut.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
dialogue "Point out a thing in her purse (mildly)", "start", "mild", """
|
||||||
|
Point out a thing in her purse (mildly)
|
||||||
|
""", "character.sandbox.mild = true"
|
||||||
|
###
|
||||||
|
dialogue = (title, startTag, endTag, text, effect) ->
|
||||||
|
retval = room(randomid(), {
|
||||||
|
optionText: title
|
||||||
|
dsc: text
|
||||||
|
clear: false # backlog is useful in dialogues
|
||||||
|
choices: "#"+endTag
|
||||||
|
})
|
||||||
|
if typeof(startTag) == "string"
|
||||||
|
retval.tags = [startTag]
|
||||||
|
else if typeof(startTag) == "object"
|
||||||
|
retval.tags = startTag
|
||||||
|
if effect?
|
||||||
|
retval.before = (character, system) ->
|
||||||
|
eval(effect)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
module.exports = dialogue
|
16
lib/interface.coffee
Normal file
16
lib/interface.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
###
|
||||||
|
Salet interface configuration.
|
||||||
|
###
|
||||||
|
undum = require('./undum.js')
|
||||||
|
$(document).ready(() ->
|
||||||
|
$("#ways").on("click", "a", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
undum.processClick($(this).attr("href"))
|
||||||
|
)
|
||||||
|
$("#inventory").on("click", "a", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
)
|
||||||
|
$("#load").on("click", "a", (event) ->
|
||||||
|
window.location.reload()
|
||||||
|
)
|
||||||
|
)
|
60
lib/localize.coffee
Normal file
60
lib/localize.coffee
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Internationalization support
|
||||||
|
|
||||||
|
languages = {}
|
||||||
|
|
||||||
|
# Default Messages
|
||||||
|
|
||||||
|
en = {
|
||||||
|
terrible: "terrible",
|
||||||
|
poor: "poor",
|
||||||
|
mediocre: "mediocre",
|
||||||
|
fair: "fair",
|
||||||
|
good: "good",
|
||||||
|
great: "great",
|
||||||
|
superb: "superb",
|
||||||
|
yes: "yes",
|
||||||
|
no: "no",
|
||||||
|
choice: "Choice {number}",
|
||||||
|
no_group_definition: "Couldn't find a group definition for {id}.",
|
||||||
|
link_not_valid: "The link '{link}' doesn't appear to be valid.",
|
||||||
|
link_no_action: "A link with a situation of '.', must have an action.",
|
||||||
|
unknown_situation: "You can't move to an unknown situation: {id}.",
|
||||||
|
existing_situation: "You can't override situation {id} in HTML.",
|
||||||
|
erase_message: "This will permanently delete this character and immediately return you to the start of the game. Are you sure?",
|
||||||
|
no_current_situation: "I can't display, because we don't have a current situation.",
|
||||||
|
no_local_storage: "No local storage available.",
|
||||||
|
random_seed_error: "You must provide a valid random seed.",
|
||||||
|
random_error: "Initialize the Random with a non-empty seed before use.",
|
||||||
|
dice_string_error: "Couldn't interpret your dice string: '{string}'."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set this data as both the default fallback language, and the english preferred language.
|
||||||
|
languages[""] = en
|
||||||
|
languages["en"] = en
|
||||||
|
|
||||||
|
languageCodes = Object.keys(languages)
|
||||||
|
|
||||||
|
localize = (languageCode, message) ->
|
||||||
|
for thisCode in languageCodes
|
||||||
|
localized = languages[languageCode][message]
|
||||||
|
if localized
|
||||||
|
return localized
|
||||||
|
return message
|
||||||
|
|
||||||
|
# API
|
||||||
|
String.prototype.l = (args) ->
|
||||||
|
# Get lang attribute from html tag.
|
||||||
|
lang = document.getElementsByTagName("html")[0].getAttribute("lang") || ""
|
||||||
|
|
||||||
|
# Find the localized form.
|
||||||
|
localized = localize(lang, this)
|
||||||
|
|
||||||
|
# Merge in any replacement content.
|
||||||
|
if args
|
||||||
|
for name in args
|
||||||
|
localized = localized.replace(
|
||||||
|
new RegExp("\\{"+name+"\\}"), args[name]
|
||||||
|
)
|
||||||
|
return localized
|
||||||
|
|
||||||
|
module.exports = languages;
|
36
lib/markdown.coffee
Normal file
36
lib/markdown.coffee
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
###
|
||||||
|
Indent normalization. Removes tabs AND spaces from every line beginning.
|
||||||
|
Implies that you don't mix up your tabs and spaces.
|
||||||
|
Copyright 2015 Bruno Dias
|
||||||
|
###
|
||||||
|
normaliseTabs = (text) ->
|
||||||
|
unless text?
|
||||||
|
return ""
|
||||||
|
lines = text.split('\n');
|
||||||
|
indents = lines
|
||||||
|
.filter((l) => l != '')
|
||||||
|
.map((l) => l.match(/^\s+/))
|
||||||
|
.map((m) ->
|
||||||
|
if (m == null)
|
||||||
|
return ''
|
||||||
|
return m[0]
|
||||||
|
)
|
||||||
|
smallestIndent = indents.reduce((max, curr) ->
|
||||||
|
if (curr.length < max.length)
|
||||||
|
return curr
|
||||||
|
return max
|
||||||
|
)
|
||||||
|
return lines.map((l) ->
|
||||||
|
return l.replace(new RegExp('^' + smallestIndent), '')
|
||||||
|
).join('\n')
|
||||||
|
|
||||||
|
markdown = (text) ->
|
||||||
|
unless text?
|
||||||
|
return ""
|
||||||
|
if typeof text is Function
|
||||||
|
text = text()
|
||||||
|
return marked(normaliseTabs(text), {
|
||||||
|
smartypants: true
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = markdown
|
50
lib/obj.coffee
Normal file
50
lib/obj.coffee
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
markdown = require('./markdown.coffee')
|
||||||
|
undum = require('./undum.js')
|
||||||
|
objlink = (content, ref) ->
|
||||||
|
return "<a href='./_act_#{ref}' class='once'>#{content}</a>"
|
||||||
|
|
||||||
|
Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1
|
||||||
|
|
||||||
|
parsedsc = (text, name) ->
|
||||||
|
window.objname = name
|
||||||
|
text = text.replace /\{\{(.+)\}\}/g, (str, p1) ->
|
||||||
|
name = window.objname
|
||||||
|
window.objname = undefined
|
||||||
|
return objlink(p1, name)
|
||||||
|
return text
|
||||||
|
|
||||||
|
# An object class.
|
||||||
|
# An object cannot be in several locations at once, you must clone the variable.
|
||||||
|
class SaletObj
|
||||||
|
constructor: (spec) ->
|
||||||
|
unless spec.name?
|
||||||
|
console.error("Trying to create an object with no name")
|
||||||
|
return null
|
||||||
|
for key, value of spec
|
||||||
|
this[key] = value
|
||||||
|
level: 0
|
||||||
|
look: (character, system, f) =>
|
||||||
|
if @dsc
|
||||||
|
text = markdown(@dsc.fcall(this, character, system, f))
|
||||||
|
text = "<span class='look lvl#{@level}'>" + text + "</span>"
|
||||||
|
# replace braces {{}} with link to _act_
|
||||||
|
return parsedsc(text, @name)
|
||||||
|
takeable: false
|
||||||
|
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) =>
|
||||||
|
@level = 0 # this is scenery
|
||||||
|
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 SaletObj(spec)
|
||||||
|
module.exports = obj
|
170
lib/oneOf.coffee
Normal file
170
lib/oneOf.coffee
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
###
|
||||||
|
oneOf.js
|
||||||
|
|
||||||
|
Copyright (c) 2015 Bruno Dias
|
||||||
|
|
||||||
|
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.
|
||||||
|
###
|
||||||
|
|
||||||
|
###
|
||||||
|
Undularity Tools
|
||||||
|
|
||||||
|
Those functions are not a core part of Undularity, but provide some
|
||||||
|
general functionality that relates to adaptive text generation.
|
||||||
|
|
||||||
|
This is provided partly as a helper to less technical users, and as
|
||||||
|
a convenience for authors.
|
||||||
|
###
|
||||||
|
|
||||||
|
# Monkey patching
|
||||||
|
|
||||||
|
###
|
||||||
|
Shuffles an array. It can use Undum's random number generator implementation,
|
||||||
|
so it expects a System.rnd object to be passed into it. If one isn't
|
||||||
|
supplied, it will use Math.Random instead.
|
||||||
|
|
||||||
|
This is an implementation of the Fischer-Yates (Knuth) shuffle.
|
||||||
|
|
||||||
|
Returns the shuffled array.
|
||||||
|
###
|
||||||
|
|
||||||
|
Array.prototype.shuffle = (system) ->
|
||||||
|
rng = if system then system.rnd.random else Math.random
|
||||||
|
# slice() clones the array. Object members are copied by reference, beware.
|
||||||
|
newArr = this.slice()
|
||||||
|
m = newArr.length
|
||||||
|
|
||||||
|
while (m)
|
||||||
|
i = Math.floor(rng() * m--)
|
||||||
|
t = newArr[m]
|
||||||
|
newArr[m] = newArr[i]
|
||||||
|
newArr[i] = t
|
||||||
|
|
||||||
|
return newArr
|
||||||
|
|
||||||
|
###
|
||||||
|
oneOf()
|
||||||
|
|
||||||
|
Takes an array and returns an object with several methods. Each method
|
||||||
|
returns an iterator which iterates over the array in a specific way:
|
||||||
|
|
||||||
|
inOrder()
|
||||||
|
Returns the array items in order.
|
||||||
|
|
||||||
|
cycling()
|
||||||
|
Returns the array items in order, cycling back to the first item when
|
||||||
|
it runs out.
|
||||||
|
|
||||||
|
stopping()
|
||||||
|
Returns the array items in order, then repeats the last item when it
|
||||||
|
runs out.
|
||||||
|
|
||||||
|
randomly()
|
||||||
|
Returns the array items at random. Takes a system object, for consistent
|
||||||
|
randomness. Will never return the same item twice in a row.
|
||||||
|
|
||||||
|
trulyAtRandom()
|
||||||
|
Returns the array items purely at random. Takes a system object, for
|
||||||
|
consistent randomness.
|
||||||
|
|
||||||
|
inRandomOrder()
|
||||||
|
Returns the array items in a random order. Takes a system object, for
|
||||||
|
consistent randomness.
|
||||||
|
###
|
||||||
|
|
||||||
|
###
|
||||||
|
Takes a function and gives it a toString() property that calls itself and
|
||||||
|
returns its value, allowing for ambiguous use of the closure object
|
||||||
|
as a text snippet.
|
||||||
|
|
||||||
|
Returns the modified function.
|
||||||
|
###
|
||||||
|
stringish = (callback) ->
|
||||||
|
callback.toString = () ->
|
||||||
|
return '' + this.call()
|
||||||
|
return callback
|
||||||
|
|
||||||
|
oneOf = (ary...) ->
|
||||||
|
if ary.length == 0
|
||||||
|
throw new Error(
|
||||||
|
"tried to create a oneOf iterator with a 0-length array");
|
||||||
|
|
||||||
|
return {
|
||||||
|
inOrder: () ->
|
||||||
|
i = 0
|
||||||
|
return stringish(() ->
|
||||||
|
if i >= ary.length
|
||||||
|
return null
|
||||||
|
return ary[i++]
|
||||||
|
)
|
||||||
|
|
||||||
|
cycling: () ->
|
||||||
|
i = 0
|
||||||
|
return stringish(() ->
|
||||||
|
if (i >= ary.length)
|
||||||
|
i = 0
|
||||||
|
return ary[i++]
|
||||||
|
)
|
||||||
|
|
||||||
|
stopping: () ->
|
||||||
|
i = 0
|
||||||
|
return stringish(() ->
|
||||||
|
if (i >= ary.length)
|
||||||
|
i = ary.length - 1
|
||||||
|
return ary[i++]
|
||||||
|
)
|
||||||
|
|
||||||
|
randomly: (system) ->
|
||||||
|
rng = if system then system.rnd.random else Math.random
|
||||||
|
last = null
|
||||||
|
|
||||||
|
if (ary.length<2)
|
||||||
|
throw new Error("attempted to make randomly() iterator with a 1-length array")
|
||||||
|
return stringish( () ->
|
||||||
|
i = null
|
||||||
|
offset = null
|
||||||
|
if not last?
|
||||||
|
i = Math.floor(rng() * ary.length)
|
||||||
|
else
|
||||||
|
###
|
||||||
|
Let offset be a random number between 1 and the length of the
|
||||||
|
array, minus one. We jump offset items ahead on the array,
|
||||||
|
wrapping around to the beginning. This gives us a random item
|
||||||
|
other than the one we just chose.
|
||||||
|
###
|
||||||
|
|
||||||
|
offset = Math.floor(rng() * (ary.length -1) + 1);
|
||||||
|
i = (last + offset) % ary.length;
|
||||||
|
|
||||||
|
last = i
|
||||||
|
return ary[i]
|
||||||
|
)
|
||||||
|
|
||||||
|
trulyAtRandom: (system) ->
|
||||||
|
rng = if system then system.rnd.random else Math.random
|
||||||
|
return stringish(() ->
|
||||||
|
return ary[Math.floor(rng() * ary.length)];
|
||||||
|
)
|
||||||
|
|
||||||
|
inRandomOrder: (system) ->
|
||||||
|
shuffled = ary.shuffle(system)
|
||||||
|
i = 0
|
||||||
|
return stringish(() ->
|
||||||
|
if (i >= ary.length)
|
||||||
|
i = 0
|
||||||
|
return shuffled[i++]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.oneOf = () ->
|
||||||
|
oneOf.apply(null, this)
|
||||||
|
|
||||||
|
module.exports = oneOf;
|
207
lib/random.js
Normal file
207
lib/random.js
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Random Number generation based on seedrandom.js code by David Bau.
|
||||||
|
// Copyright 2010 David Bau, all rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or
|
||||||
|
// without modification, are permitted provided that the following
|
||||||
|
// conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above
|
||||||
|
// copyright notice, this list of conditions and the
|
||||||
|
// following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the
|
||||||
|
// following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of this module nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written
|
||||||
|
// permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
var width = 256;
|
||||||
|
var chunks = 6;
|
||||||
|
var significanceExponent = 52;
|
||||||
|
var startdenom = Math.pow(width, chunks);
|
||||||
|
var significance = Math.pow(2, significanceExponent);
|
||||||
|
var overflow = significance * 2;
|
||||||
|
|
||||||
|
var Random = (function () {
|
||||||
|
var Random = function(seed) {
|
||||||
|
this.random = null;
|
||||||
|
if (!seed) throw {
|
||||||
|
name: "RandomSeedError",
|
||||||
|
message: "random_seed_error".l()
|
||||||
|
};
|
||||||
|
var key = [];
|
||||||
|
mixkey(seed, key);
|
||||||
|
var arc4 = new ARC4(key);
|
||||||
|
this.random = function() {
|
||||||
|
var n = arc4.g(chunks);
|
||||||
|
var d = startdenom;
|
||||||
|
var x = 0;
|
||||||
|
while (n < significance) {
|
||||||
|
n = (n + x) * width;
|
||||||
|
d *= width;
|
||||||
|
x = arc4.g(1);
|
||||||
|
}
|
||||||
|
while (n >= overflow) {
|
||||||
|
n /= 2;
|
||||||
|
d /= 2;
|
||||||
|
x >>>= 1;
|
||||||
|
}
|
||||||
|
return (n + x) / d;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// Helper type.
|
||||||
|
var ARC4 = function(key) {
|
||||||
|
var t, u, me = this, keylen = key.length;
|
||||||
|
var i = 0, j = me.i = me.j = me.m = 0;
|
||||||
|
me.S = [];
|
||||||
|
me.c = [];
|
||||||
|
if (!keylen) { key = [keylen++]; }
|
||||||
|
while (i < width) { me.S[i] = i++; }
|
||||||
|
for (i = 0; i < width; i++) {
|
||||||
|
t = me.S[i];
|
||||||
|
j = lowbits(j + t + key[i % keylen]);
|
||||||
|
u = me.S[j];
|
||||||
|
me.S[i] = u;
|
||||||
|
me.S[j] = t;
|
||||||
|
}
|
||||||
|
me.g = function getnext(count) {
|
||||||
|
var s = me.S;
|
||||||
|
var i = lowbits(me.i + 1); var t = s[i];
|
||||||
|
var j = lowbits(me.j + t); var u = s[j];
|
||||||
|
s[i] = u;
|
||||||
|
s[j] = t;
|
||||||
|
var r = s[lowbits(t + u)];
|
||||||
|
while (--count) {
|
||||||
|
i = lowbits(i + 1); t = s[i];
|
||||||
|
j = lowbits(j + t); u = s[j];
|
||||||
|
s[i] = u;
|
||||||
|
s[j] = t;
|
||||||
|
r = r * width + s[lowbits(t + u)];
|
||||||
|
}
|
||||||
|
me.i = i;
|
||||||
|
me.j = j;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
me.g(width);
|
||||||
|
};
|
||||||
|
// Helper functions.
|
||||||
|
var mixkey = function(seed, key) {
|
||||||
|
seed += '';
|
||||||
|
var smear = 0;
|
||||||
|
for (var j = 0; j < seed.length; j++) {
|
||||||
|
var lb = lowbits(j);
|
||||||
|
smear ^= key[lb];
|
||||||
|
key[lb] = lowbits(smear*19 + seed.charCodeAt(j));
|
||||||
|
}
|
||||||
|
seed = '';
|
||||||
|
for (j in key) {
|
||||||
|
seed += String.fromCharCode(key[j]);
|
||||||
|
}
|
||||||
|
return seed;
|
||||||
|
};
|
||||||
|
var lowbits = function(n) {
|
||||||
|
return n & (width - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return Random;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/* Returns a random floating point number between zero and
|
||||||
|
* one. NB: The prototype implementation below just throws an
|
||||||
|
* error, it will be overridden in each Random object when the
|
||||||
|
* seed has been correctly configured. */
|
||||||
|
Random.prototype.random = function() {
|
||||||
|
throw {
|
||||||
|
name:"RandomError",
|
||||||
|
message: "random_error".l()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* Returns an integer between the given min and max values,
|
||||||
|
* inclusive. */
|
||||||
|
Random.prototype.randomInt = function(min, max) {
|
||||||
|
return min + Math.floor((max-min+1)*this.random());
|
||||||
|
};
|
||||||
|
/* Returns the result of rolling n dice with dx sides, and adding
|
||||||
|
* plus. */
|
||||||
|
Random.prototype.dice = function(n, dx, plus) {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
result += this.randomInt(1, dx);
|
||||||
|
}
|
||||||
|
if (plus) result += plus;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
/* Returns the result of rolling n averaging dice (i.e. 6 sided dice
|
||||||
|
* with sides 2,3,3,4,4,5). And adding plus. */
|
||||||
|
Random.prototype.aveDice = (function() {
|
||||||
|
var mapping = [2,3,3,4,4,5];
|
||||||
|
return function(n, plus) {
|
||||||
|
var result = 0;
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
result += mapping[this.randomInt(0, 5)];
|
||||||
|
}
|
||||||
|
if (plus) result += plus;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
/* Returns a dice-roll result from the given string dice
|
||||||
|
* specification. The specification should be of the form xdy+z,
|
||||||
|
* where the x component and z component are optional. This rolls
|
||||||
|
* x dice of with y sides, and adds z to the result, the z
|
||||||
|
* component can also be negative: xdy-z. The y component can be
|
||||||
|
* either a number of sides, or can be the special values 'F', for
|
||||||
|
* a fudge die (with 3 sides, +,0,-), '%' for a 100 sided die, or
|
||||||
|
* 'A' for an averaging die (with sides 2,3,3,4,4,5).
|
||||||
|
*/
|
||||||
|
|
||||||
|
Random.prototype.diceString = (function () {
|
||||||
|
var diceRe = /^([1-9][0-9]*)?d([%FA]|[1-9][0-9]*)([-+][1-9][0-9]*)?$/;
|
||||||
|
return function(def) {
|
||||||
|
var match = def.match(diceRe);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(
|
||||||
|
"dice_string_error".l({string:def})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var num = match[1]?parseInt(match[1], 10):1;
|
||||||
|
var sides;
|
||||||
|
var bonus = match[3]?parseInt(match[3], 10):0;
|
||||||
|
|
||||||
|
switch (match[2]) {
|
||||||
|
case 'A':
|
||||||
|
return this.aveDice(num, bonus);
|
||||||
|
case 'F':
|
||||||
|
sides = 3;
|
||||||
|
bonus -= num*2;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
sides = 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sides = parseInt(match[2], 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return this.dice(num, sides, bonus);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
module.exports = Random;
|
277
lib/room.coffee
Normal file
277
lib/room.coffee
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
# I confess that this world model heavily borrows from INSTEAD engine. - A.Y.
|
||||||
|
|
||||||
|
undum = require('./undum.js')
|
||||||
|
obj = require('./obj.coffee')
|
||||||
|
markdown = require('./markdown.coffee')
|
||||||
|
cycle = require('./cycle.coffee')
|
||||||
|
|
||||||
|
way_to = (content, ref) ->
|
||||||
|
return "<a href='#{ref}' class='way' id='waylink-#{ref}'>#{content}</a>"
|
||||||
|
|
||||||
|
# jQuery was confused by this point where's the context so I did it vanilla-way
|
||||||
|
print = (content) ->
|
||||||
|
if content == ""
|
||||||
|
return
|
||||||
|
if typeof content == "function"
|
||||||
|
content = content()
|
||||||
|
block = document.getElementById("current-situation")
|
||||||
|
if block
|
||||||
|
block.innerHTML = block.innerHTML + markdown(content)
|
||||||
|
else
|
||||||
|
console.error("No current situation found.")
|
||||||
|
|
||||||
|
Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1
|
||||||
|
|
||||||
|
addClass = (element, className) ->
|
||||||
|
if (element.classList)
|
||||||
|
element.classList.add(className)
|
||||||
|
else
|
||||||
|
element.className += ' ' + className
|
||||||
|
|
||||||
|
cls = (system) ->
|
||||||
|
system.clearContent()
|
||||||
|
system.clearContent("#intro")
|
||||||
|
|
||||||
|
update_ways = (ways, name) ->
|
||||||
|
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].title.fcall(this, name)
|
||||||
|
content += way_to(title, way)
|
||||||
|
distances.push({
|
||||||
|
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 = []
|
||||||
|
for node in distances
|
||||||
|
if node.distance < min
|
||||||
|
min = node.distance
|
||||||
|
min_key = [node.key]
|
||||||
|
if node.distance == min
|
||||||
|
min_key.push(node.key)
|
||||||
|
if min < Infinity
|
||||||
|
for node in min_key
|
||||||
|
addClass(document.getElementById("waylink-#{node}"), "destination")
|
||||||
|
|
||||||
|
picture_tag = (picture) ->
|
||||||
|
extension = picture.substr((~-picture.lastIndexOf(".") >>> 0) + 2)
|
||||||
|
if (extension == "webm")
|
||||||
|
return """
|
||||||
|
<video src="#{picture}" controls>
|
||||||
|
Your browser does not support the video tag for some reason.
|
||||||
|
You won't be able to view this video in this browser.
|
||||||
|
</video>
|
||||||
|
"""
|
||||||
|
return "<img class='img-responsive' src='#{picture}' alt='Room illustration'>"
|
||||||
|
|
||||||
|
class SaletRoom extends undum.Situation
|
||||||
|
constructor: (spec) ->
|
||||||
|
undum.Situation.call(this, spec)
|
||||||
|
for index, value of spec
|
||||||
|
this[index] = value
|
||||||
|
return this
|
||||||
|
visited: 0
|
||||||
|
title: "Room"
|
||||||
|
objects: {}
|
||||||
|
|
||||||
|
# room illustration image, VN-style. Can be a GIF or WEBM. Can be a function.
|
||||||
|
pic: false
|
||||||
|
|
||||||
|
dsc: false # room description
|
||||||
|
extendSection: false
|
||||||
|
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
|
||||||
|
|
||||||
|
###
|
||||||
|
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).
|
||||||
|
|
||||||
|
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) =>
|
||||||
|
if @clear and f?
|
||||||
|
cls(system)
|
||||||
|
|
||||||
|
if f != @name and f?
|
||||||
|
@visited++
|
||||||
|
if undum.game.situations[f].exit?
|
||||||
|
undum.game.situations[f].exit(character, system, @name)
|
||||||
|
|
||||||
|
if @enter
|
||||||
|
@enter character, system, f
|
||||||
|
|
||||||
|
current_situation = ""
|
||||||
|
if not @extendSection
|
||||||
|
classes = if @classes then ' ' + @classes.join(' ') else ''
|
||||||
|
situation = document.getElementById('current-situation')
|
||||||
|
if situation?
|
||||||
|
situation.removeAttribute('id')
|
||||||
|
# Javascript DOM manipulation functions like jQuery's append() or document.createElement
|
||||||
|
# don't work like a typical printLn - they create *DOM nodes*.
|
||||||
|
# You can't leave an unclosed tag just like that. So we have to buffer the output.
|
||||||
|
current_situation = "<section id='current-situation' data-situation='#{@name}' class='situation-#{@name}#{classes}'>"
|
||||||
|
|
||||||
|
if f != @name and @before?
|
||||||
|
current_situation += markdown(@before.fcall(this, character, system, f))
|
||||||
|
|
||||||
|
current_situation += @look character, system, f
|
||||||
|
|
||||||
|
if f != @name and @after?
|
||||||
|
current_situation += markdown(@after.fcall(this, character, system, f))
|
||||||
|
|
||||||
|
if not @extendSection
|
||||||
|
current_situation += "</section>"
|
||||||
|
|
||||||
|
system.write(current_situation)
|
||||||
|
|
||||||
|
if @choices
|
||||||
|
system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices))
|
||||||
|
|
||||||
|
###
|
||||||
|
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 = ""
|
||||||
|
|
||||||
|
if @pic
|
||||||
|
retval += '<div class="pic">'+picture_tag(@pic.fcall(this, character, system, f))+'</div>'
|
||||||
|
|
||||||
|
# Print the room description
|
||||||
|
if @dsc
|
||||||
|
retval += markdown(@dsc.fcall(this, character, system, f))
|
||||||
|
|
||||||
|
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 = {}
|
||||||
|
|
||||||
|
drop: (name) =>
|
||||||
|
delete @objects[name]
|
||||||
|
|
||||||
|
###
|
||||||
|
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) =>
|
||||||
|
if (link = action.match(/^_(act|cycle)_(.+)$/)) #object action
|
||||||
|
for name, thing of @objects
|
||||||
|
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.")
|
||||||
|
|
||||||
|
# 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 = "<span>"+content+"</span>" # <p> 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: () =>
|
||||||
|
@distance = 0
|
||||||
|
|
||||||
|
candidates = [this]
|
||||||
|
while candidates.length > 0
|
||||||
|
current_room = candidates.shift()
|
||||||
|
if current_room.ways
|
||||||
|
for node in current_room.ways
|
||||||
|
if node.distance == Infinity
|
||||||
|
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
|
||||||
|
retval = new SaletRoom(spec)
|
||||||
|
retval.register()
|
||||||
|
return retval
|
||||||
|
|
||||||
|
module.exports = room
|
1617
lib/undum.js
Normal file
1617
lib/undum.js
Normal file
File diff suppressed because it is too large
Load diff
13
package.json
13
package.json
|
@ -1,9 +1,5 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undum": "git://github.com/oreolek/undum-commonjs#commonjs",
|
|
||||||
"raconteur": "git://github.com/sequitur/raconteur.git#stable",
|
|
||||||
"jquery": "^2.1.3",
|
|
||||||
"markdown-it": "^4.1.0"
|
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -13,16 +9,15 @@
|
||||||
"browserify-shim": "^3.8.8",
|
"browserify-shim": "^3.8.8",
|
||||||
"coffeeify": "^1.0.0",
|
"coffeeify": "^1.0.0",
|
||||||
"gulp": "^3.8.11",
|
"gulp": "^3.8.11",
|
||||||
"gulp-gzip": "^1.1.0",
|
|
||||||
"gulp-less": "^3.0.2",
|
|
||||||
"gulp-minify-css": "^1.0.0",
|
|
||||||
"gulp-uglify": "^1.2.0",
|
"gulp-uglify": "^1.2.0",
|
||||||
|
"gulp-coffee": "^2.3.1",
|
||||||
"gulp-util": "^3.0.4",
|
"gulp-util": "^3.0.4",
|
||||||
"gulp-zip": "^3.0.2",
|
"gulp-zip": "^3.0.2",
|
||||||
|
"gulp-concat": "^2.6.0",
|
||||||
|
"gulp-sass": "^2.1.1",
|
||||||
"lodash": "^3.6.0",
|
"lodash": "^3.6.0",
|
||||||
"vinyl-buffer": "^1.0.0",
|
"vinyl-buffer": "^1.0.0",
|
||||||
"vinyl-source-stream": "^1.1.0",
|
"vinyl-source-stream": "^1.1.0",
|
||||||
"watchify": "^3.1.0",
|
"watchify": "^3.1.0"
|
||||||
"shortid": "^2.2.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,13 +51,6 @@ body {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.click_message {
|
|
||||||
display: none;
|
|
||||||
font-size: 0.9em;
|
|
||||||
font-style: italic;
|
|
||||||
text-align: center;
|
|
||||||
color: #987;
|
|
||||||
}
|
|
||||||
.noscript_message {
|
.noscript_message {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -70,24 +63,8 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#tools_wrapper {
|
#tools_wrapper {
|
||||||
display: none; // Shown by Javascript
|
|
||||||
/*
|
|
||||||
.tools {
|
|
||||||
background: $secondary-bg;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0.5em;
|
|
||||||
@include col(4, 5);
|
|
||||||
@media (min-width: breakpoint-min(sm)) {
|
|
||||||
@include make-col-offset(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
.ways {
|
.ways {
|
||||||
h2 {
|
|
||||||
display: none; // Shown by Javascript
|
|
||||||
}
|
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
// @include col(4, 5);
|
|
||||||
@include col(9, 10);
|
@include col(9, 10);
|
||||||
@media (min-width: breakpoint-min(sm)) {
|
@media (min-width: breakpoint-min(sm)) {
|
||||||
@include make-col-offset(1);
|
@include make-col-offset(1);
|
||||||
|
@ -103,11 +80,10 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#content_wrapper {
|
#content_wrapper {
|
||||||
display: none; // Shown by Javascript
|
|
||||||
background: $text_background;
|
background: $text_background;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
#content {
|
.content {
|
||||||
@include col(10, 12);
|
@include col(10, 12);
|
||||||
@media (min-width: breakpoint-min(sm)) {
|
@media (min-width: breakpoint-min(sm)) {
|
||||||
@include make-col-offset(1);
|
@include make-col-offset(1);
|
||||||
|
@ -163,7 +139,6 @@ body {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
color: darken($body-color, 10%);
|
color: darken($body-color, 10%);
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
display: none; // Shown by Javascript
|
|
||||||
#footleft {
|
#footleft {
|
||||||
@include make-col();
|
@include make-col();
|
||||||
@media (min-width: breakpoint-min(sm)) {
|
@media (min-width: breakpoint-min(sm)) {
|
||||||
|
|
Loading…
Reference in a new issue