From 162135e13ebc582ba092029e0c4c2673b561ffc9 Mon Sep 17 00:00:00 2001 From: Alexander Yakovlev Date: Mon, 23 Jan 2017 21:28:42 +0700 Subject: [PATCH] Initial commit - Being Astrid: The Game --- .gitignore | 4 ++ Gulpfile.coffee | 121 ++++++++++++++++++++++++++++++++++ README.md | 30 +++++++++ game/begin.coffee | 76 ++++++++++++++++++++++ game/dialogue.coffee | 23 +++++++ game/phrase.coffee | 26 ++++++++ game/story.coffee | 15 +++++ html/index.html | 70 ++++++++++++++++++++ package.json | 21 ++++++ sass/_variables.scss | 17 +++++ sass/main.scss | 152 +++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 555 insertions(+) create mode 100644 .gitignore create mode 100644 Gulpfile.coffee create mode 100644 README.md create mode 100644 game/begin.coffee create mode 100644 game/dialogue.coffee create mode 100644 game/phrase.coffee create mode 100644 game/story.coffee create mode 100644 html/index.html create mode 100644 package.json create mode 100644 sass/_variables.scss create mode 100644 sass/main.scss diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca7298e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +build +dist +dist.zip \ No newline at end of file diff --git a/Gulpfile.coffee b/Gulpfile.coffee new file mode 100644 index 0000000..94096bd --- /dev/null +++ b/Gulpfile.coffee @@ -0,0 +1,121 @@ +browserSync = require('browser-sync') +gulp = require('gulp') +source = require('vinyl-source-stream') +gutil = require('gulp-util') +coffee = require("gulp-coffee") +sass = require('gulp-sass') +uglify = require('gulp-uglify') +buffer = require('vinyl-buffer') +zip = require('gulp-zip') +concat = require('gulp-concat') +rename = require('gulp-rename') + +reload = browserSync.reload + +html = (target) -> + return () -> + gulp.src(['html/index.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', () -> + 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' + } + }) + + 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(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('.')) +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..56d8fed --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Salet +A general client-side framework for cybertext interactive fiction games. + +**Salet** is based upon [Undum,](https://github.com/idmillington/undum) rewritten in CoffeeScript and altered to its own needs. +It also uses some code from [Raconteur,](https://github.com/sequitur/raconteur) same deal. + +## License + +The code, documentation, styles, design and images are all distributed under the MIT license. +This permits you to modify and use them, even for commercial use. +A copy of the MIT license is found in the LICENSE file. + +*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.* + +## List of contributors +The list is alphabetical. +Hopefully it will grow. + +* Alexander Yakovlev - Salet's original author +* Andrew Plotkin +* Bruno Dias - Raconteur's original author +* David Eyk +* Dmitry Eliseev +* Ian Millington - Undum's original author +* Ivan Narozhny +* Juhana Leinonen +* Michael Neal Tenuis +* Selene diff --git a/game/begin.coffee b/game/begin.coffee new file mode 100644 index 0000000..4d85aab --- /dev/null +++ b/game/begin.coffee @@ -0,0 +1,76 @@ +salet.game_id = "your-game-id-here" +salet.game_version = "1.6" +ably = new Ably.Realtime('v6yAiA.PKvuDg:iJhwQu-DkAWpDOUB') +channel = ably.channels.get('astrid') + +$(document).ready(() -> + window.addEventListener('popstate', (event) -> + salet.goBack() + ) + $("#night").on("click", () -> + if (window.night) + styles = { + "-webkit-filter": "" + "filter": "" + "background-color": "" + } + $("body").css(styles) + $("#night").removeClass("active") + window.night = false + else + styles = { + "-webkit-filter": "invert(1)hue-rotate(180deg)" + "filter": "invert(1)hue-rotate(180deg)" + "background-color": "#000" + } + $("body").css(styles) + $("#night").addClass("active") + window.night = true + ) + + channel.subscribe('enter', (message) -> + if ( + message.data.room == salet.current and + message.data.name != salet.character.name + ) + salet.view.write("В комнату входит "+message.data.name+".") + ) + + salet.beginGame() +) + +### +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 "#{content}" +textlink = (content, ref) -> + return "#{content}" +actlink = (content, ref) -> + return "#{content}" + +# The first room of the game. +# For accessibility reasons the text is provided in HTML, not here. +room "start", + enter: () -> + names = [ + 'рыжая Астрид', + 'Астрид-хулиганка', + 'волшебная русалка Астрид', + 'Астрид-ведьма' + ] + salet.character.name = names[salet.rnd.randomInt(names.length)] + dsc: "" + choices: "#start" + +croom = (name, options) -> + options.enter = () -> + if (salet.interactive) + channel.publish('enter', { + room: @name, + name: salet.character.name + }) + options.dsc = "### #{options.title}\n" + options.dsc + return room(name,options) diff --git a/game/dialogue.coffee b/game/dialogue.coffee new file mode 100644 index 0000000..1ac529d --- /dev/null +++ b/game/dialogue.coffee @@ -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 diff --git a/game/phrase.coffee b/game/phrase.coffee new file mode 100644 index 0000000..005f630 --- /dev/null +++ b/game/phrase.coffee @@ -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 diff --git a/game/story.coffee b/game/story.coffee new file mode 100644 index 0000000..4dc5413 --- /dev/null +++ b/game/story.coffee @@ -0,0 +1,15 @@ +croom "entry", + tags: ["start"], + optionText: "Быть Астрид" + title: "Прихожая" + ways: ["corridor"] + dsc: """ + Вы находитесь в красивой подводной прихожей. За прозрачными стенами плавают рыбки. + """ + +croom "corridor", + ways: ["entry"] + title: "Коридор" + dsc: """ + Короткий коридор, который замыкается в себе. + """ diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..937fe58 --- /dev/null +++ b/html/index.html @@ -0,0 +1,70 @@ + + + + + Быть Астрид + + + + + + +
+
+
+
+ Эти ссылки ведут в соседние комнаты + +
+
+ + +
+
+
+
+
+
+
+

Быть Астрид

+ +
+
+
+
+
+
+

Вы — Астрид.

+ +

Астрид спит и видит себя.

+

Во сне все люди равны, потому что все они — Астрид.

+ + +
+
+
+
+ +
+ +
+
+ + + + + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..4fb3288 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "dependencies": { + "gulp-rename": "^1.2.2", + "salet": "^1.6.15" + }, + "private": true, + "devDependencies": { + "bootstrap": "^4.0.0-alpha.5", + "browser-sync": "^2.18.6", + "coffee-script": "^1.12.2", + "gulp": "^3.8.11", + "gulp-coffee": "^2.3.3", + "gulp-concat": "^2.6.1", + "gulp-sass": "^3.1.0", + "gulp-uglify": "^2.0.0", + "gulp-util": "^3.0.8", + "gulp-zip": "^3.0.2", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.1.0" + } +} diff --git a/sass/_variables.scss b/sass/_variables.scss new file mode 100644 index 0000000..4d31b92 --- /dev/null +++ b/sass/_variables.scss @@ -0,0 +1,17 @@ +$font-family-sans-serif: 'PT Sans','Open Sans',"Helvetica Neue", Helvetica, Arial, sans-serif; +$headings-font-family: "PT Sans Caption",$font-family-sans-serif; +$font-family-base: $font-family-sans-serif; + +$body-bg: #fff; +$body-color: #000; +$link-color: blue; +$btn-bg: grey; +$btn-color: lighten($btn-bg, 50%); +$secondary-bg: #F1EED9; + +$brand-primary: lighten($body-color, 20%); +$brand-danger: darken($body-bg, 30%); + +$waycolor: $link-color; +$text_background: $body-bg; // can be btn-bg +$animation_duration: 2s; diff --git a/sass/main.scss b/sass/main.scss new file mode 100644 index 0000000..cfb0e79 --- /dev/null +++ b/sass/main.scss @@ -0,0 +1,152 @@ +@import "variables"; +@import "../node_modules/bootstrap/scss/bootstrap.scss"; + +// The title block +.title { + margin-top: 3.5em; + @extend .col-xs-12; + .label { + margin-top: 1.5em; + margin-bottom: 1em; + @extend .col-md-8; + @extend .offset-md-2; + @extend .col-xs-12; + text-align: center; + } + .subtitle { + font-size: smaller; + color: #aaa; + } + 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; + } +} +#choices { + @extend .col-xs-12; +} +.fixed { + position: fixed; + left: 0; + right: 0; + top: 0; + z-index: 1000; + width: 100%; +} +#tools_wrapper { + background: $body-bg; + .ways { + padding: 0.5em; + @extend .col-md-6; + } + .buttons { + @extend .col-md-6; + text-align: right; + } + button { + display: inline-block; + } +} +#content_wrapper { + background: $text_background; + border-radius: 5px; +} +.content { + @extend .col-md-10; + @extend .offset-md-1; + @extend .col-xs-12; + padding: 1em; + ul { + margin: 0; + padding: 0 0 0 1em; + } + ul.options { + padding: 0; + text-align: center; + margin-top: 0.5em; + margin-bottom: 0.7em; + list-style-type: none; + border-radius: 4px; + li { + padding: 0.5em; + } + li:hover { + cursor: pointer; + } + li:last-child { + border-bottom: none; + } + } + section { + border-top: 1px dashed #bbb; + } + .room-start { + border-top: none; + } + h3 { + text-align: center; + } +} +#legal { + margin-top: 1em; + color: darken($body-color, 10%); + font-size: smaller; + #footleft { + @extend .col-md-5; + @extend .offset-md-2; + @extend .col-xs-12; + } + #footright { + text-align: right; + @extend .col-md-2; + @extend .offset-md-2; + @extend .col-xs-12; + } +} + +.way { + color: $waycolor; + margin-right: 1em; +} +.cycle { + 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; +} + +.center { + text-align: center; +}