commit f1a6d47dea68755b52ca4a0990ac66af4f8dc740 Author: Alexander Yakovlev Date: Thu Jan 7 18:27:57 2016 +0700 Initial commit - scaffold 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/.gitmodules b/.gitmodules new file mode 100644 index 0000000..00a446f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sass/bootstrap"] + path = sass/bootstrap + url = https://github.com/twbs/bootstrap.git diff --git a/Gulpfile.coffee b/Gulpfile.coffee new file mode 100644 index 0000000..6e266ef --- /dev/null +++ b/Gulpfile.coffee @@ -0,0 +1,118 @@ +browserSync = require('browser-sync') +gulp = require('gulp') +coffee = require("gulp-coffee") +sass = require('gulp-sass') +zip = require('gulp-zip') +concat = require('gulp-concat') + +reload = browserSync.reload; + +# Copy assets over without touching them +assets = (target) -> + return () -> + return gulp.src([ + 'img/*.png', + 'img/*.jpeg', + 'img/*.jpg' + ]).pipe(gulp.dest(target)) + +gulp.task('assets', assets('./build')); + +gulp.task('sass', function () { + gulp.src('sass/main.scss') + .pipe(sass().on('error', sass.logError)) + .pipe(gulp.dest('./build/css')); +}); + +gulp.task('html', html('./build')); + +gulp.task('concatCoffee', function() { + return gulp.src([ + './game/begin.coffee', + './game/story.coffee', + './game/init.coffee', + ]) + .pipe(concat('./main.coffee')) + .pipe(gulp.dest('./build/game')); +}); + +gulp.task('coffee', ['concatCoffee']) + +bundler.on('update', coffee); +bundler.on('log', gutil.log); # Output build logs to terminal + +gulp.task('build', ['html', 'img', 'sass', 'coffee']) + +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/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('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', 'legal-dist'], + function () { + return; +}); + +gulp.task('zip', ['dist'], function () { + return gulp.src('dist/**') + .pipe(zip('dist.zip')) + .pipe(gulp.dest('.')); +}); diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c93169a --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2016 Alexander Yakovlev, https://oreolek.ru/ + +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. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9637290 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Salet +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. + +## 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. diff --git a/game/begin.coffee b/game/begin.coffee new file mode 100644 index 0000000..0e6a24f --- /dev/null +++ b/game/begin.coffee @@ -0,0 +1,170 @@ +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. + +undum.game.id = "6a909a4-586a-4089-bd18-26da684d1c8d" +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() +# usage: writemd( system, "Text to write") +writemd = (system, text) -> + if typeof text is Function + text = text() + text = markdown.render(text) + system.write(text) + +preparemd = (text, mark) -> + if typeof text is Function + text = text() + text = markdown.render(text) + if mark? + text = """ +
+ #{text} +
+ """ + 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", + 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) + +
+ """ + +is_visited = (situation) -> + situations = undum.game.situations[situation] + if situations + return Boolean situations.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") diff --git a/game/init.coffee b/game/init.coffee new file mode 100644 index 0000000..4057f96 --- /dev/null +++ b/game/init.coffee @@ -0,0 +1,14 @@ +# 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 diff --git a/game/story.coffee b/game/story.coffee new file mode 100644 index 0000000..e69de29 diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..399db6d --- /dev/null +++ b/html/index.html @@ -0,0 +1,49 @@ + + + + + Salet tutorial + + + + + +
+
+
+

Salet tutorial

+
+ +
+
+
+

Other rooms

+
+
+
+ + + + +
+
+ +
+ +
+
+ + + + diff --git a/img/mit.png b/img/mit.png new file mode 100644 index 0000000..fd65d24 Binary files /dev/null and b/img/mit.png differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..ffbd622 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + }, + "private": true, + "devDependencies": { + "gulp": "^3.8.11", + "gulp-uglify": "^1.2.0", + "gulp-sass": "^2.1.1", + "gulp-coffee": "^2.3.1", + "gulp-zip": "^3.0.2", + "gulp-shell": "^0.5.1", + "gulp-concat": "^2.6.0", + "browser-sync": "^2.11.0" + } +} diff --git a/sass/_mixins.scss b/sass/_mixins.scss new file mode 100644 index 0000000..5916f78 --- /dev/null +++ b/sass/_mixins.scss @@ -0,0 +1,19 @@ +@mixin halfcolumn() { + @include make-col(); + @media (min-width: breakpoint-min(sm)) { + @include make-col-span(6); + } + @media (max-width: breakpoint-max(xs)) { + @include make-col-span(12); + } +} +@mixin col($sm-width, $xs-width) { + @include make-col; + @media (min-width: breakpoint-min(sm)) { + @include make-col-span($sm-width); + } + @media (max-width: breakpoint-max(xs)) { + @include make-col-span($xs-width); + } +} + diff --git a/sass/_variables.scss b/sass/_variables.scss new file mode 100644 index 0000000..a2b12ae --- /dev/null +++ b/sass/_variables.scss @@ -0,0 +1,13 @@ +$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: #F1EED9; +$body-color: #58351A; +$link-color: #382313; +$btn-bg: #C33601; +$btn-color: $body-color; +$secondary-bg: #F1EED9; + +$waycolor: $link-color; +$text_background: $body-bg; // can be btn-bg diff --git a/sass/bootstrap b/sass/bootstrap new file mode 160000 index 0000000..08031d6 --- /dev/null +++ b/sass/bootstrap @@ -0,0 +1 @@ +Subproject commit 08031d6a76337e9e6d0c7aa3d034b8006e26e705 diff --git a/sass/main.scss b/sass/main.scss new file mode 100644 index 0000000..7ff2828 --- /dev/null +++ b/sass/main.scss @@ -0,0 +1,134 @@ +@import "mixins"; +@import "variables"; +// Bootstrap v4 stripped core +@import "bootstrap/scss/variables"; +@import "bootstrap/scss/mixins"; +@import "bootstrap/scss/normalize"; +@import "bootstrap/scss/print"; +@import "bootstrap/scss/reboot"; +@import "bootstrap/scss/type"; +@import "bootstrap/scss/images"; +@import "bootstrap/scss/grid"; +@import "bootstrap/scss/buttons"; +@import "bootstrap/scss/responsive-embed"; +@import "bootstrap/scss/utilities"; + +body { + overflow-y: scroll; + overflow-x: hidden; + background: $body-bg; +} +#tools_wrapper { + display: none; // Shown by Javascript + .ways { + padding: 0.5em; + // @include col(4, 5); + @include col(9, 10); + @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; + } + } +} +#content_wrapper { + background: $text_background; + border-radius: 5px; +} +#content { + @include col(10, 12); + @media (min-width: breakpoint-min(sm)) { + @include make-col-offset(1); + } + p { + hyphens: auto; + } + 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; + } + .situation-start { + border-top: none; + } + img.right { + float: right; + margin: 1.1em 0 1.1em 1.1em; + } + img.left { + float: left; + margin: 1.1em 1.1em 1.1em 0; + } + h3 { + text-align: center; + } +} +#legal { + @include col(10,12); + @media (min-width: breakpoint-min(sm)) { + @include make-col-offset(1); + } + 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)) { + @include make-col-span(10); + } + @media (max-width: breakpoint-max(xs)) { + @include make-col-span(12); + } + } + #footright { + text-align: right; + @include make-col(); + @media (min-width: breakpoint-min(sm)) { + @include make-col-span(2); + } + @media (max-width: breakpoint-max(xs)) { + @include make-col-span(12); + margin-bottom: 1em; + } + } +} + +.way { + color: $waycolor; + margin-right: 1em; +} +.cycle { + color: darkgreen; + border-bottom: darkgreen dashed 1px; +} +hr { + width: 50%; + border-color: $body-color; +}