Cloak of Darkness WIP in Salet

master
Alexander Yakovlev 5 years ago
commit c72d6bad55
  1. 4
      .gitignore
  2. 130
      Gulpfile.coffee
  3. 123
      game/begin.coffee
  4. 23
      game/dialogue.coffee
  5. 26
      game/phrase.coffee
  6. 54
      game/story.coffee
  7. 75
      game/translations/en.cson
  8. 17
      game/translations/ru.cson
  9. 70
      html/en.html
  10. 66
      html/index.html
  11. BIN
      img/compass.png
  12. BIN
      img/double-face-mask.png
  13. BIN
      img/light-backpack.png
  14. BIN
      img/white-book.png
  15. 20
      package.json
  16. 31
      sass/_variables.scss
  17. 176
      sass/main.scss

4
.gitignore vendored

@ -0,0 +1,4 @@
node_modules
build
dist
dist.zip

@ -0,0 +1,130 @@
browserSync = require('browser-sync')
gulp = require('gulp')
gutil = require('gulp-util')
coffee = require("gulp-coffee")
sass = require('gulp-sass')
uglify = require('gulp-uglify')
zip = require('gulp-zip')
concat = require('gulp-concat')
rename = require('gulp-rename')
fs = require 'fs'
CSON = require 'cson'
reload = browserSync.reload
html = (target) ->
return () ->
gulp.src(['html/*.html'])
.pipe(gulp.dest(target))
gulp.src(['node_modules/salet/lib/index.min.js'])
.pipe(rename('salet.min.js'))
.pipe(gulp.dest(target+"/game"))
# Images
img = (target) ->
return () ->
return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg']).pipe(gulp.dest(target))
# Audio assets
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'))
# SCSS styles
gulp.task('sass', () ->
gulp.src('sass/main.scss')
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
.pipe(gulp.dest('./build/css'))
)
gulp.task('concatCoffee', () ->
if !fs.existsSync('./build/game/translations')
fs.mkdirSync('./build/game/translations')
for language in ['ru', 'en']
data = CSON.parseCSONFile('game/translations/'+language+'.cson')
json = JSON.stringify(data) + '\n'
fs.writeFileSync("./build/game/translations/"+language+".json", json)
gulp.src([
## additional functions
'./game/dialogue.coffee',
'./game/phrase.coffee',
## the actual game
'./game/begin.coffee',
'./game/story.coffee',
]).pipe(concat('./main.coffee'))
.pipe(gulp.dest('./build/game'))
)
gulp.task('coffee', ['concatCoffee'], () ->
gulp.src('./build/game/main.coffee')
.pipe(coffee({bare: true}))
.pipe(gulp.dest('./build/game/'))
)
gulp.task('build', ['html', 'img', 'sass', 'coffee', 'audio'])
gulp.task('serve', ['build'], () ->
browserSync({
server: {
baseDir: 'build'
}
online: true
browser: []
ghostMode: false
})
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(['./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'))
)
gulp.task('coffee-dist', ['concatCoffee'], () ->
gulp.src('./build/game/main.coffee', {sourcemaps: false})
.pipe(coffee())
.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('.'))
)

@ -0,0 +1,123 @@
salet.game_id = "8b0c371c-57f4-49b3-ae3c-cba07d1a9733"
salet.game_version = "1.0"
salet.beforeAction = (roomId, actionId) ->
verbRe = /verb\_(?:\w+)\_(?:\w+)/
match = verbRe.exec(actionId)
if match? and match[1] and match[2]
verb = match[1]
unit = match[2]
salet.view.write(salet.rooms[roomId].units[unit][verb].fcall(salet.rooms[roomId]))
return true # consume the action
return false
$.holdReady( true )
$.getJSON('game/translations/'+i18n.lang+'.json', (data) ->
i18n.push(i18n.lang, data)
$.holdReady( false )
)
switchTab = (tabid) ->
$(".tab").removeClass("active")
$("#"+tabid).addClass("active")
$(document).ready(() ->
window.addEventListener('popstate', (event) ->
salet.goBack()
)
$("body").on("click", '#night', () ->
if (window.night)
$("body").removeClass("night")
$("#night").removeClass("active")
window.night = false
else
$("body").addClass("night")
$("#night").addClass("active")
window.night = true
)
$("body").on("click", "#storytab", (event) ->
event.preventDefault()
if not salet.here().canSave
salet.goBack()
return false
)
$("body").on("click", ".tab", (event) ->
switchTab(event.target.id)
return true
)
salet.beginGame()
)
updateverb = (unit, verb) ->
if unit[verb]? or salet.character.displayAll
$("##{verb}list").append("<li><a href='./verb_#{verb}_#{unit.name}'>#{unit.display()}</a></li>")
###
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>"
sysroom = (name, options) ->
options.canSave = false
options.enter = () ->
$("#inventory").hide()
options.exit = () ->
$("#inventory").show()
options.dsc = () ->
return @text.fcall()+"\n\n"+"""
<div class="center"><a href="./exit"><button class="btn btn-lg btn-outline-primary">Go back</button></a></div>
"""
options.actions = {
exit: () ->
return salet.goBack()
}
return room(name, options)
croom = (name, spec) ->
spec.clear ?= true
spec.optionColor ?= ""
spec.optionText ?= () ->
retval = """
<div class="#{spec.optionColor}">
<div class="title">#{spec.title.fcall()}</div>
"""
if (spec.subtitle?)
retval += """
<div class="subtitle">#{spec.subtitle.fcall()}</div>
"""
retval += '</div>'
spec.enter = () ->
salet.character.update_sidebar()
if @onEnter?
@onEnter()
return room(name, spec)
sysroom "inventory",
text: () ->
if salet.character.inventory.length == 0
text = "You are carrying nothing."
else
text = "You are carrying:\n\n"
for thing in salet.character.inventory
text += "* #{salet.character.listinv(thing.name)}\n"
sysroom "settings",
text: () ->
nightclass = ""
if window.night
nightclass = "active"
return "credits".l() + """
<button id="filter" class="btn btn-outline-primary}">#{"showall".l()}</button>
<button id="night" class="btn btn-outline-primary #{nightclass}">#{"night".l()}</button>
<button onclick="TogetherJS(this); return false;" class="btn btn-outline-primary">#{"multiplayer".l()}</button>
"""

@ -0,0 +1,23 @@
###
A dialogue shortcut.
Usage:
dialogue "Point out a thing in her purse (mildly)", "start", "mild", """
Point out a thing in her purse (mildly)
""", "character.mild = true"
###
dialogue = (title, startTag, endTag, text, effect) ->
retval = room("dialogue_"+Object.keys(salet.rooms).length, {
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

@ -0,0 +1,26 @@
###
A phrase shortcut.
Usage:
phrase "Point out a thing in her purse (mildly)", "start", """
Point out a thing in her purse (mildly)
""", "character.sandbox.mild = true"
@param title phrase Phrase (question)
@param salet Salet core
@param string tag tag marking viewing condition
@param string text Response
@param string effect an optional parameter, eval'd code
###
phrase = (title, tag, text, effect) ->
retval = room("phrase_"+salet.rooms.length, {
optionText: title
dsc: text
clear: false # backlog is useful in dialogues
choices: "#"+tag
tags: [tag]
})
if effect?
retval.before = (character, system) ->
eval(effect)
return retval

@ -0,0 +1,54 @@
salet.init = () ->
@character.displayAll = false
@character.update_sidebar = () ->
$(".objects").empty()
for obj in document.querySelectorAll(".objects")
for u in salet.here().units
updateverb(u, $(obj).data("verb"))
for u in salet.character.inventory
updateverb(u, $(obj).data("verb"))
cloak = unit "cloak",
dsc: () -> "cloak".l()
display: () -> "cloak_disp".l()
drop: () ->
if (salet.currentRoom != 'cloakroom')
return "drop_cloak".l()
return "hang_cloak".l()
wear: () ->
if (salet.here().has('cloak'))
salet.here().drop('cloak')
salet.character.take('cloak')
return "wear_cloak".l()
else # no cloak in the room, maybe in the inventory?
if salet.character.has('cloak')
return "wear_cloak".l()
else
return "no_cloak".l()
@character.take(cloak)
croom "start",
before: () -> "start".l()
dsc: () -> "foyer".l()
ways: ["entrance", "cloakroom", "bar"]
croom "foyer",
clear: false
dsc: () -> "foyer".l()
ways: ["entrance", "cloakroom", "bar"]
title: () -> "foyer_title".l()
croom "cloakroom",
dsc: () -> "cloakroom".l()
title: () -> "cloakroom_title".l()
ways: ["foyer"]
croom "entrance",
dsc: () -> "entrance".l()
after: () ->
salet.goTo('foyer')
title: () -> "entrance_title".l()
croom "bar",
dsc: () -> "bar".l()
title: () -> "bar_title".l()
ways: ["foyer"]

@ -0,0 +1,75 @@
foyer_title: "Foyer of the Opera House"
start: """
Hurrying through the rainswept November night, you're glad to see the bright
lights of the Opera House.
It's surprising that there aren't more people about but, hey, what do you
expect in a cheap demo game...?
### Cloak of Darkness
#### A basic IF demonstration
##### Roger Filth, remade in Salet by Alexander Yakovlev
"""
foyer: """
You are standing in a spacious hall, splendidly decorated in red
and gold, with glittering chandeliers overhead.
The entrance from the street is to the [north,](north) and there are doorways
[south](south) and [west.](west)
"""
credits: """
The game uses code licensed by MIT license.
Code contributors:
* Alexander Yakovlev
* Andrew Plotkin
* Bruno Dias
* David Eyk
* Dmitry Eliseev
* Ian Millington
* Ivan Narozhny
* Juhana Leinonen
* Reactive Sets
* Michael Neal Tenuis
* Selene
"""
entrance: """
You've only just arrived, and besides, the weather outside seems to be getting worse.
"""
cloakroom: """
The walls of this small room were clearly once lined with hooks,
though now [only one](./hook) remains. The exit is a door to the [east.](cloakroom)
"""
cloakroom_title: "Cloakroom"
entrance_title: "Street entrance"
bar_title: "Foyer bar"
bar: """
The bar, much rougher than you'd have guessed after the opulence
of the foyer to the north, is completely empty.
"""
message: """
There seems to be some sort of {{message}} scrawled in the sawdust on the floor.
"""
message_x: """
The message, neatly marked in the sawdust, reads...
### You have won
"""
message_ruined: """
The message has been carelessly trampled, making it difficult to read.
You can just distinguish the words...
### You have lost
"""
cloak: """
A handsome cloak, of velvet trimmed with satin, and slightly spattered with raindrops.
Its blackness is so deep that it almost seems to suck light from the room.
"""
cloak_disp: "cloak"
hook: "It's just a small brass hook,"
hook_empty: "screwed to the wall."
hook_full: "with {list} hanging on it."
dark: "Blundering around in the dark isn't a good idea!"
drop_cloak: "This isn't the best place to leave a smart cloak lying around."
hang_cloak: "There's a hook here, so you hang the cloak on it."
night: "Night mode"
multiplayer: "Multiplayer mode"
showall: "Filter list of objects"

@ -0,0 +1,17 @@
credits: """
Фоновое изображение: нет
Игра использует код по лицензии MIT. К коду приложили руку:
* Alexander Yakovlev
* Andrew Plotkin
* Bruno Dias
* David Eyk
* Dmitry Eliseev
* Ian Millington
* Ivan Narozhny
* Juhana Leinonen
* Reactive Sets
* Michael Neal Tenuis
* Selene
"""

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cloak of Darkness</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">
<div class="container">
<div class="row">
<div class='ways'>
<ul class="nav nav-pills" id="ways">
</ul>
</div>
</div> <!-- End of div.tools_wrapper -->
<div id="content_wrapper">
<div id="content" class="content narrow">
<noscript>This game requires Javascript.</noscript>
</div>
<div class="sidebar">
<div class="ui">
<a href="#" id="storytab" class="tab active">
<img src="img/white-book.png"> Story
</a>
<a href="inventory" id="chartab" class="tab">
<img src="img/light-backpack.png"> Inventory
</a>
<a href="map" id="maptab" class="tab">
<img src="img/compass.png"> Map
</a>
</div>
<div class="action">
<div class="verb">Examine</div>
<ul class="objects" id="examinelist" data-verb="examine">
</ul>
<div class="verb">Take</div>
<ul class="objects" id="takelist" data-verb="take">
</ul>
<div class="verb">Drop</div>
<ul class="objects" id="droplist" data-verb="drop">
</ul>
<div class="verb">Wear</div>
<ul class="objects" id="wearlist" data-verb="wear">
</ul>
</div>
</div>
<a name="end_of_content"></a>
</div>
<div class="row">
<div class="footer">
<a href="settings">
<button class="btn btn-outline-primary">Options</button>
</a>
<button id="erase" class="btn btn-outline-danger">Restart</button>
</div>
</div>
</div> <!-- End of div.page -->
<!-- CDN JS Libraries -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
<script src="https://togetherjs.com/togetherjs-min.js"></script>
<script type="text/javascript" src="game/salet.min.js"></script>
<script type="text/javascript" defer="defer" src="game/main.js"></script>
</body>
</html>

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Инженер</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">
<div class="container">
<div class="row">
<div class='ways'>
<ul class="nav nav-pills" id="ways">
<li class='nav-item'><a class='nav-link' href="djsd">Жестиана</a></li>
<li class='nav-item'><a class='nav-link' href="fff">Дом на окраине</a></li>
<li class='nav-item'><a class='nav-link' href="33">Заброшенный дом</a></li>
</ul>
</div>
</div> <!-- End of div.tools_wrapper -->
<div id="content_wrapper">
<div id="content" class="content narrow">
<noscript>Эта игра требует включённого Javascript.</noscript>
</div>
<div class="sidebar">
<div class="ui">
<a href="#" id="storytab" class="tab active">
<img src="img/white-book.png"> Повествование
</a>
<a href="character" id="chartab" class="tab">
<img src="img/light-backpack.png"> Инвентарь
</a>
<a href="map" id="maptab" class="tab">
<img src="img/compass.png"> Карта
</a>
</div>
<div class="action">
<div class="verb">ГОВОРИТЬ</div>
<ul>
<li><a href="talk-npc1">NPC 1</a></li>
<li><a href="talk-npc2">NPC 2</a></li>
</ul>
</div>
</div>
<a name="end_of_content"></a>
</div>
<div class="row">
<div class="footer">
<a href="settings">
<button class="btn btn-outline-primary">Настройки</button>
</a>
<button id="erase" class="btn btn-outline-danger">Заново</button>
</div>
</div>
</div> <!-- End of div.page -->
<!-- CDN JS Libraries -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js" crossorigin="anonymous"></script>
<script src="https://togetherjs.com/togetherjs-min.js"></script>
<script type="text/javascript" src="game/salet.min.js"></script>
<script type="text/javascript" defer="defer" src="game/main.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,20 @@
{
"dependencies": {
"salet": "^1.7.5"
},
"private": true,
"devDependencies": {
"bootstrap": "^4.0.0-alpha.5",
"browser-sync": "^2.18.8",
"coffee-script": "^1.12.6",
"cson": "^4.1.0",
"gulp": "^3.8.11",
"gulp-coffee": "^2.3.4",
"gulp-concat": "^2.6.1",
"gulp-rename": "^1.2.2",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^2.1.2",
"gulp-util": "^3.0.8",
"gulp-zip": "^4.0.0"
}
}

@ -0,0 +1,31 @@
$font-family-sans-serif: 'Scada', 'PT Sans', sans-serif;
$font-family-serif: 'PT Serif', serif;
$headings-font-family: $font-family-serif;
//$headings-font-family: "PT Sans Caption",$font-family-sans-serif;
$font-family-base: $font-family-serif;
$body-bg: #eee;
$body-color: #000;
$link-color: darkviolet;
$btn-bg: grey;
$btn-color: lighten($btn-bg, 50%);
$secondary-bg: #F1EED9;
$brand-primary: lighten($body-color, 20%);
$brand-danger: darken(#fff, 30%);
$waycolor: $link-color;
$text_background: transparent;
$animation_duration: 2s;
$enable-rounded: true;
$enable-shadows: false;
$enable-gradients: false;
$enable-transitions: false;
$enable-hover-media-query: false;
$enable-grid-classes: false;
$enable-print-styles: true;
$ok-color: $link-color;
$neutral-color: brown;
$warning-color: darkred;

@ -0,0 +1,176 @@
@import "variables";
@import "../node_modules/bootstrap/scss/bootstrap.scss";
.fixed {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 1000;
width: 100%;
}
body {
// background-image: url('../img/background.png');
background-image: radial-gradient(circle,rgba(0,0,0,.0),rgba(0,0,0,.3));
}
.container {
@include make-container();
@include make-container-max-widths();
border-radius: 5px;
// border-style: solid;
// border-width: 81px 103px 77px 66px;
// border-image: url(../img/border.png) 81 103 77 66 repeat;
}
#ways_wrapper {
@include make-row();
}
#ways {
@include media-breakpoint-up(md) {
@include make-col(6);
@include make-col-offset(3);
}
text-align: center;
.nav-item {
list-style-type: none;
display: inline-block;
@extend .btn;
@extend .btn-outline-primary;
a {
color: $body-color;
}
&:hover a {
color: #fff;
}
}
}
#content_wrapper {
@include make-row();
background: $text_background;
}
.sidebar {
@include media-breakpoint-up(md) {
@include make-col(3);
}
.verb {
margin-top: 1em;
text-align: center;
font-size: larger;
text-transform: uppercase;
}
li {
@extend .btn;
@extend .btn-outline-danger;
display: block;
text-align: left;
}
}
.content {
@include make-col(12);
@include media-breakpoint-up(md) {
@include make-col(10);
@include make-col-offset(1);
}
&.narrow {
@include media-breakpoint-up(md) {
@include make-col(9);
margin-left: 0;
}
}
padding: 1em;
ul {
margin: 0;
padding: 0 0 0 1em;
}
ul.options {
padding: 0;
margin-top: 0.5em;
margin-bottom: 0.7em;
list-style-type: none;
li {
display: inline;
}
li a {
display: block;
margin-bottom: 0.5em;
font-family: $headings-font-family;
text-decoration: none;
> div {
border-radius: 5px;
border: 1px solid #000;
padding: 1em;
background-image: linear-gradient( 45deg, #ccc, #fff );
color: $ok-color;
&:hover {
background-color: rgba(153,136,119,0.2);
background-image: none;
}
}
}
.warning {
background-image: linear-gradient( 45deg, #ddd, #fff );
color: $warning-color;
}
.neutral {
background-image: linear-gradient( 90deg, #ccc, #fff );
color: $neutral-color;
}
}
.room-start {
border-top: none;
}
h3, h4, h5 {
text-align: center;
}
blockquote {
font-family: "EB Garamond", serif;
margin: 1em 2em;
line-height: 1.45;
color: #383838;
font-size: $font-size-base* 1.4;
}
}
.cycle {
color: darkgreen;
border-bottom: darkgreen dashed 1px;
}
hr {
width: 50%;
border-color: $body-color;
}
.btn-outline-primary,
.btn-outline-danger {
border: none;
border-color: transparent;
}
.center {
text-align: center;
}
.gothic {
font-family: "Germania One", cursive;
}
.footer {
@include media-breakpoint-up(md) {
@include make-col(6);
@include make-col-offset(3);
}
text-align: center;
}
.tab {
width: 100%;
position: relative;
text-align: left;
display: block;
padding: 14px 21px;
border-radius: 2px 2px 0 0;
font-size: $font-size-base;
font-weight: normal;
top: 4px;
&:hover {
background: lighten($brand-primary, 10);
}
&.active {
top: 0;
padding-top: 17px;
background: darken($body-bg, 15);
}
}
Loading…
Cancel
Save