Backports from Rachel, new interface.
This commit is contained in:
parent
f1a6d47dea
commit
16b82caf8d
121
Gulpfile.coffee
121
Gulpfile.coffee
|
@ -1,118 +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;
|
||||
reload = browserSync.reload
|
||||
|
||||
# Copy assets over without touching them
|
||||
assets = (target) ->
|
||||
html = (target) ->
|
||||
return () ->
|
||||
return gulp.src([
|
||||
'img/*.png',
|
||||
'img/*.jpeg',
|
||||
'img/*.jpg'
|
||||
]).pipe(gulp.dest(target))
|
||||
return gulp.src(['html/index.html','html/en.html'])
|
||||
.pipe(gulp.dest(target));
|
||||
|
||||
gulp.task('assets', assets('./build'));
|
||||
img = (target) ->
|
||||
return () ->
|
||||
return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg'])
|
||||
.pipe(gulp.dest(target));
|
||||
|
||||
gulp.task('sass', function () {
|
||||
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().on('error', sass.logError))
|
||||
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
|
||||
.pipe(gulp.dest('./build/css'));
|
||||
)
|
||||
|
||||
sources = [
|
||||
'./game/begin.coffee',
|
||||
'./game/story.coffee',
|
||||
'./game/init.coffee',
|
||||
]
|
||||
|
||||
opts = _.assign({}, watchify.args, {
|
||||
entries: ["./build/game/main.coffee"]
|
||||
debug: true
|
||||
transform: [coffeify]
|
||||
});
|
||||
bundler = watchify(browserify(opts));
|
||||
|
||||
gulp.task('html', html('./build'));
|
||||
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([
|
||||
'./game/begin.coffee',
|
||||
'./game/story.coffee',
|
||||
'./game/init.coffee',
|
||||
])
|
||||
gulp.task('concatCoffee', () ->
|
||||
return gulp.src(sources)
|
||||
.pipe(concat('./main.coffee'))
|
||||
.pipe(gulp.dest('./build/game'));
|
||||
});
|
||||
);
|
||||
|
||||
gulp.task('coffee', ['concatCoffee'])
|
||||
gulp.task('coffee', ['concatCoffee'], bundle);
|
||||
|
||||
bundler.on('update', coffee);
|
||||
bundler.on('log', gutil.log); # Output build logs to terminal
|
||||
bundler.on('update', bundle);
|
||||
bundler.on('log', gutil.log);
|
||||
|
||||
gulp.task('build', ['html', 'img', 'sass', 'coffee'])
|
||||
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(['./build/css/main.css'], sassListener);
|
||||
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'));
|
||||
});
|
||||
browserSync.reload);
|
||||
)
|
||||
|
||||
gulp.task('html-dist', html('./dist'));
|
||||
gulp.task('img-dist', img('./dist/img'));
|
||||
gulp.task('legal-dist', function () {
|
||||
gulp.task('audio-dist', audio('./dist/audio'));
|
||||
gulp.task('legal-dist', () ->
|
||||
return gulp.src(['LICENSE.txt'])
|
||||
.pipe(gulp.dest("./dist"));
|
||||
});
|
||||
);
|
||||
|
||||
gulp.task('sass-dist', function () {
|
||||
gulp.task('sass-dist', () ->
|
||||
return gulp.src('./sass/main.scss')
|
||||
.pipe(sass({outputStyle: 'compressed'}))
|
||||
.pipe(gulp.dest('./dist/css'));
|
||||
});
|
||||
);
|
||||
|
||||
var distBundler = browserify({
|
||||
distBundler = browserify({
|
||||
debug: false,
|
||||
entries: ['./build/game/main.coffee'],
|
||||
transform: ['coffeeify']
|
||||
});
|
||||
|
||||
distBundler.external('undum-commonjs');
|
||||
|
||||
gulp.task('coffee-dist', ['undum-dist', 'concatCoffee'], function () {
|
||||
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', 'legal-dist'],
|
||||
function () {
|
||||
return;
|
||||
});
|
||||
gulp.task('dist', ['html-dist', 'img-dist', 'sass-dist', 'coffee-dist', 'audio-dist', 'legal-dist']);
|
||||
|
||||
gulp.task('zip', ['dist'], function () {
|
||||
gulp.task('zip', ['dist'], () ->
|
||||
return gulp.src('dist/**')
|
||||
.pipe(zip('dist.zip'))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
A general client-side framework for cybertext interactive fiction games.
|
||||
|
||||
**Salet** is based upon the ideas of [Undum,](https://github.com/idmillington/undum) but is not a direct follower.
|
||||
It also uses some code from [Raconteur,](https://github.com/sequitur/raconteur) rewritten in CoffeeScript and altered to its own needs.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -1,170 +1,44 @@
|
|||
situation = require('raconteur')
|
||||
markdown = require('../../lib/markdown.coffee')
|
||||
room = require("../../lib/room.coffee")
|
||||
obj = require('../../lib/obj.coffee')
|
||||
dialogue = require('../../lib/dialogue.coffee')
|
||||
undum = require('undum-commonjs')
|
||||
oneOf = require('raconteur/lib/oneOf.js')
|
||||
qualities = require('raconteur/lib/qualities.js')
|
||||
oneOf = require('../../lib/oneOf.coffee')
|
||||
$ = require("jquery")
|
||||
require('../../lib/interface.coffee')
|
||||
|
||||
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.
|
||||
|
||||
undum.game.id = "6a909a4-586a-4089-bd18-26da684d1c8d"
|
||||
undum.game.id = "your-game-id-here"
|
||||
undum.game.version = "1.0"
|
||||
|
||||
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()
|
||||
###
|
||||
Element helpers. There is no real need to build monsters like a().id("hello")
|
||||
because you won't use them as is. It does not make sense in context, the
|
||||
author has Markdown and all utilities to *forget* about the markup.
|
||||
###
|
||||
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")
|
||||
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)
|
||||
|
||||
code_can_input = (character) ->
|
||||
return character.sandbox.code.length < 8
|
||||
|
||||
code_print = (character) ->
|
||||
mask = 8 - character.sandbox.code.length
|
||||
retval = character.sandbox.code
|
||||
if mask > 0
|
||||
for i in [1..mask]
|
||||
retval += "_"
|
||||
return retval
|
||||
|
||||
code_input = (character, digit) ->
|
||||
if code_can_input(character)
|
||||
character.sandbox.code = character.sandbox.code + digit
|
||||
|
||||
code_reset = (character) ->
|
||||
character.sandbox.code = ""
|
||||
|
||||
code_check = (character, system) ->
|
||||
if character.sandbox.code.length >= 8
|
||||
# There is an Undum.. let's call it a feature
|
||||
# that prevents the player from entering "3112".
|
||||
# You see, you can't select the situation 1 when you are
|
||||
# already in this situation, so you can't input 1 twice.
|
||||
if character.sandbox.code == "01012017"
|
||||
character.sandbox.box_opened = 1
|
||||
if character.sandbox.knows_the_code == 0
|
||||
writemd(system, """
|
||||
Is he an extraordinary puzzle cracker or was it a sheer luck, but Ronald manages to *guess* the code.
|
||||
""")
|
||||
else
|
||||
writemd(system, """
|
||||
New Year 2017.
|
||||
L. Y. must be Leonard Yakovlev, a famous painter.
|
||||
Some tabloids tried to connect him with Ana but it seemed like a weak link.
|
||||
|
||||
By that logic, his sketch is worth more than all the cash here.
|
||||
Ronald thinks about it, but decides to "let the woman have her memories".
|
||||
""")
|
||||
writemd(system, """
|
||||
Something clicks and box opens.
|
||||
|
||||
The phone is slick, black and light in Ronald's hand.
|
||||
It springs to life, humming with purpose.
|
||||
The screen plays an animation: "LOADING..."
|
||||
|
||||
Ronald has no other business here.
|
||||
It's time to go.
|
||||
""")
|
||||
system.doLink("bedroom")
|
||||
else
|
||||
writemd(system, "Something clicks and the display resets, but the box stays locked.")
|
||||
if character.sandbox.code == "000000"
|
||||
writemd(system, "Of course, Ronald didn't hope it would be that easy.")
|
||||
|
||||
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",
|
||||
# The first room of the game.
|
||||
# For accessibility reasons the text is provided in HTML, not here.
|
||||
room "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 Anastacia 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.
|
||||
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>
|
||||
"""
|
||||
""",
|
||||
choices: "#start"
|
||||
|
||||
# This function needs to go after the start room.
|
||||
is_visited = (situation) ->
|
||||
situations = undum.game.situations[situation]
|
||||
if situations
|
||||
return Boolean situations.visited
|
||||
place = undum.game.situations[situation]
|
||||
if place
|
||||
return Boolean place.visited
|
||||
return 0
|
||||
|
||||
# N-th level examine function
|
||||
level = (text, mark) ->
|
||||
$("#content .#{mark}").fadeOut()
|
||||
return preparemd(text, mark)
|
||||
|
||||
lvl1 = (text) ->
|
||||
$("#content .lvl2").fadeOut()
|
||||
$("#content .lvl3").fadeOut()
|
||||
$("#content .lvl4").fadeOut()
|
||||
level(text, "lvl1")
|
||||
|
||||
lvl2 = (text) ->
|
||||
$("#content .lvl3").fadeOut()
|
||||
$("#content .lvl4").fadeOut()
|
||||
level(text, "lvl2")
|
||||
|
||||
lvl3 = (text) ->
|
||||
$("#content .lvl4").fadeOut()
|
||||
level(text, "lvl3")
|
||||
|
||||
lvl4 = (text) ->
|
||||
level(text, "lvl4")
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
# This is where you initialize your game.
|
||||
# All code in this file comes last, so the game is almost ready by this point.
|
||||
|
||||
player "Player"
|
||||
money: 0
|
||||
status: "Good"
|
||||
|
||||
undum.game.init = (character, system) ->
|
||||
$("#ways").on("click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
undum.processClick($(this).attr("href"))
|
||||
)
|
||||
|
||||
window.onload = undum.begin
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# Your game goes here
|
||||
dialogue "Option 1", "start", "secretary", """
|
||||
No spoilers!
|
||||
"""
|
||||
|
||||
dialogue "Option 2", "start", "secretary", """
|
||||
No spoilers!
|
||||
"""
|
||||
|
||||
room "university-start",
|
||||
tags: ["secretary"]
|
||||
ways: ["supermarket"]
|
||||
optionText: "Leave the University"
|
||||
before: () ->
|
||||
document.getElementById("intro").innerHTML = ""
|
||||
document.getElementById("content").innerHTML = ""
|
||||
undum.game.situations["supermarket"].destination()
|
||||
"""
|
||||
You leave the University.
|
||||
"""
|
||||
content: """
|
||||
Okay, now to the supermarket.
|
||||
"""
|
||||
|
||||
room "supermarket",
|
||||
content: """
|
||||
A trendy supermarket.
|
||||
"""
|
|
@ -2,16 +2,33 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Salet tutorial</title>
|
||||
<title>Salet showcase</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href='https://fonts.googleapis.com/css?family=PT+Sans:400,400italic|PT+Sans+Caption' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="page" class="container">
|
||||
<div class="row">
|
||||
<div id="title" class="title">
|
||||
<div class="label">
|
||||
<h1>Salet</h1>
|
||||
<h2>A general cybertext IF engine</h2>
|
||||
<noscript>
|
||||
<p class="noscript_message">This game requires Javascript.</p>
|
||||
</noscript>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="content_wrapper" class="row">
|
||||
<div id="content">
|
||||
<h1>Salet tutorial</h1>
|
||||
<div id="intro" class="content">
|
||||
<section>
|
||||
<p>Intro here.</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>
|
||||
|
@ -20,22 +37,31 @@
|
|||
<h2>Other rooms</h2>
|
||||
<div id="ways"></div>
|
||||
</div>
|
||||
<div class='buttons'>
|
||||
<button id="undo">Undo</button>
|
||||
<button id="save">Save</button>
|
||||
<button id="load">Load</button>
|
||||
<button id="erase">Restart</button>
|
||||
<div class="menu">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="nav-item">
|
||||
<span class="nav-link disabled" id="inventory">Inventory</span>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">System</a>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-item disabled" id="save">Save</span>
|
||||
<span class="dropdown-item" id="erase">Restart</span>
|
||||
<div class="dropdown-divider"></div>
|
||||
<span class="dropdown-item disabled" id="undo">Undo (non-fuctioning)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div><!-- end of tools -->
|
||||
</div> <!-- End of div.tools_wrapper -->
|
||||
|
||||
<div class="row">
|
||||
<div id="legal">
|
||||
<div id="footleft">
|
||||
<!-- Author credit goes here -->
|
||||
<p>The game was written by <b><a href="http://en.oreolek.ru/" target="_blank">Oreolek.</a></b></p>
|
||||
<!-- It's a good gesture to specify how long is your game. -->
|
||||
<p>Approximate play time: five minutes.</p>
|
||||
<p>Written using <a href="http://git.oreolek.ru/oreolek/salet" target="_blank">Salet.</a>
|
||||
<p>The game was written by <em>(you should put your name here)</em></p>
|
||||
<p>Approximate play time: not measured.</p>
|
||||
<p>Written using <a href="http://git.oreolek.ru/oreolek/salet" target="_blank">Salet</a>.</p>
|
||||
<p>Betatesting credit: none yet</p>
|
||||
</div>
|
||||
<div id="footright">
|
||||
<a href="./LICENSE.txt"><img src="img/mit.png" alt="This program is licensed under MIT license."></a>
|
||||
|
@ -44,6 +70,27 @@
|
|||
</div>
|
||||
</div> <!-- End of div.page -->
|
||||
|
||||
<script type="text/javascript" src="game/main.js"></script>
|
||||
<div id="ui_library">
|
||||
<div id="quality" class="quality">
|
||||
<span class="name" data-attr="name"></span>
|
||||
<span class="value" data-attr="value"></span>
|
||||
</div>
|
||||
|
||||
<div id="quality_group" class="quality_group">
|
||||
<h2 data-attr="title"></h2>
|
||||
<div class="qualities_in_group">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr id="turn_separator">
|
||||
</div>
|
||||
|
||||
<div id="content_library"></div>
|
||||
<!-- 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>
|
||||
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="game/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
33
lib/dialogue.coffee
Normal file
33
lib/dialogue.coffee
Normal file
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
content: text
|
||||
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
|
14
lib/interface.coffee
Normal file
14
lib/interface.coffee
Normal file
|
@ -0,0 +1,14 @@
|
|||
###
|
||||
Salet interface configuration.
|
||||
###
|
||||
$ = require("jquery")
|
||||
|
||||
$(document).ready(() ->
|
||||
$("#ways").on("click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
undum.processClick($(this).attr("href"))
|
||||
)
|
||||
$("#inventory").on("click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
)
|
||||
)
|
32
lib/markdown.coffee
Normal file
32
lib/markdown.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
###
|
||||
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) ->
|
||||
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) ->
|
||||
if typeof text is Function
|
||||
text = text()
|
||||
return marked(normaliseTabs(text), {
|
||||
smartypants: true
|
||||
})
|
||||
|
||||
module.exports = markdown
|
33
lib/obj.coffee
Normal file
33
lib/obj.coffee
Normal file
|
@ -0,0 +1,33 @@
|
|||
markdown = require('./markdown.coffee')
|
||||
undum = require('undum-commonjs')
|
||||
$ = require("jquery")
|
||||
objlink = (content, ref) ->
|
||||
return "<a href='./_act_#{ref}'>#{content}</a>"
|
||||
|
||||
class RaconteurObj
|
||||
constructor: (spec) ->
|
||||
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>"
|
||||
window.name = @name
|
||||
text = text.replace /([\s^])\{\{(\w+)\}\}([\s$])/g, (str, p1, p2, p3) ->
|
||||
name = window.name
|
||||
window.name = undefined
|
||||
return p1+objlink(p2, name)+p3
|
||||
return text
|
||||
take: () -> "You take the #{@name}." # taking to inventory
|
||||
act: () -> "You don't find anything extraordinary about the #{@name}." # object action
|
||||
dsc: () -> "You see a {{#{@name}}} here." # object description
|
||||
put: (room) ->
|
||||
@level = 0 # this is scenery
|
||||
undum.game.situations[room].objects[@name] = this
|
||||
|
||||
obj = (name, spec) ->
|
||||
spec ?= {}
|
||||
spec.name = name
|
||||
return new RaconteurObj(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;
|
166
lib/room.coffee
Normal file
166
lib/room.coffee
Normal file
|
@ -0,0 +1,166 @@
|
|||
# I confess that this world model heavily borrows from INSTEAD engine. - A.Y.
|
||||
|
||||
undum = require('undum-commonjs')
|
||||
RaconteurSituation = require('./situation.coffee')
|
||||
obj = require('./obj.coffee')
|
||||
markdown = require('./markdown.coffee')
|
||||
$ = require("jquery")
|
||||
|
||||
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 typeof content == "function"
|
||||
content = content()
|
||||
block = document.getElementById("current-situation")
|
||||
if block
|
||||
block.innerHTML = block.innerHTML + markdown(content)
|
||||
else #the game is not initialized yet. This is dangerous and will not augment any links.
|
||||
block = document.getElementById("content")
|
||||
block.innerHTML = markdown(content)
|
||||
|
||||
Array::remove = (e) -> @[t..t] = [] if (t = @indexOf(e)) > -1
|
||||
|
||||
addClass = (element, className) ->
|
||||
if (element.classList)
|
||||
element.classList.add(className)
|
||||
else
|
||||
element.className += ' ' + className
|
||||
|
||||
# Function to return the current room.
|
||||
# Works because our `enter()` function sets the `data-situation` attribute.
|
||||
here = () ->
|
||||
return undum.game.situations[document.getElementById("current-situation").getAttribute("data-situation")]
|
||||
|
||||
update_ways = (ways) ->
|
||||
content = ""
|
||||
distances = []
|
||||
if ways
|
||||
for way in ways
|
||||
if undum.game.situations[way]?
|
||||
title = undum.game.situations[way].name
|
||||
content += way_to(title, way)
|
||||
distances.push({
|
||||
key: way
|
||||
distance: undum.game.situations[way].distance
|
||||
})
|
||||
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")
|
||||
|
||||
class SaletRoom extends RaconteurSituation
|
||||
constructor: (spec) ->
|
||||
RaconteurSituation.call(this, spec)
|
||||
if spec.objects?
|
||||
@objects = spec.objects
|
||||
if spec.exit?
|
||||
@exit = spec.exit
|
||||
return this
|
||||
objects: []
|
||||
distance: Infinity # distance to the destination
|
||||
|
||||
###
|
||||
I call SaletRoom.exit every time the player exits to another room.
|
||||
###
|
||||
exit: (character, system, to) ->
|
||||
###
|
||||
Undum calls Situation.enter 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.
|
||||
###
|
||||
enter: (character, system, f) ->
|
||||
#system.clearContent()
|
||||
if f != @name and f?
|
||||
@visited++
|
||||
undum.game.situations[f].exit(character, system, @name)
|
||||
|
||||
if not @extendSection
|
||||
classes = if @classes then ' ' + @classes.join(' ') else ''
|
||||
situation = document.getElementById('current-situation')
|
||||
if situation?
|
||||
situation.setAttribute('id', undefined)
|
||||
system.write("<section id='current-situation' data-situation='#{@name}' class='situation-#{@name}#{classes}'>")
|
||||
|
||||
if f != @name and @before?
|
||||
print(@before.fcall(this, character, system, f))
|
||||
|
||||
if @look
|
||||
@look character, system, f
|
||||
|
||||
if f != @name and @after?
|
||||
print(@after.fcall(this, character, system, f))
|
||||
|
||||
if not @extendSection
|
||||
system.write("</section>")
|
||||
|
||||
if @choices
|
||||
system.writeChoices(system.getSituationIdChoices(@choices, @minChoices, @maxChoices))
|
||||
|
||||
look: (character, system, f) ->
|
||||
update_ways(@ways)
|
||||
|
||||
# Print the room description
|
||||
if @content
|
||||
system.write(markdown(@content.fcall(this, character, system, f)))
|
||||
|
||||
if @objects? then for thing in @objects
|
||||
system.write thing.look()
|
||||
|
||||
###
|
||||
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) ->
|
||||
# default Raconteur action
|
||||
if (action.match(/^_(writer|replacer|inserter)_.+$/))
|
||||
return RaconteurSituation.prototype.act.call(this, character, system, f)
|
||||
|
||||
if (link = action.match(/^_act_(.+)$/)) #object action
|
||||
for thing in @objects
|
||||
if thing.name == link[1]
|
||||
# We check the "take" function. If it exists, the player can take this object.
|
||||
# If not, we check the "act" function.
|
||||
if thing.take
|
||||
@objects.remove(thing)
|
||||
character.sandbox.inventory.push thing
|
||||
@enter(character, system, @name)
|
||||
return print(thing.take.fcall(thing, character, system))
|
||||
if thing.act
|
||||
return print(thing.act.fcall(thing, character, system))
|
||||
# the loop is done but no return came - match not found
|
||||
console.error("Could not find #{link[1]} in current room.")
|
||||
|
||||
# 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)
|
||||
|
||||
room = (name, spec) ->
|
||||
if spec
|
||||
spec.name = name
|
||||
retval = new SaletRoom(spec)
|
||||
return retval.register()
|
||||
|
||||
module.exports = room
|
101
lib/situation.coffee
Normal file
101
lib/situation.coffee
Normal file
|
@ -0,0 +1,101 @@
|
|||
###
|
||||
This file is built on top of Raconteur.
|
||||
Raconteur is copyright (c) 2015 Bruno Dias
|
||||
This file is copyright (c) 2016 Alexander Yakovlev
|
||||
|
||||
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.
|
||||
###
|
||||
|
||||
undum = require('undum-commonjs')
|
||||
markdown = require('./markdown.coffee')
|
||||
|
||||
###
|
||||
fcall() (by analogy with fmap) is added to the prototypes of both String and
|
||||
Function. When called on a Function, it's an
|
||||
alias for Function#call(); when called on a String, it only returns the
|
||||
string itself, discarding any input.
|
||||
###
|
||||
|
||||
Function.prototype.fcall = Function.prototype.call;
|
||||
String.prototype.fcall = () -> return this
|
||||
|
||||
#Adds the "fade" class to a htmlString.
|
||||
String.prototype.fade = () ->
|
||||
return this.classList.add("fade")
|
||||
|
||||
###
|
||||
The prototype RaconteurSituation is the basic spec for situations
|
||||
created with Raconteur. It should be able to handle any use case for Undum.
|
||||
This prototype is fairly complex; see the API documentation.
|
||||
###
|
||||
|
||||
RaconteurSituation = (spec) ->
|
||||
if RaconteurSituation.arguments.length == 0
|
||||
return
|
||||
undum.Situation.call(this, spec)
|
||||
|
||||
for key, value of spec
|
||||
this[key] ?= value
|
||||
|
||||
@visited = 0
|
||||
return this
|
||||
RaconteurSituation.inherits(undum.Situation)
|
||||
|
||||
###
|
||||
Situation.prototype.act() is called by Undum whenever an action link
|
||||
(Ie, a link that doesn't point at another situation or an external URL) is
|
||||
clicked.
|
||||
|
||||
Raconteur's version of act() is set up to implement commonly used
|
||||
functionality: "writer" links, "replacer" links, "inserter" links, and
|
||||
generic "action" links that call functions which access the underlying
|
||||
Undum API.
|
||||
###
|
||||
|
||||
RaconteurSituation.prototype.act = (character, system, action) ->
|
||||
actionClass = action.match(/^_(\w+)_(.+)$/)
|
||||
that = this
|
||||
|
||||
responses = {
|
||||
writer: (ref) ->
|
||||
content = @writers[ref].fcall(that, character, system, action)
|
||||
output = markdown(content).fade()
|
||||
system.writeInto(output, '#current-situation')
|
||||
replacer: (ref) ->
|
||||
content = @writers[ref].fcall(that, character, system, action)
|
||||
output = markdown(content).fade()
|
||||
system.replaceWith(output, '#'+ref)
|
||||
inserter: (ref) ->
|
||||
content = @writers[ref].fcall(that, character, system, action)
|
||||
output = markdown(content).fade()
|
||||
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}");
|
||||
|
||||
RaconteurSituation.prototype.register = () ->
|
||||
if not @name?
|
||||
console.error("Situation has no name")
|
||||
return this
|
||||
undum.game.situations[@name] = this
|
||||
return this
|
||||
|
||||
module.exports = RaconteurSituation
|
16
package.json
16
package.json
|
@ -1,15 +1,25 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"undum-commonjs": "git://github.com/oreolek/undum-commonjs#commonjs",
|
||||
"jquery": "^2.1.3"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"babelify": "^6.0.2",
|
||||
"browser-sync": "^2.6.0",
|
||||
"browserify": "^9.0.8",
|
||||
"browserify-shim": "^3.8.8",
|
||||
"coffeeify": "^1.0.0",
|
||||
"gulp": "^3.8.11",
|
||||
"gulp-uglify": "^1.2.0",
|
||||
"gulp-sass": "^2.1.1",
|
||||
"gulp-coffee": "^2.3.1",
|
||||
"gulp-util": "^3.0.4",
|
||||
"gulp-zip": "^3.0.2",
|
||||
"gulp-shell": "^0.5.1",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"browser-sync": "^2.11.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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ $body-bg: #F1EED9;
|
|||
$body-color: #58351A;
|
||||
$link-color: #382313;
|
||||
$btn-bg: #C33601;
|
||||
$btn-color: $body-color;
|
||||
$btn-color: lighten($btn-bg, 50%);
|
||||
$secondary-bg: #F1EED9;
|
||||
|
||||
$waycolor: $link-color;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 08031d6a76337e9e6d0c7aa3d034b8006e26e705
|
||||
Subproject commit 643bd8eaeb7a2a692fec3add22a3b61eff0fb62c
|
|
@ -9,7 +9,11 @@
|
|||
@import "bootstrap/scss/type";
|
||||
@import "bootstrap/scss/images";
|
||||
@import "bootstrap/scss/grid";
|
||||
@import "bootstrap/scss/buttons";
|
||||
//@import "bootstrap/scss/buttons";
|
||||
|
||||
@import "bootstrap/scss/animation";
|
||||
@import "bootstrap/scss/dropdown";
|
||||
@import "bootstrap/scss/nav";
|
||||
@import "bootstrap/scss/responsive-embed";
|
||||
@import "bootstrap/scss/utilities";
|
||||
|
||||
|
@ -18,22 +22,67 @@ body {
|
|||
overflow-x: hidden;
|
||||
background: $body-bg;
|
||||
}
|
||||
// The title block
|
||||
.title {
|
||||
margin-top: 2em;
|
||||
@include col(10,12);
|
||||
@media (min-width: breakpoint-min(sm)) {
|
||||
@include make-col-offset(1);
|
||||
}
|
||||
cursor: pointer; // Until we click to start.
|
||||
.label {
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
max-width: 18em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: smaller;
|
||||
color: #aaa;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
text-shadow: rgba(255,255,255,0.5) 2px 2px 2px,
|
||||
rgba(0,0,0,0.1) -1px -1px 2px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.warnings {
|
||||
font-size: small;
|
||||
font-style: italic;
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
.noscript_message {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
color: #943;
|
||||
}
|
||||
}
|
||||
#tools_wrapper {
|
||||
display: none; // Shown by Javascript
|
||||
.ways {
|
||||
padding: 0.5em;
|
||||
// @include col(4, 5);
|
||||
@include col(9, 10);
|
||||
@include col(8, 9);
|
||||
@media (min-width: breakpoint-min(sm)) {
|
||||
@include make-col-offset(1);
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
@include col(1, 2);
|
||||
button {
|
||||
@extend .btn;
|
||||
@include button-variant($btn-color, $btn-bg, $btn-color);
|
||||
margin-bottom: 1em;
|
||||
.destination {
|
||||
font-weight: bold;
|
||||
}
|
||||
.menu {
|
||||
@include col(3, 4);
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +90,7 @@ body {
|
|||
background: $text_background;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#content {
|
||||
.content {
|
||||
@include col(10, 12);
|
||||
@media (min-width: breakpoint-min(sm)) {
|
||||
@include make-col-offset(1);
|
||||
|
@ -97,7 +146,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)) {
|
||||
|
@ -120,6 +168,11 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
#content_library,
|
||||
#ui_library {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.way {
|
||||
color: $waycolor;
|
||||
margin-right: 1em;
|
||||
|
@ -128,6 +181,20 @@ body {
|
|||
color: darkgreen;
|
||||
border-bottom: darkgreen dashed 1px;
|
||||
}
|
||||
ul.options {
|
||||
border: 1px solid #876;
|
||||
li {
|
||||
border-bottom: 1px solid #876;
|
||||
}
|
||||
li:hover {
|
||||
background-color: rgba(153,136,119,0.2);
|
||||
}
|
||||
}
|
||||
#legal {
|
||||
.muted {
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
hr {
|
||||
width: 50%;
|
||||
border-color: $body-color;
|
||||
|
|
Loading…
Reference in a new issue