commit a209caa42d6df9f1be15b06bf17780bda0f87202 Author: Alexander Yakovlev Date: Sat Jan 12 18:24:16 2019 +0700 Начало diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcaf716 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +index.html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5a326ee --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: + tweego src -o index.html +test: + tweego -t src -o index.html diff --git a/src/game.css b/src/game.css new file mode 100644 index 0000000..a96232d --- /dev/null +++ b/src/game.css @@ -0,0 +1,93 @@ +html { + /* Vertical colour gradient */ + background-image: linear-gradient(to bottom, gainsboro, silver); + background-image: -webkit-linear-gradient(top, gainsboro, silver); + background-attachment: fixed; + width: 100%; + /* Fallback colour */ + background-color: papayawhip; +} +body { + background-color: transparent; + margin: 10% 0 0 0; + font-size: 100%; + text-align: center; + width: 100%; +} +hr { + border-top: 1px solid #000; +} + +#passages { + /* Box background (white with 70% opacity) */ + background-color: rgba(255, 255, 255, 0.7); + + /* Border */ + border: 2px solid white; + + /* Rounded corners */ + border-radius: 1em; + + /* Centered box */ + display: inline-block; + width: 60%; + min-height: 40%; + margin:auto; + margin-bottom: 5%; + padding: 0px; +} + +.passage { + margin: 0px; + padding: 2em; + + /* Text formatting */ + color: black; + font-size: 100%; + text-align:justify; +} + +.red, +.green {, + transition: all .7s; + -webkit-transition: all .7s; +} +.red { + color: #420000; +} +.red:hover { + color: #DD0000; +} +.green { + color: #004200; +} +.green:hover { + color: #00DD00; +} + +/* Links */ +a.internalLink, +a.externalLink { + color: royalblue; + padding: 2px; +} +a.internalLink:hover, +a.externalLink:hover { + color: deepskyblue; + text-decoration: none; +} +a.gamepadSelected { + border: 2px dotted blue; + padding: 0px; +} + +/* Shrink the page when viewed on devices with a low screen width */ +@media screen and (max-width: 960px) { + #passages { width: 75%; } +} +@media screen and (max-width: 840px) { + #passages { width: 85%; } +} +@media screen and (max-width: 720px) { + #passages { width: 95%; } +} diff --git a/src/game.tw b/src/game.tw new file mode 100644 index 0000000..113a4d3 --- /dev/null +++ b/src/game.tw @@ -0,0 +1,111 @@ +::StoryTitle +Космические Волны +::StorySettings +ifid:dcd7d6b9-0f25-4ce1-bff1-e603c67066d5 +::StoryConfig [script] +Config.debug = true; +Config.ui.stowBarInitially = true; +Config.passages.nobr = true; +::Start + + + +
+

Космические Волны

+Oreolek +
+ +

Сегодня вечером группа «Космические Волны» выступает с единственным концертом на Луне. Вам необходимо попасть на ракету Москва-кратер Аль-Баттани C. Сумка уже собрана, но можно что-то повесить на себя. Что же взять с собой в дорогу?

+ + + +::guitar +<> +

Вряд ли получится протолкнуться в зону автографов, но вы очень надеетесь на свою уникальную удачу. Без гитары — никуда.

+ +<> + +::hat +<> +

На этой шапке тоже нарисованы уши. Говорят, что сначала лунный холод отмораживает их, а потом уже принимается за ваши собственные.

+ +<> + +::bottle +<> +

Чёрная смородина, мёд, шишки и немного веселья для полного отрыва на концерте. +Будет незабываемо.

+ +<> + +::flight + +
+ +

В ракете вас пристёгивают к жёсткому креслу. <> Вы отвоевали право не расставаться с гитарой, но вот сумку пришлось отдать стюардессе на хранение. <> Сумку пришлось отдать на хранение стюардессе. <>

+ +

На подлёте к Луне вы слышите громкое хлоп. Затем мимо вас проплывает пилот в пухлом скафандре, отталкиваясь руками от кресел.

+ +
    +
  • [[Попробовать освободиться|escape]]
  • +
  • [[Ждать|wait]]
  • +
+ +::escape + +<> + +

Вы отстёгиваете один из ремней. Теперь вы можете поворачивать туловище.

+ +<> + +::wait + +<> + +::escape2 + +

хлоп.

+ +

Внезапно ракета вокруг просто… перестаёт быть. С тихим пуф вы падаете спиной назад на что-то тёплое. +Над головой светит красное небо. +<> + <> + И гитары в руках тоже больше нет. + <> + И шапки на голове тоже больше нет. + <> + И бутылки в кармане тоже больше нет. + <> + <> +<> +

+ +

— Паис! Паис! — кричит женский голос.

+ +

К вам подбегает <><> +Русоволосая девушка лет двадцати в рабочем комбинезоне и резиновых сапогах. +<><> и помогает встать. Вы оглядываетесь: под вами без чувств лежит какой-то мужчина.

+ +

— Паис! Вставай!

+ +

Вы оглядываетесь. Кроваво-красное небо, жёлтые колосья пшеницы в чёрном песке, какие-то дома вдалеке. +Вы не знаете, где очутились.

+ + + +
    +
  • [[Разбудить мужчину|man]]
  • +
  • [[Тихо уйти|away]]
  • +
+ +::away + +

Вы отходите на несколько шагов и наступаете на незаметную яму. +Нога вязнет в чёрном песке, за ней застревает и вторая.

+ +

Девушка расталкивает мужчину; тот встаёт diff --git a/src/gamepad.min.js b/src/gamepad.min.js new file mode 100644 index 0000000..9a5d28f --- /dev/null +++ b/src/gamepad.min.js @@ -0,0 +1,23 @@ +/* @license + * Copyright 2012 Priit Kallas + * + * 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. + */ +!function(a){"use strict";var b=function(){},c={getType:function(){return"null"},isSupported:function(){return!1},update:b},d=function(a){var c=this,d=window;this.update=b;this.requestAnimationFrame=a||d.requestAnimationFrame||d.webkitRequestAnimationFrame||d.mozRequestAnimationFrame;this.tickFunction=function(){c.update();c.startTicker()};this.startTicker=function(){c.requestAnimationFrame.apply(d,[c.tickFunction])}};d.prototype.start=function(a){this.update=a||b;this.startTicker()};var e=function(){};e.prototype.update=b;e.prototype.start=function(a){this.update=a||b};var f=function(a,b){this.listener=a;this.gamepadGetter=b;this.knownGamepads=[]};f.factory=function(a){var b=c,d=window&&window.navigator;d&&("undefined"!=typeof d.webkitGamepads?b=new f(a,function(){return d.webkitGamepads}):"undefined"!=typeof d.webkitGetGamepads&&(b=new f(a,function(){return d.webkitGetGamepads()})));return b};f.getType=function(){return"WebKit"},f.prototype.getType=function(){return f.getType()},f.prototype.isSupported=function(){return!0};f.prototype.update=function(){var a,b,c=this,d=Array.prototype.slice.call(this.gamepadGetter(),0);for(b=this.knownGamepads.length-1;b>=0;b--){a=this.knownGamepads[b];if(d.indexOf(a)<0){this.knownGamepads.splice(b,1);this.listener._disconnect(a)}}for(b=0;bc;c++)this._addButtonUpdater(a,d,c);b=d.axes.byAxis.length;for(c=0;b>c;c++)this._addAxisUpdater(a,d,c);this.gamepads[a.index]=a;this._fire(h.Event.CONNECTED,a)};h.prototype._addButtonUpdater=function(a,c,d){var e=b,f=i(h.StandardButtons,d,"EXTRA_BUTTON_"),g=this._createButtonGetter(a,c.buttons,d),j=this,k={gamepad:a,control:f};a.state[f]=0;a.lastState[f]=0;e=function(){var b=g(),c=a.lastState[f],d=b>.5,e=c>.5;a.state[f]=b;d&&!e?j._fire(h.Event.BUTTON_DOWN,Object.create(k)):!d&&e&&j._fire(h.Event.BUTTON_UP,Object.create(k));0!==b&&1!==b&&b!==c&&j._fireAxisChangedEvent(a,f,b);a.lastState[f]=b};a.updater.push(e)};h.prototype._addAxisUpdater=function(a,c,d){var e=b,f=i(h.StandardAxes,d,"EXTRA_AXIS_"),g=this._createAxisGetter(a,c.axes,d),j=this;a.state[f]=0;a.lastState[f]=0;e=function(){var b=g(),c=a.lastState[f];a.state[f]=b;b!==c&&j._fireAxisChangedEvent(a,f,b);a.lastState[f]=b};a.updater.push(e)};h.prototype._fireAxisChangedEvent=function(a,b,c){var d={gamepad:a,axis:b,value:c};this._fire(h.Event.AXIS_CHANGED,d)};h.prototype._createButtonGetter=function(){var a=function(){return 0},b=function(b,c,d){var e=a;d>c?e=function(){var a=d-c,e=b();e=(e-c)/a;return 0>e?0:e}:c>d&&(e=function(){var a=c-d,e=b();e=(e-d)/a;return e>1?0:1-e});return e},c=function(a){return"[object Array]"===Object.prototype.toString.call(a)};return function(d,e,f){var g,h=a,i=this;g=e.byButton[f];if(-1!==g)"number"==typeof g&&g0&&this._fire(h.Event.TICK,this.gamepads)},h.prototype._applyDeadzoneMaximize=function(a,b,c){b="undefined"!=typeof b?b:this.deadzone;c="undefined"!=typeof c?c:this.maximizeThreshold;a>=0?b>a?a=0:a>c&&(a=1):a>-b?a=0:-c>a&&(a=-1);return a};a.Gamepad=h}("undefined"!=typeof module&&module.exports||window); diff --git a/src/twine_gamepad.js b/src/twine_gamepad.js new file mode 100644 index 0000000..b51a3ee --- /dev/null +++ b/src/twine_gamepad.js @@ -0,0 +1,181 @@ +/* @license +The MIT License (MIT) + +Copyright (c) 2014 mildmojo + +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. +*/ + +(function(exports) { + var Gamepad = window.Gamepad; + + // Removed until it's tested with node-webkit. + // if ((typeof(module) !== 'undefined') && module.exports) { + // Gamepad = require('gamepad'); + // } else { + // Gamepad = window.Gamepad; + // } + + var gamepad = new Gamepad(); + var axes = { LEFT_STICK_X: 0, LEFT_STICK_Y: 0 }; + var DEADZONE = 0.5; + + gamepad.bind(Gamepad.Event.CONNECTED, function(_device) { + console.log('Gamepad connected.'); + }); + + gamepad.bind(Gamepad.Event.DISCONNECTED, function(_device) { + console.log('Gamepad disconnected.'); + }); + + gamepad.bind(Gamepad.Event.BUTTON_DOWN, function(e) { + switch(e.control) { + case 'DPAD_DOWN': + case 'DPAD_RIGHT': + nextLink(); + break; + case 'DPAD_UP': + case 'DPAD_LEFT': + prevLink(); + break; + case 'FACE_1': + case 'FACE_2': + case 'FACE_3': + case 'FACE_4': + var selected = document.querySelector('.gamepadSelected'); + if (selected) { + removeClass(selected, 'gamepadSelected'); + selected.dispatchEvent(new MouseEvent('click')); + } + break; + } + }); + + // Treat axes as buttons; register presses and releases. + gamepad.bind(Gamepad.Event.AXIS_CHANGED, function(data) { + var newValue = 0; + if (Math.abs(data.value) > DEADZONE) { + newValue = data.value < 0 ? -1 : 1; + if (axes[data.axis] !== newValue) { + if (data.axis === 'LEFT_STICK_X') { + if (newValue === -1) { + gamepad._fire(Gamepad.Event.BUTTON_DOWN, {control: 'DPAD_LEFT'}); + } else { + gamepad._fire(Gamepad.Event.BUTTON_DOWN, {control: 'DPAD_RIGHT'}); + } + } else if (data.axis === 'LEFT_STICK_Y') { + if (newValue === 1) { + gamepad._fire(Gamepad.Event.BUTTON_DOWN, {control: 'DPAD_DOWN'}); + } else { + gamepad._fire(Gamepad.Event.BUTTON_DOWN, {control: 'DPAD_UP'}); + } + } + } + } + axes[data.axis] = newValue; + }); + + gamepad.init(); + + function nextLink() { + var links = getTwineLinks(); + if (links === null) return; + var selectedIndex = findSelectedIndex(links); + var newIndex = 0; + if (selectedIndex !== null) { + removeClass(links[selectedIndex], 'gamepadSelected'); + newIndex = (selectedIndex + 1) % links.length + } + addClass(links[newIndex], 'gamepadSelected'); + showLink(); + } + + function prevLink() { + var links = getTwineLinks(); + if (links === null) return; + var selectedIndex = findSelectedIndex(links); + var newIndex = 0; + if (selectedIndex !== null) { + removeClass(links[selectedIndex], 'gamepadSelected'); + newIndex = selectedIndex > 0 ? selectedIndex - 1 : links.length - 1; + } + addClass(links[newIndex], 'gamepadSelected'); + showLink(); + } + + function showLink() { + var selected = document.querySelector('.gamepadSelected'); + if (selected && !isVisible(selected)) { + var position = isAboveViewport(selected) + selected.scrollIntoView(position); + } + } + + function getTwineLinks() { + return document.querySelectorAll('a.internalLink, a.externalLink'); + } + + function findSelectedIndex(links) { + for (var i = 0; i < links.length; i++) { + if (hasClass(links[i], 'gamepadSelected')) { + return i; + } + } + return null; + } + + function isVisible(el) { + var offset = el.getBoundingClientRect(); + if (offset.left < 0 || offset.top < 0){ + return false; + } + if (offset.left > window.innerWidth || offset.top > window.innerHeight){ + return false; + } + return true; + } + + function isAboveViewport(el) { + return el.getBoundingClientRect().top < 0; + } + + // From youmightnotneedjquery.com + function hasClass(el, className) { + if (el.classList) + return el.classList.contains(className); + else + return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className); + } + + // From youmightnotneedjquery.com + function removeClass(el, className) { + if (el.classList) + el.classList.remove(className); + else + el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); + } + + // From youmightnotneedjquery.com + function addClass(el, className) { + if (el.classList) + el.classList.add(className); + else + el.className += ' ' + className; + } + +})(((typeof(module) !== 'undefined') && module.exports) || window);