1
0
Fork 0
mirror of https://gitlab.com/Oreolek/black_phone.git synced 2024-04-25 21:59:42 +03:00

Salet conversion WIP

This commit is contained in:
Alexander Yakovlev 2016-01-20 23:48:30 +07:00
parent 0d2f825662
commit f497b78e27
19 changed files with 2729 additions and 392 deletions

131
Gulpfile.coffee Normal file
View 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('.'));
);

View file

@ -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('.'));
});

View file

@ -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
a copy of this software and associated documentation files (the

View file

@ -2,54 +2,32 @@
# 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
situation = require('raconteur')
undum = require('undum-commonjs')
oneOf = require('raconteur/lib/oneOf.js')
qualities = require('raconteur/lib/qualities.js')
$ = require("jquery")
Array.prototype.oneOf = () ->
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.
markdown = require('../../lib/markdown.coffee')
room = require("../../lib/room.coffee")
obj = require('../../lib/obj.coffee')
dialogue = require('../../lib/dialogue.coffee')
oneOf = require('../../lib/oneOf.coffee')
require('../../lib/interface.coffee')
undum = require('../../lib/undum.js')
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) ->
if typeof text is Function
text = text()
text = markdown.render(text)
text = markdown(text)
system.write(text)
preparemd = (text, mark) ->
if typeof text is Function
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)
money = (character, amount) ->
character.sandbox.money = character.sandbox.money + amount
code_can_input = (character) ->
return character.sandbox.code.length < 8
@ -108,40 +86,12 @@ code_check = (character, system) ->
character.sandbox.code = ""
update_ways = (ways) ->
content = ""
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.
room "start",
dsc: """
Peter opened his door to find an empty silent corridor.
He went to the neighbor's door and met a closed door.
Ronald was working inside, quietly walking around the apartment.
He began the inspection from [the living room.](living-room)
<hr>
He began the inspection from #{way_to('the living room.', 'living-room')}
"""
is_visited = (situation) ->
@ -153,7 +103,7 @@ is_visited = (situation) ->
# N-th level examine function
level = (text, mark) ->
$("#content .#{mark}").fadeOut()
return preparemd(text, mark)
return markdown(text, mark)
lvl1 = (text) ->
$("#content .lvl2").fadeOut()

View file

@ -1,24 +1,8 @@
qualities
general:
money: qualities.integer('Money'),
undum.game.init = (character, system) ->
$("#ways").on("click", "a", (event) ->
event.preventDefault()
undum.processClick($(this).attr("href"))
)
_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.money = 0
character.sandbox.code = ""
character.sandbox.knows_the_code = 0
character.sandbox.box_opened = 0

View file

@ -1,44 +1,40 @@
situation "living-room",
room "living-room",
title: "Living room"
before: () ->
if not $(".ways h2").is(':visible')
$(".ways h2").fadeIn()
update_ways(this.ways)
audio = document.getElementById("bgsound")
audio.currentTime=0
audio.volume = 0.5
audio.play()
enter: (character, system, from) ->
if (from == "start")
audio = document.getElementById("bgsound")
audio.currentTime=0
audio.volume = 0.5
audio.play()
ways: ["bedroom", "kitchen", "balcony"]
content: """
Ronald is standing in a dark room with a big #{textlink("window.", "window")}
#{textlink("The walls", "walls")} are covered with a dingy rose wallpaper.
dsc: """
#{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:
door: (character, system) ->
if character.sandbox.box_opened == 0
writemd(system, lvl1("Ronald has a job here. It's still on."))
else
system.doLink("exitdoor")
writers:
walls: (character, system) ->
lvl1("""
objects:
window: obj "window",
act: """
The moon is full today.
It illuminates the apartment, makes the things stand out in some weird angles.
"""
dsc: "Ronald is standing in a dark room with a big {{window}}"
walls: obj "walls",
dsc: "{{The walls}} are covered with a dingy rose wallpaper."
act: """
There are colorful photographs on the walls.
A wooden house in a forest.
A village on a mountaintop.
A family sitting around a fire.
A sunset burning in a vast ocean.
A black monolith standing on sand dunes.
""")
window: (character, system) ->
lvl1("""
The moon is full today.
It illuminates the apartment, makes the things stand out in some weird angles.
""")
"""
door: obj "door",
dsc: "Oh, and {{the door Ronald came into}} the apartment is there, too."
act: (character, system) ->
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) ->
lvl1("""
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.
""")
else
money(character, system, 20000)
money(character, 20000)
lvl2("""
Nietsche's four-part novel about The Man, The Superman and everything in-between.
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.
""")
situation "bedroom",
before: () ->
update_ways(this.ways)
room "bedroom",
title: "Bedroom"
ways: ["living-room", "kitchen", "bathroom"]
content: (character, system) ->
dsc: (character, system) ->
return """
The bedroom is spacious; its walls are lavender green, almost white in the moonlight.
@ -188,7 +182,7 @@ situation "bedroom",
""")
else
character.sandbox.seen_coat = 1
money(character, system, 4000)
money(character, 4000)
return lvl2("""
A warm coat.. hey, what's this?
One of the pockets is loaded with cash!
@ -264,7 +258,7 @@ situation "bedroom",
""")
money: (character, system) ->
character.sandbox.seen_safe = 1
money(character, system, 50000)
money(character, 50000)
lvl4("""
It's a big cash.
Odd that she didn't take this when she left.
@ -278,12 +272,10 @@ situation "bedroom",
The sketch is signed: *"L. Y. - 2017"*
""")
situation "kitchen",
before: () ->
update_ways(this.ways)
room "kitchen",
title: "Kitchen"
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.
"""
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?
""")
situation "bathroom",
room "bathroom",
before: (character,system) ->
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")
undum.game.situations["bedroom"].ways.splice(index, 1)
update_ways(undum.game.situations["bedroom"].ways)
return false
title: "Bathroom"
ways: ["bedroom"]
situation "balcony",
before: () ->
update_ways(this.ways)
room "balcony",
title: "Balcony"
ways: ["living-room"]
content: """
dsc: """
A small glazed-in empty balcony.
It's an amazing night.
The whole town is lit by moonlight, standing perfectly still.
@ -359,12 +348,10 @@ situation "balcony",
&nbsp;&nbsp;&nbsp; *L. Y.*
""")
situation "box",
before: () ->
update_ways(this.ways)
room "box",
ways: ["bedroom"]
choices: "#box"
content: (character, system) ->
dsc: (character, system) ->
return """
It's a red wood, very expensive.
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
situation "smash",
room "smash",
canView: (character) ->
character.sandbox.view_smash == 1
optionText: "Smash the box"
@ -388,10 +374,10 @@ situation "smash",
character.sandbox.view_smash = 0
choices: "#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) ->
situation "put#{number}",
room "put#{number}",
choices: "#box"
tags: ["box"]
optionText: "Enter #{number}"
@ -401,7 +387,7 @@ safe_button = (number) ->
code_can_input(character)
after: (character, system) ->
code_check(character, system)
content: (character) -> """
dsc: (character) -> """
Ronald presses button #{number}. The display is #{code_print(character)} now.
"""
@ -410,22 +396,20 @@ safe_button(2)
safe_button(7)
safe_button(0)
situation "reset",
room "reset",
choices: "#box"
tags: ["box"]
optionText: "Reset the display"
before: (character) ->
code_reset(character)
content: """
dsc: """
Ronald presses Backspace until the display is empty.
"""
situation "exitdoor",
before: () ->
update_ways(this.ways)
room "exitdoor",
ways: ["living-room"]
choices: "#door"
content: """
dsc: """
Ronald is ready to go.
Maybe he's satisfied with his explorations or just wants to finish this.
But then a new problem arrives.
@ -433,15 +417,14 @@ situation "exitdoor",
Someone's shadow is under the doorframe.
"""
situation "finale",
room "finale",
before: () ->
_paq.push(['setCustomDimension', 1, true])
$("#tools_wrapper").hide()
update_ways(this.ways)
optionText: "Use the Phone"
tags: ["door"]
ways: []
content: (character, system) -> """
dsc: (character, system) -> """
"LOADING... 100%"
Ronald opens the door and presses his finger to the phone screen.
@ -460,8 +443,8 @@ situation "finale",
Well, that was a good night.
#{if character.qualities.money > 0
"The pocket is heavy with #{character.qualities.money} rubles and the phone."
#{if character.sandbox.money > 0
"The pocket is heavy with #{character.sandbox.money} rubles and the phone."
else
"The phone is heavy in the pocket."
}

View file

@ -16,24 +16,37 @@
<noscript>
<p class="noscript_message">This game requires Javascript.</p>
</noscript>
<p class="click_message">click to begin</p>
</div>
</div>
</div>
<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 id="content" class="content"></div>
<a name="end_of_content"></a>
</div>
<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'>
<h2>Other rooms</h2>
<div id="ways"></div>
@ -46,9 +59,8 @@
<div class="row">
<div id="legal">
<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>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>
</div>
<div id="footright">
@ -75,7 +87,10 @@
<div id="content_library"></div>
<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>
<!-- Piwik -->
<script type="text/javascript">

19
lib/cycle.coffee Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,5 @@
{
"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,
"devDependencies": {
@ -13,16 +9,15 @@
"browserify-shim": "^3.8.8",
"coffeeify": "^1.0.0",
"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-coffee": "^2.3.1",
"gulp-util": "^3.0.4",
"gulp-zip": "^3.0.2",
"gulp-concat": "^2.6.0",
"gulp-sass": "^2.1.1",
"lodash": "^3.6.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.1.0",
"shortid": "^2.2.4"
"watchify": "^3.1.0"
}
}

View file

@ -51,13 +51,6 @@ body {
margin-bottom: 1em;
}
}
.click_message {
display: none;
font-size: 0.9em;
font-style: italic;
text-align: center;
color: #987;
}
.noscript_message {
left: 0;
right: 0;
@ -70,24 +63,8 @@ body {
}
}
#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 {
h2 {
display: none; // Shown by Javascript
}
padding: 0.5em;
// @include col(4, 5);
@include col(9, 10);
@media (min-width: breakpoint-min(sm)) {
@include make-col-offset(1);
@ -103,11 +80,10 @@ body {
}
}
#content_wrapper {
display: none; // Shown by Javascript
background: $text_background;
border-radius: 5px;
}
#content {
.content {
@include col(10, 12);
@media (min-width: breakpoint-min(sm)) {
@include make-col-offset(1);
@ -163,7 +139,6 @@ body {
margin-top: 1em;
color: darken($body-color, 10%);
font-size: smaller;
display: none; // Shown by Javascript
#footleft {
@include make-col();
@media (min-width: breakpoint-min(sm)) {