commit 9df72b5ae34dc42d7947e7415619efcc67c33c73 Author: Oreolek Date: Tue Dec 1 13:42:52 2015 +0700 Initial commit: shoot and reload 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.js b/Gulpfile.js new file mode 100644 index 0000000..b06e959 --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,161 @@ +'use strict'; + +/* Raconteur Gulpfile scaffold. */ +/* Includes code adapted from Gulp documentation, among other sources. */ + +/* Imports */ +var watchify = require('watchify'), + browserify = require('browserify'), + browserSync = require('browser-sync'), + gulp = require('gulp'), + source = require('vinyl-source-stream'), + gutil = require('gulp-util'), + coffeify = require('coffeeify'), + less = require('gulp-less'), + minifyCSS = require('gulp-minify-css'), + uglify = require('gulp-uglify'), + buffer = require('vinyl-buffer'), + zip = require('gulp-zip'), + _ = require('lodash'); + +var reload = browserSync.reload; + +/* Tasks */ + +/* Trivial file copies */ + +function html (target) { + return function () { + return gulp.src('html/index.html') + .pipe(gulp.dest(target)); + }; +} + +function img (target) { + return function () { + return gulp.src(['img/*.png', 'img/*.jpeg', 'img/*.jpg']) + .pipe(gulp.dest(target)); + }; +} + +gulp.task('html', html('./build')); +gulp.task('img', img('./build/img')); + +/* Less */ + +gulp.task('less', function () { + gulp.src('less/main.less') + .pipe(less()) + .pipe(gulp.dest('./build/css')); +}); + +/* Bundle libraries */ + +var undumBundler = browserify({debug: true}); +undumBundler.require('undum-commonjs'); + +gulp.task('buildUndum', function () { + return undumBundler.bundle().pipe(source('undum.js')).pipe(gulp.dest('./build/game')); +}); + +/* Generate JavaScript with browser sync. */ + +var customOpts = { + entries: ['./game/main.coffee'], + debug: true, + transform: [coffeify] +}; + +var opts = _.assign({}, watchify.args, customOpts); +var bundler = watchify(browserify(opts)); +bundler.external('undum-commonjs'); + +gulp.task('coffee', ['buildUndum'], bundle); // `gulp coffee` will generate bundle +bundler.on('update', bundle); // Re-bundle on dep updates +bundler.on('log', gutil.log); // Output build logs to terminal + +function bundle () { + return bundler.bundle() + .on('error', gutil.log.bind(gutil, 'Browserify Error')) + .pipe(source('bundle.js')) + .pipe(gulp.dest('./build/game')); +}; + +/* Make a development build */ + +gulp.task('build', ['html', 'img', 'less', 'coffee'], function () { + +}); + +/* Start a development server */ + +gulp.task('serve', ['build'], function () { + browserSync({ + server: { + baseDir: 'build' + } + }); + + var lessListener = function () { + reload('./build/css/main.css'); + } + + gulp.watch(['./html/*.html'], ['html']); + gulp.watch(['./less/*.less'], ['less']); + gulp.watch(['./img/*.png', './img/*.jpeg', './img/*.jpg'], ['img']); + + gulp.watch(['./build/css/main.css'], lessListener); + 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('less-dist', function () { + return gulp.src('./less/main.less') + .pipe(less()) + .pipe(minifyCSS()) + .pipe(gulp.dest('./dist/css')); +}); + +var distBundler = browserify({ + debug: false, + entries: ['./game/main.coffee'], + transform: ['coffeeify'] +}); + +distBundler.external('undum-commonjs'); + +gulp.task('coffee-dist', ['undum-dist'], 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', 'less-dist', 'coffee-dist'], + function () { + return; +}); + +gulp.task('zip', ['dist'], function () { + return gulp.src('dist/**') + .pipe(zip('dist.zip')) + .pipe(gulp.dest('.')); +}); \ No newline at end of file diff --git a/game/main.coffee b/game/main.coffee new file mode 100644 index 0000000..3516649 --- /dev/null +++ b/game/main.coffee @@ -0,0 +1,165 @@ +# copyright (c) Alexander Yakovlev 2015. +# Distributed under the MIT license. See LICENSE for information. + +situation = require('raconteur') +undum = require('undum-commonjs') +$ = require('jquery') +oneOf = require('raconteur/lib/oneOf.js') +elements = require('raconteur/lib/elements.js') +qualities = require('raconteur/lib/qualities.js') +colour = require("color") +md = require('markdown-it') +markdown = new md({ + typographer: true, + html: true +}) + +a = elements.a +span = elements.span +img = elements.img + +undum.game.id = "7a1aba32-f0fd-4e3b-ba5a-59e3fa9e6012" +undum.game.version = "0.1" + +way_to = (content, ref) -> a(content).class('way').ref(ref) +textlink = (content, ref) -> a(content).once().writer(ref) +is_visited = (situation) -> undum.game.situations[situation].visited == 1 +writemd = (system, text) -> + if typeof text is Function + text = text() + system.write(markdown.render(text)) + +link_colour = "#B68000" + +spend_bullet = (character, system) -> + bullets = character.sandbox.clips[character.sandbox.current_clip] + if bullets >= 1 + character.sandbox.clips[character.sandbox.current_clip]-- + system.setQuality("bullets", bullets - 1) + +spend_clip = (character, system) -> + clips = character.sandbox.clips.length + bullets = character.sandbox.clips[character.sandbox.current_clip] + if clips == 0 + return + if bullets == 0 + character.sandbox.clips.splice(character.sandbox.current_clip, 1) + system.setQuality("bullets", character.sandbox.clips[character.sandbox.current_clip]) + system.setQuality("clips", clips - 1) + writemd(system, "Я выбрасываю пустой картридж.") + else + if character.sandbox.current_clip < clips - 1 + character.sandbox.current_clip++ + else + character.sandbox.current_clip == 0 + system.setQuality("bullets", character.sandbox.clips[character.sandbox.current_clip]) + +situation 'start', + content: """ + -- Проклятье, они продолжают идти! + + Узкий коридор, я и непрекращающаяся очередь сверкающих белоснежной кожей андроидов. + Я уверен, что я представлял этот Новый Год совершенно не так. + """, + choices: ["#shoot"], + +situation "hit", + content: (character, system, from) -> + response = oneOf( + "Голова андроида взрывается снопом сверкающих искр.", + "Андроид пытается увернуться, но попадает точнёхонько под пулю. Он падает, разливая масло на пол." + ).randomly(system) + return response() + choices: ["#shoot"] + before: (character, system, from) -> + system.setQuality("enemies", character.qualities.enemies - 1) + character.sandbox.nicked = 0 + choices: ["#shoot"] + +situation "nicked", + content: (character, system, from) -> + if character.sandbox.nicked == 1 + system.setQuality("enemies", character.qualities.enemies - 1) + character.sandbox.nicked = 0 + response = oneOf( + "Я добиваю андроида выстрелом в сердце.", + "Я добиваю андроида точным выстрелом", + "Пуля пробивает голову андроида, и он наконец падает на пол без движения.", + ).randomly(system) + return response() + else + character.sandbox.nicked = 1 + response = oneOf( + "Я отстреливаю ногу врага. Он падает, но продолжает медленно царапать путь ко мне руками." + "Я простреливаю руку андроида. Он пошатывается, но продолжает идти." + ).randomly(system) + return response() + choices: ["#shoot"] + +situation "miss", + content: (character, system, from) -> + response = oneOf( + "Пуля пролетает над левым плечом андроида.", + "Андроид вовремя уворачивается от выстрела. Ничего, в следующий раз я не промахнусь." + ).randomly(system) + return response() + choices: ["#shoot"] + +situation "shoot", + tags: ["shoot"], + optionText: (character, system, from) -> + response = oneOf( + "Выстрел", + "СТРЕЛЯТЬ!", + "УБИТЬ ИХ ИЗ ПИСТОЛЕТА", + "НЕНАВИЖУ АНДРОИДОВ" + ).stopping(system) + return response() + canChoose: (character, system) -> + return character.qualities.bullets > 0 + before: (character, system, from) -> + spend_bullet(character, system) + system.clearContent() + after: (character, system, from) -> + # d20 roll + # 1-14 - hit, 15-18 - nicked, 19-20 = miss + roll = system.rnd.randomInt(1,20) + switch + when roll < 15 then system.doLink("hit") + when roll > 18 then system.doLink("miss") + else system.doLink("nicked") + +situation "reload", + tags: ["shoot"], + choices: ["#shoot"], + optionText: "Перезарядить пистолет", + canView: (character, system) -> + return character.qualities.bullets < 6 + before: (character, system) -> + system.clearContent() + after: (character, system) -> + spend_clip(character, system) + writemd(system, "Я вставляю другой картридж в пистолет. Надеюсь, в нём есть патроны.") + return true + +# А теперь plot twist: у пистолета есть шанс осечки и промаха. Ты теряешь патрон. Всего патронов у тебя 36, а врагов 35. + +situation "finale", + content: """ + """, + +qualities + head: + bullets: qualities.integer('Патронов в картридже'), + clips: qualities.integer('Картриджей с патронами'), + enemies: qualities.integer('Врагов впереди'), + +undum.game.init = (character, system) -> + system.setQuality("bullets", 6) + system.setQuality("clips", 6) + system.setQuality("enemies", 35) + character.sandbox.clips = [6,6,6,6] + character.sandbox.current_clip = 0 + character.sandbox.nicked = 0 + +window.onload = undum.begin diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..a922816 --- /dev/null +++ b/html/index.html @@ -0,0 +1,94 @@ + + + + + + Тридцать пять выстрелов + + + + + +
+

Тридцать пять выстрелов

+ +
+ + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+ +

Тридцать пять выстрелов

+ +

нажмите, чтобы начать

+
+
+ +
+
+
+ +
+ + +
+
+ +
+
+ + +
+ +
+

+
+
+
+ +
+ + +
+
+
+
+ + +
+ +
+
+ +
+ + + + diff --git a/less/animations.less b/less/animations.less new file mode 100644 index 0000000..ba145f1 --- /dev/null +++ b/less/animations.less @@ -0,0 +1,75 @@ +/* Animate newly inserted DOM elements within content */ + +/* "Why don't we just use opacity?" + "Because opacity, creates massive jank and this doesn't. Browsers!" +*/ +.fade-in() { + animation: fadeIn 500ms ease-in-out; + -webkit-animation: fadeIn 500ms ease-in-out; + //-moz-animation: fadeIn 500ms; + a { + animation: fadeInA 500ms ease-in-out; + -webkit-animation: fadeInA 500ms ease-in-out; + } + +} + +@keyframes fadeIn { + from { + color: rgba(0,0,0,0); + } + to { + color: rgba(0,0,0,1); + } +} + +@-webkit-keyframes fadeIn { + from { + color: rgba(0,0,0,0); + } + to { + color: rgba(0,0,0,1); + } +} + +@keyframes fadeInA { + 0% { + color: transparentize(@anchor-colour, 1); + } + 100% { + color: transparentize(@anchor-colour, 0); + } +} + +@-webkit-keyframes fadeInA { + from { + color: transparentize(@anchor-colour, 1); + } + to { + color: transparentize(@anchor-colour, 0); + } +} + +@keyframes fadeInS { + 0% { + color: transparentize(@option-colour, 1); + } + 100% { + color: @option-colour; + } +} + +@-webkit-keyframes fadeInS { + from { + color: transparentize(@option-colour, 1); + } + to { + color: @option-colour; + } +} + +.fade { + .fade-in(); +} + + diff --git a/less/grid.less b/less/grid.less new file mode 100644 index 0000000..88d82bd --- /dev/null +++ b/less/grid.less @@ -0,0 +1,57 @@ +///////////////// +// Semantic.gs // for LESS: http://lesscss.org/ +///////////////// + +// Defaults which you can freely override +@column-width: 60; +@gutter-width: 20; +@columns: 12; + +// Utility variable — you should never need to modify this +@gridsystem-width: (@column-width*@columns) + (@gutter-width*@columns) * 1px; + +// Set @total-width to 100% for a fluid layout +@total-width: @gridsystem-width; + +// The micro clearfix http://nicolasgallagher.com/micro-clearfix-hack/ +.clearfix() { + *zoom:1; + + &:before, + &:after { + content:""; + display:table; + } + &:after { + clear:both; + } +} + + +////////// +// GRID // +////////// + +body { + width: 100%; + .clearfix; +} + +.row(@columns:@columns) { + display: block; + width: @total-width*((@gutter-width + @gridsystem-width)/@gridsystem-width); + margin: 0 @total-width*(((@gutter-width*.5)/@gridsystem-width)*-1); + .clearfix; +} +.column(@x,@columns:@columns) { + display: inline; + float: left; + width: @total-width*((((@gutter-width+@column-width)*@x)-@gutter-width) / @gridsystem-width); + margin: 0 @total-width*((@gutter-width*.5)/@gridsystem-width); +} +.push(@offset:1) { + margin-left: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width); +} +.pull(@offset:1) { + margin-right: @total-width*(((@gutter-width+@column-width)*@offset) / @gridsystem-width) + @total-width*((@gutter-width*.5)/@gridsystem-width); +} diff --git a/less/layout.less b/less/layout.less new file mode 100644 index 0000000..d2e64dd --- /dev/null +++ b/less/layout.less @@ -0,0 +1,460 @@ +@import 'grid.less'; + +@columns: 12; +@column-width: 60; +@gutter-width: 20; +@total-width: 100%; + +#page { + .row(); +} +.mid_panel { + .column(9); +} +.full_panel { + .column(10); + .push(1); +} +#tools_wrapper { + .column(3); + background: @text_background; + position: sticky; + top: 2em; +} +.tools { + margin: 1.1em auto; + padding: 0 1em; +} +#content_wrapper { + margin: 1.1em auto; + padding: 2.8em; + display: none; /* Shown by Javascript */ + overflow: auto; +} +@media (min-width: 981px) { + #content_wrapper { + margin: 1.1em 15%; + } +} +@media (min-width: 1281px) { + #content_wrapper { + margin: 1.1em 25%; + } +} +@media screen and (max-width: 720px) { + .mid_panel, + .full_panel { + .column(12); + margin-bottom: 1em; + } +} + +body { + background: @background; + color: @color; + font-family: @font-body; + font-size: 18px; + line-height: 1.6em; + background-attachment: fixed; + overflow-y: scroll; + overflow-x: hidden; +} + +/* The title block */ +#title, #title .label, #content, .tools { + border-radius: 2px; +} +#title { + max-width: 28em; + margin: 2.2em auto 1.1em auto; + padding: 1.7em; + cursor: pointer; /* Until we click to start. */ + .label { + overflow: hidden; + padding: 2.0em; + margin: auto; + max-width: 18em; + position: relative; + text-align: center; + } + .subtitle { + font-size: smaller; + color: #aaa; + } + h1 { + font-size: 1.6em; + line-height: 1.4em; + font-family: @font-title; + letter-spacing: 0.2em; + font-weight: normal; + padding-bottom: 1.1em; + span.fancy { + font-size: 2.5em; + line-height: 0; + font-family: Tangerine, Palatino, Times, "Times New Roman", serif; + font-style: italic; + margin: 0 -0.2em; + } + } + h2 { + font-size: 1.2em; + font-weight: normal; + margin: 1.1em 0 0 0; + } + h3 { + font-size: 1.0em; + font-weight: normal; + margin: 1.1em 0 0 0; + } + h1, + h2, + h3 { + color: rgba(33,17,0,0.9); + text-shadow: rgba(255,255,255,0.5) 2px 2px 2px, + rgba(0,0,0,0.1) -1px -1px 2px; + } + .warnings { + font-size: small; + font-style: italic; + p { + margin-bottom: 1em; + } + } + .click_message { + display: none; + left: 0; + right: 0; + bottom: 0; + position: absolute; + font-size: 0.9em; + font-style: italic; + text-align: center; + color: #987; + } + .noscript_message { + left: 0; + right: 0; + bottom: 0; + position: absolute; + font-size: 0.9em; + font-style: italic; + text-align: center; + color: #943; + } +} + +/* Main content */ +#content_wrapper { + background: @text_background; +} +span.drop + p { + text-indent: -0.4em; +} +p { + margin: 0; + -webkit-transition: text-indent 0.25s ease; + transition: text-indent 0.25s ease; +} +hr { + border: none; + background-color: rgba(0,0,0,0.25); + margin: -1px 0 -1px -2.8em; + width: 1.1em; + height: 2px; +} +#content { + p { + text-indent: 1.6em; + } + section { + border-top: 1px dashed #bbb; + } +} +#content h1 + p:first-line, +#content h1 + img + p:first-line { + font-weight: bold; + color: rgba(0,0,0,0.85); +} +#content h1 + p:first-letter, +#content h1 + img + p:first-letter { + position: relative; + padding-top: 0.1em; + display: block; + float: left; + font-weight: normal; + font-size: 3.2em; + line-height: 0.8em; + color: #210; +} +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; + } +} + +h1 { + font-size: 1.0em; + text-transform: uppercase; + letter-spacing: 2px; + margin: 2.3em 0 1.1em 0; + color: #210; + text-align: center; +} +h1:first-child { + margin-top: 0; +} +a { + color: @links; + text-decoration: none; + border-bottom: 1px solid transparent; +} +a:hover { + border-bottom: 1px dotted #900; +} + +img.right { + float: right; + margin: 1.1em 0 1.1em 1.1em; +} +img.left { + float: left; + margin: 1.1em 1.1em 1.1em 0; +} + +#toolbar, #tools_wrapper { + display: none; +} + +.tools { + p { + font-size: 0.95em; + line-height: 1.5em; + margin-top: 6px; + } + h1 { + font-size: 1.0em; + font-weight: normal; + margin-bottom: 0.6em; + } +} +.buttons { + padding-top: 0.6em; + text-align: center; + button { + font-size: 0.8em; + background: #876; + color: #e6e6c6; + border: none; + padding: 0.3em 1.0em; + cursor: pointer; + border-radius: 4px; + } + button:hover { + background: #987; + } + button + button { + margin-left: 0.3em; + } + button[disabled], button[disabled]:hover { + background: #ba9; + color: #dcb; + cursor: default; + } +} + +#legal { + max-width: 33em; + color: #654; + margin: 1em auto 0 auto; + padding-bottom: 2.2em; + display: none; /* Shown by Javascript */ + p { + font-size: 0.7em; + line-height: 1.3em; + margin-bottom: 0.5em; + } + p + p { + text-indent: 0; + } +} + +#character { + font-size: 1.0em; + line-height: 1.4em; +} +#qualities .quality, #character_text { + position: relative; + clear: both; + overflow: hidden; + margin: 0 -0.25em; + padding: 0 0.25em; +} +#character_text { + margin-bottom: 0.6em; + #character_text_content { + position: relative; + font-size: smaller; + z-index: 100; + } +} +#qualities{ + span { + position: relative; + z-index: 100; + } + span.name { + float: left; + } + span.value { + float: right; + } + h2 { + margin: 0.5em 0 0.25em 0; + font-size: 1.0em; + } +} +.highlight { + background: rgba(255, 255, 0, 0.75); + position: absolute; + left: -4px; + right: -4px; + top: 0; + bottom: 0; +} +#content_library, +#ui_library, +#menu { + display: none; +} + +@media screen and (max-width: 640px) { + body { + margin: 0; + font-size: 18.5px; + line-height: 1.5em; + } + +/* Title */ + #title { + margin-top: -1.5em; + padding: 1.0em 0.5em; + .label { + font-size: 0.65em; + max-width: 25em; + padding: 2.0em; + } + } + +/* Side panels */ + #tools_wrapper { + position: static; + } + .tools { + background-image: url("../img/text_bg.jpg"); + position: relative; + width: auto; + } + #character_panel, + #info_panel, + #menu { + display: none; + } + #tools_wrapper { + display: block; + } + +/* Main content */ + #content_wrapper { + width: auto; + padding: 2.0em; + } + #content { + font-size: 16px; + line-height: 1.5em; + } + +/* Toolbar and menu */ + #toolbar { + position: fixed; + z-index: 300; + left: 0; + right: 0; + top: 0; + background: transparent url("../img/toolbar_bg.jpg") repeat-x top left; + height: 36px; + padding: 8px; + overflow: hidden; + box-shadow: 0 0 16px rgba(0,0,0,0.75); + h1 { + float: left; + font-weight: normal; + font-size: 22px; + margin: 8px 0 0 0; + padding: 0 10px; + color: #fc6; + text-shadow: 0 -1px 0 rgba(0,0,0,0.4); + } + .nav { + float: right; + margin: 0; + a { + font-size: 16px; + line-height: 20px; + color: white; + padding: 4px 16px; + float: right; + text-decoration: none; + text-shadow: 0 1px 0 rgba(0,0,0,0.4); + -webkit-border-radius: 6px; + background-image: -webkit-gradient(linear, left top, left bottom, + from(#C00), color-stop(0.45, #a00), + color-stop(0.55, #900), to(#900)); + border: 2px solid #600; + } + } + } + + #menu { + position: fixed; + top: 52px; + left: 0; + right: 0; + font-size: 16px; + background-image: url("../img/tools_bg.jpg"); + z-index: 200; + list-style-type: none; + padding: 10px 0 0 0; + margin: 0; + opacity: 0.95; + box-shadow: 0 0 16px rgba(0,0,0,0.75); + li { + border-bottom: 1px solid rgba(0,0,0,0.25); + } + li:last-child { + border-bottom: none; + } + a { + display: block; + padding: 10px 20px; + } + } +} +.center-block { + margin: 0 auto; +} diff --git a/less/main.less b/less/main.less new file mode 100644 index 0000000..1223b95 --- /dev/null +++ b/less/main.less @@ -0,0 +1,26 @@ +@text-colour: rgba(0,0,0,0.9); +@anchor-colour: #D29506; +@option-colour: #125D79; + +@background: lightgrey; +@text_background: #e6e6c6; +@color: black; +@links: #B68000; +@font-title: 'PT Sans Caption', "PT Sans", sans-serif; +@font-body: 'PT Sans', 'Open Sans', sans-serif; + +@import "animations.less"; +@import "layout.less"; + +.way { + color: darkred; +} +ul.options { + border: 1px solid #876; + li { + border-bottom: 1px solid #876; + } + li:hover { + background-color: rgba(153,136,119,0.2); + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f3559f1 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "dependencies": { + "undum": "git://github.com/oreolek/undum-commonjs#commonjs", + "raconteur": "git://github.com/sequitur/raconteur.git#stable", + "jquery": "^2.1.3", + "markdown-it": "^4.1.0", + "color": "^0.10.1" + }, + "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-gzip": "^1.1.0", + "gulp-less": "^3.0.2", + "gulp-minify-css": "^1.0.0", + "gulp-uglify": "^1.2.0", + "gulp-util": "^3.0.4", + "gulp-zip": "^3.0.2", + "lodash": "^3.6.0", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.1.0", + "watchify": "^3.1.0" + } +}